Select
The Select component provides a dropdown selection interface using a bottom sheet. It features searchable options, smooth animations, and supports both single selection and large option lists with optimized scrolling.
Import
Section titled “Import”import { Select, type SelectOption } from '@space-uy/pulsar-ui';Basic usage
Section titled “Basic usage”const [selectedOption, setSelectedOption] = useState<  SelectOption | undefined>();
const options = [  { value: 'option1', label: 'Option 1' },  { value: 'option2', label: 'Option 2' },  { value: 'option3', label: 'Option 3' },];
<Select  options={options}  value={selectedOption}  onChange={setSelectedOption}  placeholder="Select an option"/>;Properties
Section titled “Properties”| Property | Type | Required | Default value | Description | 
|---|---|---|---|---|
| options | SelectOption[] | ✅ | - | Array of selectable options | 
| value | SelectOption | ❌ | - | Currently selected option | 
| onChange | (option: SelectOption) => void | ✅ | - | Callback when selection changes | 
| placeholder | string | ❌ | 'Select' | Placeholder text when no option is selected | 
| title | string | ❌ | - | Title displayed in the bottom sheet | 
| label | string | ❌ | - | Label text displayed above the select | 
| disabled | boolean | ❌ | false | Whether the select is disabled | 
| error | boolean | ❌ | false | Whether the select is in error state | 
| hint | string | ❌ | - | Hint text displayed below the select | 
| style | StyleProp<ViewStyle> | ❌ | - | Custom styles for the select container | 
SelectOption Type
Section titled “SelectOption Type”The SelectOption object has the following structure:
| Property | Type | Required | Description | 
|---|---|---|---|
| value | string | ✅ | Unique identifier for the option | 
| label | string | ✅ | Display text for the option | 
Basic examples
Section titled “Basic examples”Simple select
Section titled “Simple select”const [country, setCountry] = useState<SelectOption | undefined>();
const countries = [  { value: 'us', label: 'United States' },  { value: 'ca', label: 'Canada' },  { value: 'uk', label: 'United Kingdom' },  { value: 'au', label: 'Australia' },];
<Select  label="Country"  options={countries}  value={country}  onChange={setCountry}  placeholder="Select a country"/>;Select with title
Section titled “Select with title”const [priority, setPriority] = useState<SelectOption | undefined>();
const priorities = [  { value: 'low', label: 'Low Priority' },  { value: 'medium', label: 'Medium Priority' },  { value: 'high', label: 'High Priority' },  { value: 'urgent', label: 'Urgent' },];
<Select  label="Priority Level"  title="Choose Priority"  options={priorities}  value={priority}  onChange={setPriority}  placeholder="Select priority level"/>;Error state select
Section titled “Error state select”const [category, setCategory] = useState<SelectOption | undefined>();const [hasError, setHasError] = useState(false);
const categories = [  { value: 'tech', label: 'Technology' },  { value: 'design', label: 'Design' },  { value: 'marketing', label: 'Marketing' },];
<Select  label="Category"  options={categories}  value={category}  onChange={(option) => {    setCategory(option);    setHasError(false);  }}  error={hasError}  hint={    hasError ? 'Please select a category' : 'Choose the most relevant category'  }  placeholder="Select category"/>;Advanced examples
Section titled “Advanced examples”User profile form
Section titled “User profile form”const [profile, setProfile] = useState({  title: undefined as SelectOption | undefined,  country: undefined as SelectOption | undefined,  industry: undefined as SelectOption | undefined,  experience: undefined as SelectOption | undefined,});
const titles = [  { value: 'mr', label: 'Mr.' },  { value: 'mrs', label: 'Mrs.' },  { value: 'ms', label: 'Ms.' },  { value: 'dr', label: 'Dr.' },  { value: 'prof', label: 'Prof.' },];
const countries = [  { value: 'us', label: 'United States' },  { value: 'ca', label: 'Canada' },  { value: 'uk', label: 'United Kingdom' },  { value: 'de', label: 'Germany' },  { value: 'fr', label: 'France' },  { value: 'jp', label: 'Japan' },  { value: 'au', label: 'Australia' },];
const industries = [  { value: 'tech', label: 'Technology' },  { value: 'healthcare', label: 'Healthcare' },  { value: 'finance', label: 'Finance' },  { value: 'education', label: 'Education' },  { value: 'retail', label: 'Retail' },  { value: 'manufacturing', label: 'Manufacturing' },];
const experienceLevels = [  { value: 'entry', label: 'Entry Level (0-2 years)' },  { value: 'mid', label: 'Mid Level (3-5 years)' },  { value: 'senior', label: 'Senior Level (6-10 years)' },  { value: 'lead', label: 'Lead Level (10+ years)' },];
<Card>  <Text variant="h3">Professional Profile</Text>
  <View style={{ gap: 16, marginTop: 16 }}>    <View style={{ flexDirection: 'row', gap: 12 }}>      <View style={{ flex: 1 }}>        <Select          label="Title"          options={titles}          value={profile.title}          onChange={(title) => setProfile((prev) => ({ ...prev, title }))}          placeholder="Select title"        />      </View>      <View style={{ flex: 2 }}>        <Input label="Full Name" placeholder="Enter your full name" />      </View>    </View>
    <Select      label="Country"      title="Select Your Country"      options={countries}      value={profile.country}      onChange={(country) => setProfile((prev) => ({ ...prev, country }))}      placeholder="Choose your country"    />
    <Select      label="Industry"      title="Select Industry"      options={industries}      value={profile.industry}      onChange={(industry) => setProfile((prev) => ({ ...prev, industry }))}      placeholder="Choose your industry"    />
    <Select      label="Experience Level"      title="Experience Level"      options={experienceLevels}      value={profile.experience}      onChange={(experience) => setProfile((prev) => ({ ...prev, experience }))}      placeholder="Select experience level"    />  </View></Card>;Settings with large option list
Section titled “Settings with large option list”const [settings, setSettings] = useState({  language: undefined as SelectOption | undefined,  timezone: undefined as SelectOption | undefined,  theme: undefined as SelectOption | undefined,});
// Large list of languagesconst languages = [  { value: 'en', label: 'English' },  { value: 'es', label: 'Español' },  { value: 'fr', label: 'Français' },  { value: 'de', label: 'Deutsch' },  { value: 'it', label: 'Italiano' },  { value: 'pt', label: 'Português' },  { value: 'ru', label: 'Русский' },  { value: 'ja', label: '日本語' },  { value: 'ko', label: '한국어' },  { value: 'zh', label: '中文' },  // ... many more options];
// Timezone optionsconst timezones = [  { value: 'america/new_york', label: 'Eastern Time (ET)' },  { value: 'america/chicago', label: 'Central Time (CT)' },  { value: 'america/denver', label: 'Mountain Time (MT)' },  { value: 'america/los_angeles', label: 'Pacific Time (PT)' },  { value: 'europe/london', label: 'Greenwich Mean Time (GMT)' },  { value: 'europe/paris', label: 'Central European Time (CET)' },  { value: 'asia/tokyo', label: 'Japan Standard Time (JST)' },  { value: 'australia/sydney', label: 'Australian Eastern Time (AET)' },];
const themes = [  { value: 'light', label: 'Light Theme' },  { value: 'dark', label: 'Dark Theme' },  { value: 'auto', label: 'Auto (System)' },];
<Card>  <Text variant="h3">App Preferences</Text>
  <View style={{ gap: 16, marginTop: 16 }}>    <Select      label="Language"      title="Choose Language"      options={languages}      value={settings.language}      onChange={(language) => setSettings((prev) => ({ ...prev, language }))}      placeholder="Select language"      hint="Choose your preferred language"    />
    <Select      label="Timezone"      title="Select Timezone"      options={timezones}      value={settings.timezone}      onChange={(timezone) => setSettings((prev) => ({ ...prev, timezone }))}      placeholder="Select timezone"      hint="This affects how dates and times are displayed"    />
    <Select      label="Theme"      options={themes}      value={settings.theme}      onChange={(theme) => setSettings((prev) => ({ ...prev, theme }))}      placeholder="Select theme"    />  </View></Card>;Filter form
Section titled “Filter form”const [filters, setFilters] = useState({  category: undefined as SelectOption | undefined,  priceRange: undefined as SelectOption | undefined,  rating: undefined as SelectOption | undefined,  sortBy: undefined as SelectOption | undefined,});
const categories = [  { value: 'electronics', label: 'Electronics' },  { value: 'clothing', label: 'Clothing' },  { value: 'books', label: 'Books' },  { value: 'home', label: 'Home & Garden' },  { value: 'sports', label: 'Sports' },];
const priceRanges = [  { value: '0-25', label: '$0 - $25' },  { value: '25-50', label: '$25 - $50' },  { value: '50-100', label: '$50 - $100' },  { value: '100-200', label: '$100 - $200' },  { value: '200+', label: '$200+' },];
const ratings = [  { value: '4+', label: '4 Stars & Up' },  { value: '3+', label: '3 Stars & Up' },  { value: '2+', label: '2 Stars & Up' },  { value: '1+', label: '1 Star & Up' },];
const sortOptions = [  { value: 'relevance', label: 'Relevance' },  { value: 'price-low', label: 'Price: Low to High' },  { value: 'price-high', label: 'Price: High to Low' },  { value: 'rating', label: 'Customer Rating' },  { value: 'newest', label: 'Newest First' },];
const clearAllFilters = () => {  setFilters({    category: undefined,    priceRange: undefined,    rating: undefined,    sortBy: undefined,  });};
<Card>  <View    style={{      flexDirection: 'row',      justifyContent: 'space-between',      alignItems: 'center',    }}  >    <Text variant="h3">Filter Products</Text>    <Button      text="Clear All"      variant="outline"      size="small"      onPress={clearAllFilters}    />  </View>
  <View style={{ gap: 16, marginTop: 16 }}>    <Select      label="Category"      options={categories}      value={filters.category}      onChange={(category) => setFilters((prev) => ({ ...prev, category }))}      placeholder="All categories"    />
    <Select      label="Price Range"      options={priceRanges}      value={filters.priceRange}      onChange={(priceRange) => setFilters((prev) => ({ ...prev, priceRange }))}      placeholder="Any price"    />
    <Select      label="Customer Rating"      options={ratings}      value={filters.rating}      onChange={(rating) => setFilters((prev) => ({ ...prev, rating }))}      placeholder="Any rating"    />
    <Select      label="Sort By"      options={sortOptions}      value={filters.sortBy}      onChange={(sortBy) => setFilters((prev) => ({ ...prev, sortBy }))}      placeholder="Relevance"    />  </View>
  <Button    text="Apply Filters"    style={{ marginTop: 20 }}    onPress={() => console.log('Filters applied:', filters)}  /></Card>;Dynamic options
Section titled “Dynamic options”const [selectedCountry, setSelectedCountry] = useState<  SelectOption | undefined>();const [selectedCity, setSelectedCity] = useState<SelectOption | undefined>();
const countries = [  { value: 'us', label: 'United States' },  { value: 'ca', label: 'Canada' },  { value: 'uk', label: 'United Kingdom' },];
const citiesByCountry: Record<string, SelectOption[]> = {  us: [    { value: 'ny', label: 'New York' },    { value: 'la', label: 'Los Angeles' },    { value: 'chicago', label: 'Chicago' },    { value: 'houston', label: 'Houston' },  ],  ca: [    { value: 'toronto', label: 'Toronto' },    { value: 'vancouver', label: 'Vancouver' },    { value: 'montreal', label: 'Montreal' },    { value: 'calgary', label: 'Calgary' },  ],  uk: [    { value: 'london', label: 'London' },    { value: 'manchester', label: 'Manchester' },    { value: 'birmingham', label: 'Birmingham' },    { value: 'liverpool', label: 'Liverpool' },  ],};
const cities = selectedCountry  ? citiesByCountry[selectedCountry.value] || []  : [];
useEffect(() => {  // Reset city when country changes  if (selectedCountry) {    setSelectedCity(undefined);  }}, [selectedCountry]);
<Card>  <Text variant="h3">Location Selection</Text>
  <View style={{ gap: 16, marginTop: 16 }}>    <Select      label="Country"      options={countries}      value={selectedCountry}      onChange={setSelectedCountry}      placeholder="Select country"    />
    <Select      label="City"      options={cities}      value={selectedCity}      onChange={setSelectedCity}      placeholder={selectedCountry ? 'Select city' : 'Select country first'}      disabled={!selectedCountry}      hint={!selectedCountry ? 'Please select a country first' : ''}    />  </View></Card>;Disabled select
Section titled “Disabled select”<Select  label="Premium Feature"  options={[    { value: 'feature1', label: 'Advanced Analytics' },    { value: 'feature2', label: 'Custom Reports' },    { value: 'feature3', label: 'Priority Support' },  ]}  value={undefined}  onChange={() => {}}  placeholder="Upgrade to access"  disabled={true}  hint="This feature requires a premium subscription"/>Implementation notes
Section titled “Implementation notes”- Uses BottomSheet component for the options display on mobile devices
- Smooth chevron rotation animation when opening/closing the select
- Options are displayed in a scrollable FlatList for performance with large lists
- Selected option is highlighted with a check icon and different background
- The select automatically closes when an option is selected
- Full screen mode is used when there are more than 20 options for better UX
- InputContainer provides consistent styling with other form inputs
Animation details
Section titled “Animation details”- Chevron rotation: 200ms animation with 180-degree rotation when opening
- Bottom sheet: Smooth slide-up animation from the bottom
- Option selection: Instant feedback with check icon and background change
- Close animation: Smooth slide-down when backdrop is pressed or option selected
Accessibility
Section titled “Accessibility”- The select is fully keyboard accessible
- Screen readers properly announce the current selection and available options
- Bottom sheet traps focus correctly for keyboard navigation
- Disabled state prevents interaction and is communicated to assistive technologies
- Option selection is properly announced to screen readers
- Color contrast meets accessibility guidelines for all states
Performance
Section titled “Performance”- Large option lists are rendered using FlatList for optimal performance
- Only visible options are rendered, with efficient scrolling for hundreds of items
- Smooth animations without impacting performance
- Efficient re-rendering when options or selection changes