Jane Doe Arturo Reyes Priya Shah Kenji Nakamura Lena Voss Omar El-Hassan Yuki Tanaka Jane Doe Arturo Reyes Priya Shah Kenji Nakamura Lena Voss Omar El-Hassan Yuki Tanaka Select# Component <neo-select> is a single-select dropdown: trigger button, popover panel, and a roving-tabindex listbox of <neo-option> / <neo-optgroup> children. The host wires keyboard navigation, type-to-search, and a lazy-by-default / async lifecycle so options can be loaded on first open and cached on subsequent opens. For search and multiple selection, see Combobox .
The preview routes through the in-browser server simulator. Its current Settings (network latency, a non-200 response, or an unreachable server) are in effect, so the live preview can look like it is misbehaving. Adjust them in the Settings panel.
Today
Last 7 days
Last 30 days
Last 3 months
Last year
All time
HTML CSS
The selected state's markup and its scoped CSS. The CSS is wrapped in <style>@scope { ... }</style> and applies to the preview only. Edits change the preview above, applied live while Sync is on, or when you click Patch or Replace. Patch morphs the preview; Replace swaps it outright.
Signals
Datastar signals declared in the selected state's markup. Change a value to drive the preview, applied live while Sync is on, or when you click Patch.
<neo-select
id="example-select"
data-signals="{select_value: '30d'}"
data-attr:value="$select_value"
aria-label="Time range"
placeholder="Pick a range"
typeahead-match="substring"
>
<neo-option value="today">Today</neo-option>
<neo-option value="7d">Last 7 days</neo-option>
<neo-option value="30d">Last 30 days</neo-option>
<neo-option value="quarter">Last 3 months</neo-option>
<neo-option value="year">Last year</neo-option>
<neo-option value="all">All time</neo-option>
</neo-select>
<neo-select
value="team"
disabled
placeholder="Billing plan"
aria-label="Billing plan"
follow-scroll="until-trigger-invisible"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="free"
data-neo-value="free"
>Free</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="team"
data-neo-value="team"
>Team</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="enterprise"
data-neo-value="enterprise"
>Enterprise</neo-option>
</neo-select> <neo-select
value="standard"
placeholder="Shipping method"
aria-label="Shipping method"
follow-scroll="until-trigger-invisible"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="standard"
data-neo-value="standard"
>Standard (3–5 days)</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="express"
data-neo-value="express"
>Express (1–2 days)</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="overnight"
data-neo-value="overnight"
disabled
aria-disabled="true"
>Overnight (sold out)</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="pickup"
data-neo-value="pickup"
>In-store pickup</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="locker"
data-neo-value="locker"
disabled
aria-disabled="true"
>Locker drop (region unsupported)</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="freight"
data-neo-value="freight"
disabled
aria-disabled="true"
>Freight (contact sales)</neo-option>
</neo-select> <neo-select
value="Europe/Berlin"
placeholder="Pick a time zone"
typeahead-match="substring"
aria-label="Time zone"
follow-scroll="until-trigger-invisible"
>
<neo-optgroup role="group" label="Americas" aria-label="Americas">
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="America/Los_Angeles"
data-neo-value="America/Los_Angeles"
label="Los Angeles (PT)"
>Los Angeles<small>UTC−08:00</small></neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="America/New_York"
data-neo-value="America/New_York"
label="New York (ET)"
>New York<small>UTC−05:00</small></neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="America/Sao_Paulo"
data-neo-value="America/Sao_Paulo"
label="São Paulo"
>São Paulo<small>UTC−03:00</small></neo-option>
</neo-optgroup>
<neo-optgroup
role="group"
label="Europe & Africa"
aria-label="Europe & Africa"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="Europe/London"
data-neo-value="Europe/London"
label="London"
>London<small>UTC+00:00</small></neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="Europe/Berlin"
data-neo-value="Europe/Berlin"
label="Berlin"
>Berlin<small>UTC+01:00</small></neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="Africa/Cairo"
data-neo-value="Africa/Cairo"
label="Cairo"
>Cairo<small>UTC+02:00</small></neo-option>
</neo-optgroup>
<neo-optgroup
role="group"
label="Asia & Pacific"
aria-label="Asia & Pacific"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="Asia/Dubai"
data-neo-value="Asia/Dubai"
label="Dubai"
>Dubai<small>UTC+04:00</small></neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="Asia/Tokyo"
data-neo-value="Asia/Tokyo"
label="Tokyo"
>Tokyo<small>UTC+09:00</small></neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="Australia/Sydney"
data-neo-value="Australia/Sydney"
label="Sydney"
>Sydney<small>UTC+11:00</small></neo-option>
</neo-optgroup>
</neo-select> <neo-select
placeholder="Choose a channel"
clearable
typeahead-match="prefix"
aria-label="Notification channel"
follow-scroll="until-trigger-invisible"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value=""
data-neo-select-empty
>
<span class="select-clearable-icon" aria-hidden="true">
<neo-icon name="x"></neo-icon>
</span>
<span>No channel</span>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="sms"
data-neo-value="sms"
>
<span class="select-clearable-icon" aria-hidden="true">
<neo-icon name="message-square"></neo-icon>
</span>
<span>SMS</span>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="email"
data-neo-value="email"
>
<span class="select-clearable-icon" aria-hidden="true">
<neo-icon name="mail"></neo-icon>
</span>
<span>Email</span>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="push"
data-neo-value="push"
>
<span class="select-clearable-icon" aria-hidden="true">
<neo-icon name="bell"></neo-icon>
</span>
<span>Push notification</span>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="slack"
data-neo-value="slack"
>
<span class="select-clearable-icon" aria-hidden="true">
<neo-icon name="hash"></neo-icon>
</span>
<span>Slack DM</span>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="pigeon"
data-neo-value="pigeon"
disabled
aria-disabled="true"
>
<span class="select-clearable-icon" aria-hidden="true">
<neo-icon name="bird"></neo-icon>
</span>
<span>Carrier pigeon (coming soon)</span>
</neo-option>
</neo-select> .select-clearable-icon {
font-size: 1.1em;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.5rem;
flex: 0 0 auto;
}
<neo-select
value="evelyn_kone"
placeholder="Pick an owner"
clearable
aria-label="Project owner"
follow-scroll="until-trigger-invisible"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value=""
data-neo-select-empty
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/placeholder.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Unassigned</div>
<div class="select-rich-options-subtitle">...</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="diego_vega"
data-neo-value="diego_vega"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_1.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Diego Vega</div>
<div class="select-rich-options-subtitle">CFO</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="jan_osullivan"
data-neo-value="jan_osullivan"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_2.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Jan O'Sullivan</div>
<div class="select-rich-options-subtitle">Lead Designer</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="evelyn_kone"
data-neo-value="evelyn_kone"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_3.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Evelyn Kone</div>
<div class="select-rich-options-subtitle">Data Analyst</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="samira_khalil"
data-neo-value="samira_khalil"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_4.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Samira Khalil</div>
<div class="select-rich-options-subtitle">Head of Engineering</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="alice_larsson"
data-neo-value="alice_larsson"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_5.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Alice Larsson</div>
<div class="select-rich-options-subtitle">Product Manager</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="camila_duarte"
data-neo-value="camila_duarte"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_6.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Camila Duarte</div>
<div class="select-rich-options-subtitle">DevOps Engineer</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="theo_becker"
data-neo-value="theo_becker"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_7.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Theo Becker</div>
<div class="select-rich-options-subtitle">UX Researcher</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="mateusz_wisniewski"
data-neo-value="mateusz_wisniewski"
>
<div class="select-rich-options-row">
<neo-avatar class="select-rich-options-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_8.svg" alt="">
</neo-avatar>
<div class="select-rich-options-col">
<div class="select-rich-options-name">Mateusz Wiśniewski</div>
<div class="select-rich-options-subtitle">Fullstack Engineer</div>
</div>
</div>
</neo-option>
</neo-select> .select-rich-options-avatar {
flex: 0 0 auto;
--neo-avatar-size: 2.25rem;
width: 2.25rem;
height: 2.25rem;
}
.select-rich-options-row {
display: flex;
align-items: center;
gap: 0.75rem;
width: 100%;
min-width: 0;
}
.select-rich-options-col {
flex: 1 1 auto;
min-width: 0;
text-align: left;
}
.select-rich-options-name {
font-weight: 600;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-rich-options-subtitle {
margin-top: 0.1rem;
font-size: 0.85em;
color: var(--muted);
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
<neo-select
value="evelyn_kone"
placeholder="Pick an owner"
aria-label="Project owner (compact preview)"
follow-scroll="until-trigger-invisible"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="diego_vega"
data-neo-value="diego_vega"
label="Diego Vega"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_1.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Diego Vega</div>
<div class="select-compact-preview-subtitle">CFO</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="jan_osullivan"
data-neo-value="jan_osullivan"
label="Jan O'Sullivan"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_2.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Jan O'Sullivan</div>
<div class="select-compact-preview-subtitle">Lead Designer</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="evelyn_kone"
data-neo-value="evelyn_kone"
label="Evelyn Kone"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_3.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Evelyn Kone</div>
<div class="select-compact-preview-subtitle">Data Analyst</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="samira_khalil"
data-neo-value="samira_khalil"
label="Samira Khalil"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_4.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Samira Khalil</div>
<div class="select-compact-preview-subtitle">Head of Engineering</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="alice_larsson"
data-neo-value="alice_larsson"
label="Alice Larsson"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_5.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Alice Larsson</div>
<div class="select-compact-preview-subtitle">Product Manager</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="camila_duarte"
data-neo-value="camila_duarte"
label="Camila Duarte"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_6.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Camila Duarte</div>
<div class="select-compact-preview-subtitle">DevOps Engineer</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="theo_becker"
data-neo-value="theo_becker"
label="Theo Becker"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_7.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Theo Becker</div>
<div class="select-compact-preview-subtitle">UX Researcher</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="mateusz_wisniewski"
data-neo-value="mateusz_wisniewski"
label="Mateusz Wiśniewski"
>
<div class="select-compact-preview-row">
<neo-avatar class="select-compact-preview-avatar" aria-hidden="true">
<img src="/static/avatar/avatar_8.svg" alt="">
</neo-avatar>
<div class="select-compact-preview-col">
<div class="select-compact-preview-name">Mateusz Wiśniewski</div>
<div class="select-compact-preview-subtitle">Fullstack Engineer</div>
</div>
</div>
</neo-option>
</neo-select> .select-compact-preview-avatar {
flex: 0 0 auto;
--neo-avatar-size: 2.25rem;
width: 2.25rem;
height: 2.25rem;
}
.select-compact-preview-row {
display: flex;
align-items: center;
gap: 0.75rem;
width: 100%;
min-width: 0;
}
.select-compact-preview-col {
flex: 1 1 auto;
min-width: 0;
text-align: left;
}
.select-compact-preview-name {
font-weight: 600;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-compact-preview-subtitle {
margin-top: 0.1rem;
font-size: 0.85em;
color: var(--muted);
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
<neo-select
value="evelyn_kone"
placeholder="Pick an owner"
aria-label="Project owner (custom face)"
follow-scroll="until-trigger-invisible"
>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="diego_vega"
data-neo-value="diego_vega"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_1.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Diego Vega</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_1.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Diego Vega</div>
<div class="select-custom-trigger-face-subtitle">CFO</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="jan_osullivan"
data-neo-value="jan_osullivan"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_2.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Jan O'Sullivan</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_2.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Jan O'Sullivan</div>
<div class="select-custom-trigger-face-subtitle">Lead Designer</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="evelyn_kone"
data-neo-value="evelyn_kone"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_3.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Evelyn Kone</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_3.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Evelyn Kone</div>
<div class="select-custom-trigger-face-subtitle">Data Analyst</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="samira_khalil"
data-neo-value="samira_khalil"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_4.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Samira Khalil</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_4.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Samira Khalil</div>
<div class="select-custom-trigger-face-subtitle">Head of Engineering</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="alice_larsson"
data-neo-value="alice_larsson"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_5.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Alice Larsson</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_5.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Alice Larsson</div>
<div class="select-custom-trigger-face-subtitle">Product Manager</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="camila_duarte"
data-neo-value="camila_duarte"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_6.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Camila Duarte</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_6.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Camila Duarte</div>
<div class="select-custom-trigger-face-subtitle">DevOps Engineer</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="theo_becker"
data-neo-value="theo_becker"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_7.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Theo Becker</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_7.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Theo Becker</div>
<div class="select-custom-trigger-face-subtitle">UX Researcher</div>
</div>
</div>
</neo-option>
<neo-option
role="option"
tabindex="-1"
aria-selected="false"
value="mateusz_wisniewski"
data-neo-value="mateusz_wisniewski"
>
<span data-neo-option-trigger>
<neo-avatar
class="select-custom-trigger-face-avatar-sm"
aria-hidden="true"
>
<img src="/static/avatar/avatar_8.svg" alt="">
</neo-avatar>
<span class="select-custom-trigger-face-name">Mateusz Wiśniewski</span>
</span>
<div class="select-custom-trigger-face-row">
<neo-avatar
class="select-custom-trigger-face-avatar-lg"
aria-hidden="true"
>
<img src="/static/avatar/avatar_8.svg" alt="">
</neo-avatar>
<div class="select-custom-trigger-face-col">
<div class="select-custom-trigger-face-name">Mateusz Wiśniewski</div>
<div class="select-custom-trigger-face-subtitle">Fullstack Engineer</div>
</div>
</div>
</neo-option>
</neo-select> .select-custom-trigger-face-avatar-sm {
flex: 0 0 auto;
--neo-avatar-size: 1.5rem;
width: 1.5rem;
height: 1.5rem;
}
.select-custom-trigger-face-avatar-lg {
flex: 0 0 auto;
--neo-avatar-size: 2.25rem;
width: 2.25rem;
height: 2.25rem;
}
.select-custom-trigger-face-name {
font-weight: 600;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-custom-trigger-face-row {
display: flex;
align-items: center;
gap: 0.75rem;
width: 100%;
min-width: 0;
}
.select-custom-trigger-face-col {
flex: 1 1 auto;
min-width: 0;
text-align: left;
}
.select-custom-trigger-face-subtitle {
margin-top: 0.1rem;
font-size: 0.85em;
color: var(--muted);
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
Reference# Attributes # Name Type Default Description valuestring reflected observednone Selected option value. Empty clears the selection. namestringnone Form field name. The control is form-associated: with a name set it submits its value in a native <form> like a <select>, resets with the form, and is barred from submission while disabled. size"sm" | "lg"none Control-size step: sm compact, lg large. Omit for the default size. disabledboolean observedfalseMakes the control non-interactive: the trigger drops out of the tab order and ignores clicks and keys, and an open panel closes. placeholderstring observednone Trigger text when nothing is selected. liststring observednone References a shared <neo-datalist> by its id (like native <input list>) to source the options. Inline <neo-option> children (or an inline datalist) take precedence. asyncbooleanfalseOn every open, clear the listbox, re-show the [data-neo-async-placeholder] slot, and fire neo-select-load so a wired loader re-fetches. Without async, a select with a [data-neo-async-placeholder] child fires neo-select-load only on the first open (options empty) and caches across opens; neo-popover-open is independent, and always fires when the panel opens. clearablebooleanfalseBackspace / Delete clears the selection.caretstring observed"chevrons-up-down"Name of the trigger's caret icon. Resolved through <neo-icon> , so it follows the active icon theme. Set caret="" (empty) to hide the caret entirely. typeaheadtrue | falsetrueType-to-search over the options. With typeahead on, Space feeds the buffer; set typeahead="false" and Space activates the focused option instead. typeahead-timeoutnumber500Typeahead idle reset duration in milliseconds. typeahead-match"substring" | "prefix""substring"How the typeahead buffer matches option labels. placementstring observed"bottom-start"Anchor placement of the panel. The listbox positions the panel with the same helper as <neo-popover> ; see its placement values. screen-offsetstring observednone Viewport edge margin and the panel's scrollable max size. follow-scroll"always" | "until-trigger-invisible" observedunsetWhen absent, close on outside scroll. Set to always to stay anchored during scroll. Set to until-trigger-invisible to close once the trigger leaves the viewport. clamp-placementboolean observedfalseDefault is strict placement (panel anchored at the exact point, content scrolls); set this to opt into re-anchoring the panel position so it stays inside the viewport when content would overflow. min-fit-heightCSS length | "content" observed"content"Minimum vertical slot the strict placement requires; below the threshold the panel auto-flips to the opposite primary axis when the other side has more room. content resolves from the panel's scroll height on each positioning pass. min-fit-widthCSS length | "content" observed"content"Same as min-fit-height but for the horizontal axis. content resolves from the panel's scroll width. Use 0 to disable the horizontal auto-flip. min-open-heightCSS length observed2remMinimum usable vertical slot required to remain open after placement and auto-flip. If the bounded slot is smaller, the listbox closes instead of rendering a cramped panel. Use 0 to disable. min-open-widthCSS length observed2remSame as min-open-height but for horizontal space. Use 0 to disable. popover-fit-contentbooleanfalseThe listbox tracks the trigger width by default; set this to size the panel to the option rows instead (e.g. a glyph-only trigger in a <neo-input-group> ). Capped by --neo-popover-max-width and the viewport. hoverbooleanfalseOpen the panel on trigger hover without moving focus; close after the pointer leaves both trigger and panel. Mouse only; touch falls back to click. While set, clicking the trigger always shows the panel instantly and never toggles it shut, so touch and keyboard users keep a way in. hover-open-delayinteger (ms)100Milliseconds the pointer must rest on the trigger before hover opens the panel. Filters incidental cursor crossings. Does not apply to click. hover-close-delayinteger (ms)200Milliseconds to wait after the pointer leaves both trigger and panel before hover closes the panel. Long enough to bridge the gap between them.
Slots # Name Description default <neo-option> and <neo-optgroup> children. Alternatively, set list to source them from a shared <neo-datalist> .[data-neo-async-placeholder]Custom skeleton shown while options load. Presence of this slot also marks the select as lazy-by-default: the kit shows it on opens with no options, then caches options across opens once they arrive. [slot=trigger]Owns the trigger face. Leave it absent and the kit fills it client-side by cloning the selected option's body (so its CSS styles it and the trigger grows to fit); provide one (e.g. patched from a server) and the kit yields, rendering exactly your content. [data-neo-option-trigger]Optional per-<neo-option> child: a compact trigger face distinct from the option's rich list row. Hidden in the list; cloned into the trigger when that option is selected. Falls back to the option's label (plain text), then its full body. Shared with <neo-combobox> . [data-neo-select-empty]The empty-state trigger face: its body is cloned into the trigger when nothing is selected.
Events # Name Detail Bubbles Description neo-select-change{ value: string | null, label: string | null }yes Fires after the selection changes. neo-select-loadnoneyes Fires when options need fetching: every open with async, or a lazy select's first open. Wire a loader to it (e.g. a Datastar @post). Call el.reload() to re-fire it. Distinct from neo-popover-open, which always fires on open.
Parts # Name Description ::part(label)Trigger label showing the current selection. ::part(caret)Trigger caret icon.
CSS variables # Name Default Description --neo-select-min-width12remMinimum trigger width.
Examples# Disabled options# Mark an option unavailable with disabled: it renders muted, is skipped by arrow-key navigation, and can't be selected.
Standard (3–5 days) Express (1–2 days) Overnight (sold out) In-store pickup Locker drop (region unsupported) Freight (contact sales)
Disabled control# Set disabled on the host to make the whole control non-interactive: the trigger leaves the tab order, ignores clicks and keys, and renders faded. An open panel closes. Distinct from disabling individual options.
The select is form-associated. Give it a name and it submits its value in a plain <form>, no framework binding. Pick a plan and submit to see the posted value.
Grouped options (time zones)# Group related options under labeled headers by wrapping them in <neo-optgroup label="…">.
The group header is not focusable; arrow keys step across groups. Each option's label gives the trigger clean text even when the option body is rich markup. Los AngelesUTC−08:00 New YorkUTC−05:00 São PauloUTC−03:00 LondonUTC+00:00 BerlinUTC+01:00 CairoUTC+02:00 DubaiUTC+04:00 TokyoUTC+09:00 SydneyUTC+11:00
Clearable with empty slot# Let the user clear the selection back to an empty state.
clearable makes Backspace or Delete on the trigger drop the selection.Tag an empty <neo-option value=""> with [data-neo-select-empty] to serve as both a selectable “none” row and the trigger face shown once cleared.
No channel
SMS
Email
Push notification
Slack DM
Carrier pigeon (coming soon)
Rich options# Options carry multi-line bodies (avatar, name, title). With no label override, the trigger clones the selected option's body, so it matches the listbox row.
Jan O'Sullivan
Lead Designer
Samira Khalil
Head of Engineering
Alice Larsson
Product Manager
Camila Duarte
DevOps Engineer
Mateusz Wiśniewski
Fullstack Engineer
Compact preview (label override)# Show a compact label on the trigger while the list keeps the rich card: set each option's label and the trigger uses it instead of cloning the body.
Jan O'Sullivan
Lead Designer
Samira Khalil
Head of Engineering
Alice Larsson
Product Manager
Camila Duarte
DevOps Engineer
Mateusz Wiśniewski
Fullstack Engineer
Custom trigger face# Show a compact custom face on the trigger, distinct from the option's full list row.
A [data-neo-option-trigger] child is cloned onto the trigger when its option is selected; the list keeps the full card. It sits between a plain-text label and cloning the whole body, and works the same on <neo-combobox>.
Diego Vega Jan O'Sullivan Jan O'Sullivan
Lead Designer
Evelyn Kone Samira Khalil Samira Khalil
Head of Engineering
Alice Larsson Alice Larsson
Product Manager
Camila Duarte Camila Duarte
DevOps Engineer
Theo Becker Mateusz Wiśniewski Mateusz Wiśniewski
Fullstack Engineer
Long list# A long option list fits between the trigger and the viewport edge and scrolls inside the popover.
Strict placement (the default) keeps the panel anchored and scrolls its content. Set clamp-placement to re-anchor the panel's position so it fits instead. 🇦🇫 Afghanistan 🇦🇱 Albania 🇩🇿 Algeria 🇦🇩 Andorra 🇦🇴 Angola 🇦🇬 Antigua and Barbuda 🇦🇷 Argentina 🇦🇲 Armenia 🇦🇺 Australia 🇦🇹 Austria 🇦🇿 Azerbaijan 🇧🇸 Bahamas 🇧🇭 Bahrain 🇧🇩 Bangladesh 🇧🇧 Barbados 🇧🇾 Belarus 🇧🇪 Belgium 🇧🇿 Belize 🇧🇯 Benin 🇧🇹 Bhutan 🇧🇴 Bolivia 🇧🇦 Bosnia and Herzegovina 🇧🇼 Botswana 🇧🇷 Brazil 🇧🇳 Brunei 🇧🇬 Bulgaria 🇧🇫 Burkina Faso 🇧🇮 Burundi 🇰🇭 Cambodia 🇨🇲 Cameroon 🇨🇦 Canada 🇨🇻 Cape Verde 🇨🇫 Central African Republic 🇹🇩 Chad 🇨🇱 Chile 🇨🇳 China 🇨🇴 Colombia 🇰🇲 Comoros 🇨🇬 Congo 🇨🇷 Costa Rica 🇭🇷 Croatia 🇨🇺 Cuba 🇨🇾 Cyprus 🇨🇿 Czechia 🇩🇰 Denmark 🇩🇯 Djibouti 🇩🇲 Dominica 🇩🇴 Dominican Republic 🇪🇨 Ecuador 🇪🇬 Egypt 🇸🇻 El Salvador 🇬🇶 Equatorial Guinea 🇪🇷 Eritrea 🇪🇪 Estonia 🇸🇿 Eswatini 🇪🇹 Ethiopia 🇫🇯 Fiji 🇫🇮 Finland 🇫🇷 France 🇬🇦 Gabon 🇬🇲 Gambia 🇬🇪 Georgia 🇩🇪 Germany 🇬🇭 Ghana 🇬🇷 Greece 🇬🇩 Grenada 🇬🇹 Guatemala 🇬🇳 Guinea 🇬🇼 Guinea-Bissau 🇬🇾 Guyana 🇭🇹 Haiti 🇭🇳 Honduras 🇭🇺 Hungary 🇮🇸 Iceland 🇮🇳 India 🇮🇩 Indonesia 🇮🇷 Iran 🇮🇶 Iraq 🇮🇪 Ireland 🇮🇱 Israel 🇮🇹 Italy 🇯🇲 Jamaica 🇯🇵 Japan 🇯🇴 Jordan 🇰🇿 Kazakhstan 🇰🇪 Kenya 🇰🇮 Kiribati 🇰🇼 Kuwait 🇰🇬 Kyrgyzstan 🇱🇦 Laos 🇱🇻 Latvia 🇱🇧 Lebanon 🇱🇸 Lesotho 🇱🇷 Liberia 🇱🇾 Libya 🇱🇮 Liechtenstein 🇱🇹 Lithuania 🇱🇺 Luxembourg 🇲🇬 Madagascar 🇲🇼 Malawi 🇲🇾 Malaysia 🇲🇻 Maldives
Auto-flip threshold# Strict placement flips the panel to the opposite side when its preferred side is too short.
If the slot is shorter than min-fit-height (default content) and the opposite side has more room, the listbox flips there. Here min-fit-height is 8rem: when the trigger sits low in the viewport the listbox flips from bottom-start to top-start. Scroll it down to see it. 🇦🇫 Afghanistan 🇦🇱 Albania 🇩🇿 Algeria 🇦🇩 Andorra 🇦🇴 Angola 🇦🇬 Antigua and Barbuda 🇦🇷 Argentina 🇦🇲 Armenia 🇦🇺 Australia 🇦🇹 Austria 🇦🇿 Azerbaijan 🇧🇸 Bahamas 🇧🇭 Bahrain 🇧🇩 Bangladesh 🇧🇧 Barbados 🇧🇾 Belarus 🇧🇪 Belgium 🇧🇿 Belize 🇧🇯 Benin 🇧🇹 Bhutan 🇧🇴 Bolivia 🇧🇦 Bosnia and Herzegovina 🇧🇼 Botswana 🇧🇷 Brazil 🇧🇳 Brunei 🇧🇬 Bulgaria 🇧🇫 Burkina Faso 🇧🇮 Burundi 🇰🇭 Cambodia 🇨🇲 Cameroon 🇨🇦 Canada 🇨🇻 Cape Verde 🇨🇫 Central African Republic 🇹🇩 Chad 🇨🇱 Chile 🇨🇳 China 🇨🇴 Colombia 🇰🇲 Comoros 🇨🇬 Congo 🇨🇷 Costa Rica 🇭🇷 Croatia 🇨🇺 Cuba 🇨🇾 Cyprus 🇨🇿 Czechia 🇩🇰 Denmark 🇩🇯 Djibouti 🇩🇲 Dominica 🇩🇴 Dominican Republic 🇪🇨 Ecuador 🇪🇬 Egypt 🇸🇻 El Salvador 🇬🇶 Equatorial Guinea 🇪🇷 Eritrea 🇪🇪 Estonia 🇸🇿 Eswatini 🇪🇹 Ethiopia 🇫🇯 Fiji 🇫🇮 Finland 🇫🇷 France 🇬🇦 Gabon 🇬🇲 Gambia 🇬🇪 Georgia 🇩🇪 Germany 🇬🇭 Ghana 🇬🇷 Greece 🇬🇩 Grenada 🇬🇹 Guatemala 🇬🇳 Guinea 🇬🇼 Guinea-Bissau 🇬🇾 Guyana 🇭🇹 Haiti 🇭🇳 Honduras 🇭🇺 Hungary 🇮🇸 Iceland 🇮🇳 India 🇮🇩 Indonesia 🇮🇷 Iran 🇮🇶 Iraq 🇮🇪 Ireland 🇮🇱 Israel 🇮🇹 Italy 🇯🇲 Jamaica 🇯🇵 Japan 🇯🇴 Jordan 🇰🇿 Kazakhstan 🇰🇪 Kenya 🇰🇮 Kiribati 🇰🇼 Kuwait 🇰🇬 Kyrgyzstan 🇱🇦 Laos 🇱🇻 Latvia 🇱🇧 Lebanon 🇱🇸 Lesotho 🇱🇷 Liberia 🇱🇾 Libya 🇱🇮 Liechtenstein 🇱🇹 Lithuania 🇱🇺 Luxembourg 🇲🇬 Madagascar 🇲🇼 Malawi 🇲🇾 Malaysia 🇲🇻 Maldives
Examples: Datastar# Morphing during interaction# A Datastar fat-morph can replace the option list while the user is interacting; the select reconciles in place.
Options can appear, disappear, or flip to disabled with the popover open. The element stays mounted, the popover stays open, and the selection survives. Autoplay cycles the states below to simulate the morphs.
The preview routes through the in-browser server simulator. Its current Settings (network latency, a non-200 response, or an unreachable server) are in effect, so the live preview can look like it is misbehaving. Adjust them in the Settings panel.
Amsterdam
Berlin
Copenhagen
Dublin
Edinburgh
HTML CSS
The selected state's markup and its scoped CSS. The CSS is wrapped in <style>@scope { ... }</style> and applies to the preview only. Edits change the preview above, applied live while Sync is on, or when you click Patch or Replace. Patch morphs the preview; Replace swaps it outright.
Signals
Datastar signals declared in the selected state's markup. Change a value to drive the preview, applied live while Sync is on, or when you click Patch.
<neo-select
value="berlin"
aria-label="City"
placeholder="Pick a city"
>
<neo-option value="amsterdam">Amsterdam</neo-option>
<neo-option value="berlin">Berlin</neo-option>
<neo-option value="copenhagen">Copenhagen</neo-option>
<neo-option value="dublin">Dublin</neo-option>
<neo-option value="edinburgh">Edinburgh</neo-option>
</neo-select>
<neo-select
value="berlin"
aria-label="City"
placeholder="Pick a city"
>
<neo-option value="amsterdam">Amsterdam</neo-option>
<neo-option value="berlin">Berlin</neo-option>
<neo-option value="copenhagen">Copenhagen</neo-option>
<neo-option value="dublin">Dublin</neo-option>
<neo-option value="edinburgh">Edinburgh</neo-option>
<neo-option value="florence">Florence</neo-option>
<neo-option value="geneva">Geneva</neo-option>
<neo-option value="helsinki">Helsinki</neo-option>
<neo-option value="istanbul">Istanbul</neo-option>
<neo-option value="lisbon">Lisbon</neo-option>
</neo-select>
<neo-select
value="berlin"
aria-label="City"
placeholder="Pick a city"
>
<neo-option value="amsterdam">Amsterdam</neo-option>
<neo-option value="berlin">Berlin</neo-option>
<neo-option value="copenhagen" disabled>Copenhagen</neo-option>
<neo-option value="dublin" disabled>Dublin</neo-option>
<neo-option value="edinburgh">Edinburgh</neo-option>
</neo-select>
Async load# Fetch options from the server on every open with async.
Each open fires neo-select-load; wire data-on:neo-select-load to post. The server morphs an options fragment into #<host-id>-options; the [data-neo-async-placeholder] shows until it arrives. With clearable, Backspace clears the selection and the next open reloads. Flip server unreachable or a 4xx/5xx server response status to see the placeholder stay put when the request never lands. server unreachable server response status 200 OK 201 Created 202 Accepted 204 No Content 206 Partial Content301 Moved Permanently 302 Found 304 Not Modified 307 Temporary Redirect 308 Permanent Redirect400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 405 Method Not Allowed 408 Request Timeout 409 Conflict 410 Gone 418 I'm a teapot 422 Unprocessable Entity 425 Too Early 429 Too Many Requests500 Internal Server Error 501 Not Implemented 502 Bad Gateway 503 Service Unavailable 504 Gateway Timeout 507 Insufficient Storage 511 Network Authentication Required Loading states only show with simulated delay. Raise network latency or handler delay in the Settings panel.
Lazy load# Fetch options once, on first open, then reuse them.
Add a [data-neo-async-placeholder] without async: the first open fires neo-select-load. Loaded options are cached; later opens reuse them with no refetch. The option state is the only source of truth, with no guard signal needed. A server reset that re-emits the placeholder puts the select back into the "needs load" state. Loading states only show with simulated delay. Raise network latency or handler delay in the Settings panel.
Async load with failure swap# Async load that shows a failure state and a retry button when the request keeps failing.
On a failed request Datastar retries per its retry config; after retries are exhausted, a failure template replaces the [data-neo-async-placeholder]. A [data-neo-popover-async-retry] button re-runs the load without closing the panel. The placeholder stays the source, so reopening shows the loading view again. server unreachable server response status 200 OK 201 Created 202 Accepted 204 No Content 206 Partial Content301 Moved Permanently 302 Found 304 Not Modified 307 Temporary Redirect 308 Permanent Redirect400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 405 Method Not Allowed 408 Request Timeout 409 Conflict 410 Gone 418 I'm a teapot 422 Unprocessable Entity 425 Too Early 429 Too Many Requests500 Internal Server Error 501 Not Implemented 502 Bad Gateway 503 Service Unavailable 504 Gateway Timeout 507 Insufficient Storage 511 Network Authentication Required Loading states only show with simulated delay. Raise network latency or handler delay in the Settings panel.
Couldn't load options. Retry