Styled native select control
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
...import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
export default function NativeSelectDemoExample() {
return (
<NativeSelect>
<NativeSelectOption value="">Select status</NativeSelectOption>
<NativeSelectOption value="todo">Todo</NativeSelectOption>
<NativeSelectOption value="in-progress">In Progress</NativeSelectOption>
<NativeSelectOption value="done">Done</NativeSelectOption>
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
</NativeSelect>
)
}
Installation
npx @fictjs/shadcn@latest add native-selectCopy the Fict shadcn component into your project.
Source
components/ui/native-select.tsx
src/components/ui/native-select.tsx
import { cn } from '@/lib/cn'
type SelectProps = Omit<JSX.IntrinsicElements['select'], 'size'> & { size?: 'default' | 'sm' }
type OptionProps = JSX.IntrinsicElements['option']
type OptGroupProps = JSX.IntrinsicElements['optgroup']
export function NativeSelect(props: SelectProps) {
const { class: className, size = 'default', ...rest } = props
return (
<div class={cn('group/native-select relative w-fit has-[select:disabled]:opacity-50', className)} data-slot='native-select-wrapper' data-size={size}>
<select
data-slot='native-select'
data-size={size}
class={cn(
'h-8 w-full min-w-0 appearance-none rounded-lg border border-input bg-transparent py-1 pl-2.5 pr-8 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 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/20 data-[size=sm]:h-7 rtl:pl-8 rtl:pr-2.5',
)}
{...rest}
/>
<svg data-slot='native-select-icon' class='pointer-events-none absolute right-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground rtl:left-2.5 rtl:right-auto' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' aria-hidden='true'><path d='m6 9 6 6 6-6' /></svg>
</div>
)
}
export function NativeSelectOption(props: OptionProps) {
return <option data-slot='native-select-option' {...props} />
}
export function NativeSelectOptGroup(props: OptGroupProps) {
return <optgroup data-slot='native-select-optgroup' {...props} />
}
Usage
Examples
Groups
import {
NativeSelect,
...import {
NativeSelect,
NativeSelectOptGroup,
NativeSelectOption,
} from '@/components/ui/native-select'
export default function NativeSelectGroupsExample() {
return (
<NativeSelect>
<NativeSelectOption value="">Select department</NativeSelectOption>
<NativeSelectOptGroup label="Engineering">
<NativeSelectOption value="frontend">Frontend</NativeSelectOption>
<NativeSelectOption value="backend">Backend</NativeSelectOption>
<NativeSelectOption value="devops">DevOps</NativeSelectOption>
</NativeSelectOptGroup>
<NativeSelectOptGroup label="Sales">
<NativeSelectOption value="sales-rep">Sales Rep</NativeSelectOption>
<NativeSelectOption value="account-manager">Account Manager</NativeSelectOption>
<NativeSelectOption value="sales-director">Sales Director</NativeSelectOption>
</NativeSelectOptGroup>
<NativeSelectOptGroup label="Operations">
<NativeSelectOption value="support">Customer Support</NativeSelectOption>
<NativeSelectOption value="product-manager">Product Manager</NativeSelectOption>
<NativeSelectOption value="ops-manager">Operations Manager</NativeSelectOption>
</NativeSelectOptGroup>
</NativeSelect>
)
}
Disabled
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
...import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
export default function NativeSelectDisabledExample() {
return (
<NativeSelect disabled>
<NativeSelectOption value="">Disabled</NativeSelectOption>
<NativeSelectOption value="apple">Apple</NativeSelectOption>
<NativeSelectOption value="banana">Banana</NativeSelectOption>
<NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
</NativeSelect>
)
}
Invalid
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
...import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
export default function NativeSelectInvalidExample() {
return (
<NativeSelect aria-invalid="true">
<NativeSelectOption value="">Error state</NativeSelectOption>
<NativeSelectOption value="apple">Apple</NativeSelectOption>
<NativeSelectOption value="banana">Banana</NativeSelectOption>
<NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
</NativeSelect>
)
}
Rtl
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
...import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
const translations = {
ar: {
dir: 'rtl',
options: [
['', 'اختر الحالة'],
['todo', 'مهام'],
['in-progress', 'قيد التنفيذ'],
['done', 'منجز'],
['cancelled', 'ملغي'],
],
},
he: {
dir: 'rtl',
options: [
['', 'בחר סטטוס'],
['todo', 'לעשות'],
['in-progress', 'בתהליך'],
['done', 'הושלם'],
['cancelled', 'בוטל'],
],
},
en: {
dir: 'ltr',
options: [
['', 'Select status'],
['todo', 'Todo'],
['in-progress', 'In Progress'],
['done', 'Done'],
['cancelled', 'Cancelled'],
],
},
} as const
export default function NativeSelectRtlExample() {
let language = $state<keyof typeof translations>('ar')
const text = () => translations[language]
return (
<div class="grid gap-4">
<select
value={language}
onChange={event => {
language = event.currentTarget.value as keyof typeof translations
}}
>
<option value="ar">Arabic (العربية)</option>
<option value="he">Hebrew (עברית)</option>
<option value="en">English</option>
</select>
<NativeSelect dir={text().dir}>
{text().options.map(([value, label]) => (
<NativeSelectOption value={value}>{label}</NativeSelectOption>
))}
</NativeSelect>
</div>
)
}