Select
The Select component provides a dropdown interface for choosing from predefined options. It includes support for disabled options, validation states, and size variants while maintaining consistency with other form components through typography tokens.
Basic Select
Choose from predefined options
Select the most relevant category
Choose your country of residence
Selected Values:
Category: None
Country: None
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | required | Unique identifier for the select |
name | string | id | Form field name |
label | string | required | Field label text |
value | string | '' | Selected value (bindable) |
options | Option[] | required | Array of selectable options |
placeholder | string | 'Select option' | Placeholder text |
required | boolean | false | Whether field is required |
disabled | boolean | false | Disables the select |
error | string \| null | null | Error message to display |
helper | string | - | Helper text below select |
size | 'sm' \| 'md' \| 'lg' | 'md' | Select size variant |
Option Interface
interface Option {
value: string; // The value when selected
label: string; // Display text
disabled?: boolean; // Whether option is selectable
} Usage Examples
Basic Select
<script>
import Select from '$lib/shared/components/forms/Select.svelte';
let category = $state('');
const categories = [
{ value: 'tech', label: 'Technology' },
{ value: 'design', label: 'Design' },
{ value: 'marketing', label: 'Marketing' }
];
</script>
<Select
id="category"
label="Category"
bind:value={category}
{options}={categories}
placeholder="Choose category..."
/> With Disabled Options
<script>
import Select from '$lib/shared/components/forms/Select.svelte';
let priority = $state('');
const priorities = [
{ value: 'low', label: 'Low Priority' },
{ value: 'medium', label: 'Medium Priority' },
{ value: 'high', label: 'High Priority' },
{ value: 'critical', label: 'Critical (Admin Only)', disabled: true }
];
</script>
<Select
id="priority"
label="Task Priority"
bind:value={priority}
options={priorities}
helper="Critical priority requires admin access"
/> Pre-selected Value
<script>
import Select from '$lib/shared/components/forms/Select.svelte';
let country = $state('us'); // Pre-selected
const countries = [
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' },
{ value: 'ca', label: 'Canada' }
];
</script>
<Select
id="country"
label="Country"
bind:value={country}
options={countries}
helper="Default selection applied"
/> With Validation
<script>
import Select from '$lib/shared/components/forms/Select.svelte';
let selection = $state('');
let error = $state(null);
const options = [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' }
];
function validate() {
if (!selection) {
error = 'Please make a selection';
} else {
error = null;
}
}
</script>
<Select
id="required-select"
label="Required Field"
bind:value={selection}
{options}
{error}
required
onblur={validate}
/> Typography System
The Select component uses design system typography tokens:
Applied Tokens
/* Label Text */
--typography-caption-size
--typography-caption-color
--typography-caption-weight
/* Option Text */
--typography-body-size /* md size */
--typography-body-color
--typography-body-weight
/* Helper/Error Text */
--typography-caption-size
--typography-muted-color /* helper */
--error /* error state */ Size Variants
| Size | Typography Tokens | Use Case |
|---|---|---|
sm | caption | Compact forms, filters |
md | body (default) | Standard forms |
lg | heading | Prominent selections |
Styling
Visual Design
- Consistent appearance - Matches TextInput styling
- Dropdown arrow - Custom chevron icon
- Hover states - Subtle background changes
- Focus indicators - Clear focus ring
- Dark mode support - Automatic color adjustments
CSS Classes
.field-select- Base select element.select-wrapper- Container with icon positioning.select-icon- Dropdown arrow styling.field-error- Error state modifier.field-error-message- Error message styling.field-hint- Helper text styling
Custom Styling
The native select element is styled to match the design system:
.field-select {
appearance: none; /* Remove native styling */
background-image: none; /* Custom arrow via icon */
padding-right: 2.5rem; /* Space for arrow icon */
} Accessibility
ARIA Support
aria-required- Indicates required fieldsaria-invalid- Communicates error statesaria-describedby- Links helper text and errorsrole="alert"- Error messages announced
Keyboard Interaction
- Tab - Navigate to select
- Space/Enter - Open dropdown
- Arrow keys - Navigate options
- Escape - Close dropdown
- Type ahead - Jump to options by typing
Label Association
- Proper
for/idlinking between label and select - Clicking label focuses the select element
- Screen readers announce label when focused
Option Management
Dynamic Options
Options can be updated reactively:
<script>
let category = $state('');
let subcategory = $state('');
const categoryOptions = [/*...*/];
// Reactive subcategory options based on category
const subcategoryOptions = $derived(() => {
if (category === 'tech') {
return [
{ value: 'frontend', label: 'Frontend' },
{ value: 'backend', label: 'Backend' }
];
}
return [];
});
// Reset subcategory when category changes
$effect(() => {
if (category) {
subcategory = '';
}
});
</script>
<Select
id="category"
label="Category"
bind:value={category}
options={categoryOptions}
/>
{#if subcategoryOptions.length > 0}
<Select
id="subcategory"
label="Subcategory"
bind:value={subcategory}
options={subcategoryOptions}
/>
{/if} Option Formatting
For complex option data:
<script>
const users = [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
];
const userOptions = users.map(user => ({
value: user.id,
label: `${user.name} (${user.email})`
}));
</script> Validation Patterns
Required Selection
<script>
let value = $state('');
let error = $state(null);
function handleSubmit() {
if (!value) {
error = 'Please make a selection';
return;
}
// Submit logic
}
</script> Conditional Validation
<script>
let paymentMethod = $state('');
let cardType = $state('');
let cardError = $state(null);
$effect(() => {
if (paymentMethod === 'card' && !cardType) {
cardError = 'Please select card type';
} else {
cardError = null;
}
});
</script> Best Practices
Option Organization
- Logical ordering - Alphabetical or by frequency of use
- Group related options - Use separators or grouping
- Limit options - Consider search/filter for >10 options
- Clear labels - Descriptive and unambiguous
User Experience
- Sensible defaults - Pre-select common choices
- Progressive disclosure - Show relevant options based on context
- Error recovery - Clear guidance on fixing invalid selections
- Loading states - Show feedback when options are loading
Performance
- Large option sets - Consider virtual scrolling
- Search functionality - For 20+ options
- Lazy loading - Load options when needed
- Memoization - Cache computed option arrays
Common Patterns
Cascading Selects
Country → State → City selection chains
Multi-level Categories
Product categories with subcategories
User/Role Selection
Users grouped by role or department
Date/Time Pickers
Separate selects for day, month, year
Alternatives
Consider these alternatives for specific use cases:
- Radio buttons - For 2-5 visible options
- Checkbox groups - For multiple selections
- Autocomplete - For searchable options
- Custom dropdowns - For complex option rendering
Related Components
- RadioGroup - Single selection with visible options
- Checkbox - Multiple selection
- TextInput - Text entry with autocomplete
- Toggle - Binary on/off choices