Select
A native select component with consistent styling across browsers.
import { Select } from '@/components/select';
export default function SelectPreview() {
return (
<div className="w-90">
<Select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</div>
);
} Source Code
'use client';
import type { VariantProps } from 'cva';
import {
InputGroup,
InputPrefix,
inputStyle,
} from '@/components/input';
import { cn } from '@/lib/utils/classnames';
interface SelectProps extends React.ComponentPropsWithRef<'select'> {
invalid?: boolean;
variant?: VariantProps<typeof inputStyle>['variant'];
}
const Select = ({ className, invalid, variant, ...props }: SelectProps) => {
return (
<select
data-invalid={invalid}
className={cn(
inputStyle({ variant }),
'appearance-none bg-position-[right_--spacing(2)_center] bg-size-[1em] bg-no-repeat pr-10',
'bg-[url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48cGF0aCBmaWxsPSJibGFjayIgZD0iTTMuNyA1LjNsNC4zIDQuMyA0LjMtNC4zLjcuNy01IDUtNS01eiIvPjwvc3ZnPg==")]',
'dark:bg-[url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48cGF0aCBmaWxsPSJ3aGl0ZSIgZD0iTTMuNyA1LjNsNC4zIDQuMyA0LjMtNC4zLjcuNy01IDUtNS01eiIvPjwvc3ZnPg==")]',
className
)}
{...props}
/>
);
};
const SelectGroup = InputGroup;
const SelectPrefix = InputPrefix;
export { Select, SelectGroup, SelectPrefix }; Features
- Native Element: Uses the browser’s native select element for optimal mobile support
- Consistent Styling: Normalized appearance across different browsers
- Form Integration: Works seamlessly with form libraries and native form validation
- Prefix Support: Optional prefix for additional context
- Variants: Supports default and minimal visual styles
- Validation: Built-in invalid state styling
Anatomy
<SelectGroup>
<SelectPrefix />
<Select>
<option>
</Select>
</SelectGroup>
API Reference
Select
Extends the select element.
| Prop | Default | Type | Description |
|---|---|---|---|
variant | "default" | "default""minimal" | The visual style variant to use. |
invalid | false | boolean | Whether the select is in an invalid state. |
SelectGroup
Extends the div element.
A wrapper component that provides proper spacing and layout for the select and its prefix.
SelectPrefix
Extends the div element.
| Prop | Default | Type |
|---|---|---|
asChild | - | boolean |
Accessibility
The Select component uses the native <select> element, which provides excellent accessibility out of the box:
- Full keyboard navigation support
- Screen reader announcements
- Mobile-friendly interaction
- Native form integration
Examples
Simple
Basic usage with a list of options.
import { Select } from '@/components/select';
export default function SelectPreview() {
return (
<div className="w-90">
<Select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</div>
);
} Minimal
Using the minimal variant without borders.
import { Select } from '@/components/select';
export default function SelectMinimalPreview() {
return (
<div className="w-90">
<Select variant="minimal">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</div>
);
} Disabled
The select in a disabled state.
import { Select } from '@/components/select';
export default function SelectDisabledPreview() {
return (
<div className="w-90">
<Select disabled>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</div>
);
} Invalid
The select showing an invalid state.
import { Select } from '@/components/select';
export default function SelectInvalidPreview() {
return (
<div className="w-90">
<Select invalid>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</div>
);
} Prefix
Using a prefix to provide additional context.
import { UserIcon } from '@phosphor-icons/react/dist/ssr';
import {
Select,
SelectGroup,
SelectPrefix,
} from '@/components/select';
export default function SelectPrefixPreview() {
return (
<div className="w-90">
<SelectGroup>
<SelectPrefix>
<UserIcon />
</SelectPrefix>
<Select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</SelectGroup>
</div>
);
} Best Practices
-
Use Listbox for Complex Cases:
- If you need custom option rendering
- If you need search functionality
- If you need multiple selection
- If you need complex keyboard interactions
-
Mobile Considerations:
- The native select provides the best experience on mobile devices
- It automatically adapts to the platform’s native picker
-
Form Integration:
- Use the
requiredattribute for required fields - Use the
nameattribute for form submission - Use the
invalidprop in conjunction with form validation
- Use the
-
Accessibility:
- Always provide a visible label or aria-label
- Group related selects with fieldset and legend
- Use aria-describedby for additional descriptions
Previous
Radio
Next
Skeleton