Select
A select component for choosing a value from a list of options. Features keyboard navigation, grouped options, custom positioning, and full accessibility support.
Usage
Basic Select
A simple select with options:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function BasicSelect() {
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="orange">Orange</SelectItem>
<SelectItem value="grape">Grape</SelectItem>
</SelectContent>
</Select>
);
}Live Example:
Live Example:
Grouped Options
Organize options into labeled groups:
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function GroupedSelect() {
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a language" className="capitalize" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Frontend</SelectLabel>
<SelectItem value="javascript">JavaScript</SelectItem>
<SelectItem value="typescript">TypeScript</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Backend</SelectLabel>
<SelectItem value="python">Python</SelectItem>
<SelectItem value="rust">Rust</SelectItem>
<SelectItem value="go">Go</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
);
}Live Example:
Live Example:
Small Size
Use the sm size for compact layouts:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function SmallSelect() {
return (
<Select>
<SelectTrigger size="sm">
<SelectValue placeholder="Select size" className="capitalize" />
</SelectTrigger>
<SelectContent>
<SelectItem value="xs">Extra Small</SelectItem>
<SelectItem value="sm">Small</SelectItem>
<SelectItem value="md">Medium</SelectItem>
<SelectItem value="lg">Large</SelectItem>
</SelectContent>
</Select>
);
}Live Example:
Live Example:
Disabled State
Disable the select when interaction should be prevented:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function DisabledSelect() {
return (
<Select disabled>
<SelectTrigger>
<SelectValue placeholder="Disabled select" className="capitalize" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
</SelectContent>
</Select>
);
}Live Example:
Live Example:
With Field Component
Use with Field for proper form structure:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Field,
FieldLabel,
FieldDescription,
} from "@prisma/eclipse";
export function SelectWithField() {
return (
<Field>
<FieldLabel htmlFor="country">Country</FieldLabel>
<Select>
<SelectTrigger id="country">
<SelectValue placeholder="Select your country" className="capitalize" />
</SelectTrigger>
<SelectContent>
<SelectItem value="us">United States</SelectItem>
<SelectItem value="uk">United Kingdom</SelectItem>
<SelectItem value="ca">Canada</SelectItem>
<SelectItem value="au">Australia</SelectItem>
</SelectContent>
</Select>
<FieldDescription>Choose your country of residence.</FieldDescription>
</Field>
);
}Live Example:
Live Example:
Choose your country of residence.
Position Control
Control the position and alignment of the dropdown:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function PositionedSelect() {
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select position" className="capitalize" />
</SelectTrigger>
<SelectContent side="top" align="end">
<SelectItem value="top">Top</SelectItem>
<SelectItem value="bottom">Bottom</SelectItem>
<SelectItem value="left">Left</SelectItem>
<SelectItem value="right">Right</SelectItem>
</SelectContent>
</Select>
);
}Live Example:
Live Example:
Disabled Items
Disable individual items:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function SelectWithDisabledItems() {
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select an option" className="capitalize" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Available Option</SelectItem>
<SelectItem value="option2" disabled>
Disabled Option
</SelectItem>
<SelectItem value="option3">Another Available</SelectItem>
<SelectItem value="option4" disabled>
Also Disabled
</SelectItem>
</SelectContent>
</Select>
);
}Live Example:
Live Example:
API Reference
Select
The root component that manages select state.
Props:
value- Controlled value (string, optional)onValueChange- Callback when value changes ((value: string) => void, optional)open- Controlled open state (boolean, optional)onOpenChange- Callback when open state changes ((open: boolean) => void, optional)disabled- Disable the select (boolean, default: false)defaultValue- Default value for uncontrolled mode (string, optional)defaultOpen- Default open state (boolean, default: false)name- Form input name (string, optional)required- Mark as required (boolean, default: false)
<Select value={value} onValueChange={setValue}>
{/* SelectTrigger and SelectContent */}
</Select>SelectTrigger
The button that triggers the select dropdown.
Props:
size- Size variant ("sm" | "default", default: "default")className- Additional CSS classes (string or function, optional)disabled- Disable the trigger (boolean, default: false)- All standard HTML button attributes
<SelectTrigger size="sm">
<SelectValue placeholder="Choose..." />
</SelectTrigger>SelectValue
Displays the currently selected value or placeholder.
Props:
placeholder- Text to show when no value is selected (string, optional)className- Additional CSS classes (string or function, optional)
<SelectValue placeholder="Select an option" />SelectContent
The dropdown container that holds the list of options.
Props:
side- Position relative to trigger ("top" | "bottom" | "left" | "right", default: "bottom")align- Alignment relative to trigger ("start" | "center" | "end", default: "center")sideOffset- Distance from trigger in pixels (number, default: 4)alignOffset- Alignment offset in pixels (number, default: 0)alignItemWithTrigger- Align selected item with trigger (boolean, default: true)className- Additional CSS classes (string or function, optional)
<SelectContent side="top" align="end" sideOffset={8}>
{/* SelectItem components */}
</SelectContent>SelectItem
An individual option in the select list.
Props:
value- The value of the item (string, required)disabled- Disable the item (boolean, default: false)className- Additional CSS classes (string or function, optional)children- Item content (ReactNode)
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2" disabled>Disabled Option</SelectItem>SelectGroup
A container for grouping related items.
Props:
className- Additional CSS classes (string or function, optional)children- Group content (ReactNode)
<SelectGroup>
<SelectLabel>Category</SelectLabel>
<SelectItem value="item1">Item 1</SelectItem>
</SelectGroup>SelectLabel
A label for a group of items.
Props:
className- Additional CSS classes (string or function, optional)children- Label content (ReactNode)
<SelectLabel>Frontend Languages</SelectLabel>SelectSeparator
A visual separator between groups or items.
Props:
className- Additional CSS classes (string or function, optional)
<SelectSeparator />SelectScrollUpButton
Internal component for scrolling up in long lists. Automatically rendered.
SelectScrollDownButton
Internal component for scrolling down in long lists. Automatically rendered.
Features
- ✅ Two size variants (default and small)
- ✅ Keyboard navigation (Arrow keys, Enter, Escape, Tab, Home, End)
- ✅ Grouped options with labels
- ✅ Custom positioning and alignment
- ✅ Disabled state for select and individual items
- ✅ Controlled and uncontrolled modes
- ✅ Scroll buttons for long lists
- ✅ Item indicator with checkmark
- ✅ Accessible with ARIA attributes
- ✅ Portal rendering for z-index management
- ✅ Animations for open/close transitions
- ✅ Auto-alignment of selected item with trigger
- ✅ Form integration with name and required attributes
- ✅ Fully typed with TypeScript
Best Practices
- Always provide a placeholder for better UX
- Use SelectGroup and SelectLabel for organized lists
- Consider using the small size in dense layouts
- Disable items that are temporarily unavailable rather than hiding them
- Keep option text concise and scannable
- Use Field component for proper form structure with labels
- Test keyboard navigation thoroughly
- Avoid extremely long lists (consider search/filter for 50+ items)
- Use consistent option formatting
- Provide clear value descriptions
Accessibility
The Select component follows ARIA combobox pattern specifications:
- Uses semantic ARIA roles (
combobox,listbox,option) - Supports full keyboard navigation:
Space/Enter- Open/close and selectArrow Down/Up- Navigate through optionsHome/End- Jump to first/last optionEscape- Close the dropdownTab- Move focus and close dropdown
- Focus management and trapped focus in dropdown
- Screen reader announcements for selections and state changes
- Proper labeling with
aria-labelandaria-labelledby - Visual focus indicators
- Selected state announced to assistive technologies
- Disabled items are properly marked and unfocusable
- Required attribute for form validation
Common Use Cases
The Select component is perfect for:
- Form inputs - Collecting user choices in forms
- Settings - Configuration and preference selection
- Filters - Filtering data by category
- Language/locale switchers - Selecting language or region
- Status updates - Changing item status
- Priority selection - Setting task or issue priority
- Category selection - Choosing from predefined categories
- Time zones - Selecting time zone
- Country/region pickers - Location selection
- Sorting options - Choosing sort order
- Role assignment - Selecting user roles
Styling
The Select component uses design tokens and can be customized:
- Trigger: Border, rounded corners, padding, hover/focus states
- Content: Popover with shadow, rounded corners, animations
- Items: Hover/focus states with accent colors
- Selected indicator: Checkmark icon aligned right
- Groups: Scroll margin and padding
- Labels: Muted text, small font size
- Separator: Border line between groups
- Scroll buttons: Positioned at top/bottom with icons
Customize by passing className props:
<Select>
<SelectTrigger className="w-full border-2">
<SelectValue placeholder="Custom styled" />
</SelectTrigger>
<SelectContent className="max-h-[200px]">
<SelectItem className="font-bold" value="custom">
Custom Item
</SelectItem>
</SelectContent>
</Select>Integration with Forms
Use with form libraries like React Hook Form:
import { useForm, Controller } from "react-hook-form";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@prisma/eclipse";
export function SelectForm() {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<Controller
name="country"
control={control}
render={({ field }) => (
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder="Select country" />
</SelectTrigger>
<SelectContent>
<SelectItem value="us">United States</SelectItem>
<SelectItem value="uk">United Kingdom</SelectItem>
<SelectItem value="ca">Canada</SelectItem>
</SelectContent>
</Select>
)}
/>
<button type="submit">Submit</button>
</form>
);
}Controlled vs Uncontrolled
Uncontrolled (with defaultValue):
<Select defaultValue="option1">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
</SelectContent>
</Select>Controlled (with value and onValueChange):
const [value, setValue] = useState("option1");
<Select value={value} onValueChange={setValue}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
</SelectContent>
</Select>Performance Tips
For large option lists:
- Virtualization - Consider using virtual scrolling for 100+ items
- Lazy loading - Load options on-demand
- Memoization - Use React.memo for SelectItem components
- Search/filter - Add search for easier navigation
- Pagination - Load options in batches
import { useMemo } from "react";
export function OptimizedSelect({ items }) {
const memoizedItems = useMemo(
() => items.map((item) => (
<SelectItem key={item.id} value={item.id}>
{item.name}
</SelectItem>
)),
[items]
);
return (
<Select>
<SelectTrigger>
<SelectValue placeholder="Select..." />
</SelectTrigger>
<SelectContent>
{memoizedItems}
</SelectContent>
</Select>
);
}