View as Markdown
Native Select
A styled native HTML select element with consistent design system integration.
100%
Loading...
<twig:NativeSelect>
<twig:NativeSelect:Option value="">Select status</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="todo">Todo</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="in-progress">In Progress</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="done">Done</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="cancelled">Cancelled</twig:NativeSelect:Option>
</twig:NativeSelect>
Installation
Note
Available since UX Toolkit 3.5.
php bin/console ux:install native-select --kit shadcn
Install the following Composer dependencies:
composer require symfony/ux-icons tales-from-a-dev/twig-tailwind-extra:^1.0.0
Copy the following file(s) into your app:
templates/components/NativeSelect.html.twig
{%- props
## 'default'|'sm' The select size.
size = 'default'
-%}
<div
data-slot="native-select-wrapper"
data-size="{{ size }}"
class="{{ ('group/native-select relative w-fit has-[select:disabled]:opacity-50 ' ~ attributes.render('class'))|tailwind_merge }}"
>
<select
data-slot="native-select"
data-size="{{ size }}"
class="h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent px-3 py-2 pe-9 text-sm shadow-xs transition-[color,box-shadow] outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/40 data-[size=sm]:h-8 data-[size=sm]:py-1 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 dark:[color-scheme:dark] [&_option]:bg-background [&_option]:text-foreground [&_optgroup]:bg-background [&_optgroup]:text-foreground"
{{ attributes.without('class') }}
>
{##- The select options, typically `NativeSelect:Option` and `NativeSelect:OptGroup` components. -#}
{%- block content %}{% endblock -%}
</select>
<twig:ux:icon name="lucide:chevron-down" class="pointer-events-none absolute top-1/2 end-3.5 size-4 -translate-y-1/2 text-muted-foreground opacity-50 select-none" aria-hidden="true" data-slot="native-select-icon" />
</div>
templates/components/NativeSelect/OptGroup.html.twig
<optgroup data-slot="native-select-optgroup" {{ attributes }}>
{##- The `NativeSelect:Option` components in this group. -#}
{%- block content %}{% endblock -%}
</optgroup>
templates/components/NativeSelect/Option.html.twig
<option data-slot="native-select-option" {{ attributes }}>
{##- The option label. -#}
{%- block content %}{% endblock -%}
</option>
Usage
<twig:NativeSelect>
<twig:NativeSelect:Option value="">Select a fruit</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="apple">Apple</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="banana">Banana</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="blueberry">Blueberry</twig:NativeSelect:Option>
</twig:NativeSelect>
Examples
Groups
Use NativeSelect:OptGroup to organize options into labeled groups.
100%
Loading...
<twig:NativeSelect>
<twig:NativeSelect:Option value="">Select department</twig:NativeSelect:Option>
<twig:NativeSelect:OptGroup label="Engineering">
<twig:NativeSelect:Option value="frontend">Frontend</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="backend">Backend</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="devops">DevOps</twig:NativeSelect:Option>
</twig:NativeSelect:OptGroup>
<twig:NativeSelect:OptGroup label="Sales">
<twig:NativeSelect:Option value="sales-rep">Sales Rep</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="account-manager">Account Manager</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="sales-director">Sales Director</twig:NativeSelect:Option>
</twig:NativeSelect:OptGroup>
<twig:NativeSelect:OptGroup label="Operations">
<twig:NativeSelect:Option value="support">Customer Support</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="product-manager">Product Manager</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="ops-manager">Operations Manager</twig:NativeSelect:Option>
</twig:NativeSelect:OptGroup>
</twig:NativeSelect>
Disabled
100%
Loading...
<twig:NativeSelect disabled>
<twig:NativeSelect:Option value="">Disabled</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="apple">Apple</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="banana">Banana</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="blueberry">Blueberry</twig:NativeSelect:Option>
</twig:NativeSelect>
Invalid
Set aria-invalid="true" to display the error state.
100%
Loading...
<twig:NativeSelect aria-invalid="true">
<twig:NativeSelect:Option value="">Error state</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="apple">Apple</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="banana">Banana</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="blueberry">Blueberry</twig:NativeSelect:Option>
</twig:NativeSelect>
RTL
To enable RTL support, set the dir="rtl" attribute on the root element.
100%
Loading...
<div class="flex flex-col items-center gap-4">
{# Arabic #}
<div dir="rtl">
<twig:NativeSelect>
<twig:NativeSelect:Option value="">اختر الحالة</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="todo">مهام</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="in-progress">قيد التنفيذ</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="done">منجز</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="cancelled">ملغي</twig:NativeSelect:Option>
</twig:NativeSelect>
</div>
{# Hebrew #}
<div dir="rtl">
<twig:NativeSelect>
<twig:NativeSelect:Option value="">בחר סטטוס</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="todo">לעשות</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="in-progress">בתהליך</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="done">הושלם</twig:NativeSelect:Option>
<twig:NativeSelect:Option value="cancelled">בוטל</twig:NativeSelect:Option>
</twig:NativeSelect>
</div>
</div>
Accessibility
NativeSelectrenders a real<select>, so it keeps the platform's own picker, keyboard handling and type-ahead. This is the most accessible way to present a list of options.- Give it a visible
Labelbound withforandid. The first option is not a label, and a placeholder option should bedisabledso it cannot be chosen. - The chevron is drawn with
<twig:ux:icon>, which rendersaria-hidden="true", and it is not focusable, so it never sits between the user and the control. NativeSelect:OptGrouprenders a real<optgroup>, whoselabelis announced when moving into the group.- Set
aria-invalid="true"for the invalid styling, and pointaria-describedbyat the message explaining what is wrong. Prefer the nativerequiredattribute over a client-side check alone.
API Reference
<twig:NativeSelect>
| Prop | Type | Default |
|---|---|---|
size The select size.
|
'default'|'sm' |
'default' |
| Block | Description |
|---|---|
content |
The select options, typically NativeSelect:Option and NativeSelect:OptGroup components. |
<twig:NativeSelect:OptGroup>
| Block | Description |
|---|---|
content |
The NativeSelect:Option components in this group. |
<twig:NativeSelect:Option>
| Block | Description |
|---|---|
content |
The option label. |