Select

free

Select lets users choose one option or several from a searchable list.

@floating-ui/react
Preview
Dark
    import Select from '@/components/ui/Select';
    
    const frameworks = [
      { value: 'next', label: 'Next.js' },
      { value: 'remix', label: 'Remix' },
      { value: 'astro', label: 'Astro' },
      { value: 'nuxt', label: 'Nuxt' },
      { value: 'sveltekit', label: 'SvelteKit' },
    ];
    
    export default function UsageDemo() {
      return (
        <div className="w-full max-w-sm">
          <Select placeholder="Select a framework" options={frameworks} />
        </div>
      );
    }

    Installation

    Add this component with the NateUI CLI.

    npx nateui@latest add Select

    Examples

    Basic

    Preview
    Dark
      import Select from '@/components/ui/Select';
      
      const roles = [
        { value: 'owner', label: 'Owner' },
        { value: 'admin', label: 'Admin' },
        { value: 'editor', label: 'Editor' },
        { value: 'viewer', label: 'Viewer' },
      ];
      
      export default function BasicDemo() {
        return (
          <div className="w-full max-w-sm">
            <Select
              placeholder="Assign a role"
              defaultValue={roles[2]}
              options={roles}
            />
          </div>
        );
      }

      Multiple

      Preview
      Dark
      TypeScriptReact
        import Select from '@/components/ui/Select';
        
        const skills = [
          { value: 'ts', label: 'TypeScript' },
          { value: 'react', label: 'React' },
          { value: 'node', label: 'Node.js' },
          { value: 'go', label: 'Go' },
          { value: 'rust', label: 'Rust' },
          { value: 'python', label: 'Python' },
        ];
        
        export default function MultipleDemo() {
          return (
            <div className="w-full max-w-sm">
              <Select.Multi
                placeholder="Select skills"
                defaultValue={[skills[0], skills[1]]}
                options={skills}
              />
            </div>
          );
        }

        Size

        Preview
        Dark
              import Select from '@/components/ui/Select';
              
              const timezones = [
                { value: 'utc', label: 'UTC' },
                { value: 'est', label: 'Eastern (EST)' },
                { value: 'pst', label: 'Pacific (PST)' },
                { value: 'cet', label: 'Central Europe (CET)' },
              ];
              
              export default function SizeDemo() {
                return (
                  <div className="flex w-full max-w-sm flex-col gap-4">
                    <Select size="sm" placeholder="Small" options={timezones} />
                    <Select size="md" placeholder="Medium" options={timezones} />
                    <Select size="lg" placeholder="Large" options={timezones} />
                  </div>
                );
              }

              Searchable

              Preview
              Dark
                import Select from '@/components/ui/Select';
                
                const countries = [
                  { value: 'us', label: 'United States' },
                  { value: 'ca', label: 'Canada' },
                  { value: 'gb', label: 'United Kingdom' },
                  { value: 'de', label: 'Germany' },
                  { value: 'fr', label: 'France' },
                  { value: 'jp', label: 'Japan' },
                  { value: 'sg', label: 'Singapore' },
                  { value: 'au', label: 'Australia' },
                ];
                
                export default function SearchableDemo() {
                  return (
                    <div className="w-full max-w-sm">
                      <Select
                        isSearchable
                        placeholder="Select a country"
                        options={countries}
                        searchInputProps={{ placeholder: 'Search countries...' }}
                      />
                    </div>
                  );
                }

                Disabled

                Preview
                Dark
                    import Select from '@/components/ui/Select';
                    
                    const plans = [
                      { value: 'free', label: 'Free' },
                      { value: 'pro', label: 'Pro' },
                      { value: 'team', label: 'Team', disabled: true },
                      { value: 'enterprise', label: 'Enterprise', disabled: true },
                    ];
                    
                    export default function DisabledDemo() {
                      return (
                        <div className="flex w-full max-w-sm flex-col gap-4">
                          <Select placeholder="Some plans unavailable" options={plans} />
                          <Select isDisabled defaultValue={plans[1]} options={plans} />
                        </div>
                      );
                    }

                    Grouped

                    Preview
                    Dark
                      import Select from '@/components/ui/Select';
                      
                      const groupedOptions = [
                        {
                          label: 'Frontend',
                          options: [
                            { value: 'react', label: 'React' },
                            { value: 'vue', label: 'Vue' },
                            { value: 'svelte', label: 'Svelte' },
                          ],
                        },
                        {
                          label: 'Backend',
                          options: [
                            { value: 'node', label: 'Node.js' },
                            { value: 'django', label: 'Django' },
                            { value: 'rails', label: 'Rails' },
                          ],
                        },
                      ];
                      
                      export default function GroupedDemo() {
                        return (
                          <div className="w-full max-w-sm">
                            <Select<object>
                              placeholder="Select a technology"
                              options={groupedOptions}
                              formatGroupLabel={(group) => (
                                <div className="px-1 py-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                                  {group.label}
                                </div>
                              )}
                            />
                          </div>
                        );
                      }

                      Custom Options

                      Preview
                      Dark
                        import Select from '@/components/ui/Select';
                        import Avatar from '@/components/ui/Avatar';
                        
                        const members = [
                          { value: 'aria', label: 'Aria Patel', avatar: '/img/avatars/thumb-5.jpg' },
                          { value: 'diego', label: 'Diego Ramos', avatar: '/img/avatars/thumb-6.jpg' },
                          { value: 'mei', label: 'Mei Lin', avatar: '/img/avatars/thumb-7.jpg' },
                          { value: 'sam', label: 'Sam Turner', avatar: '/img/avatars/thumb-8.jpg' },
                        ];
                        
                        function MemberAvatar({ src, alt }: { src: string; alt: string }) {
                          return <Avatar size={22} src={src} alt={alt} />;
                        }
                        
                        export default function CustomOptionsDemo() {
                          return (
                            <div className="w-full max-w-sm">
                              <Select
                                placeholder="Assign a member"
                                defaultValue={members[0]}
                                options={members}
                                customInputDisplay={(selectedItem) =>
                                  selectedItem ? (
                                    <Select.ValueWithPrefix
                                      prefix={
                                        <MemberAvatar
                                          src={selectedItem.avatar}
                                          alt={selectedItem.label}
                                        />
                                      }
                                      label={selectedItem.label}
                                    />
                                  ) : null
                                }
                                customOption={({ option, selected, CheckIcon }) => (
                                  <Select.OptionWithPrefix
                                    prefix={<MemberAvatar src={option.avatar} alt={option.label} />}
                                    label={option.label}
                                    selected={selected}
                                    checkIcon={CheckIcon}
                                  />
                                )}
                              />
                            </div>
                          );
                        }

                        Creatable

                        Preview
                        Dark
                          import { useState } from 'react';
                          import Select from '@/components/ui/Select';
                          
                          type Tag = { value: string; label: string };
                          
                          const initialTags: Tag[] = [
                            { value: 'bug', label: 'bug' },
                            { value: 'feature', label: 'feature' },
                            { value: 'docs', label: 'docs' },
                          ];
                          
                          export default function CreatableDemo() {
                            const [tags, setTags] = useState<Tag[]>(initialTags);
                            const [selected, setSelected] = useState<Tag[]>([]);
                          
                            const handleChange = (next: Tag[]) => {
                              const known = new Set(tags.map((tag) => tag.value));
                              const created = next.filter((tag) => !known.has(tag.value));
                              if (created.length) {
                                setTags((prev) => [...prev, ...created]);
                              }
                              setSelected(next);
                            };
                          
                            return (
                              <div className="w-full max-w-sm">
                                <Select.Multi
                                  isCreatable
                                  isSearchable
                                  placeholder="Select or create tags"
                                  options={tags}
                                  value={selected}
                                  onChange={handleChange}
                                  searchInputProps={{ placeholder: 'Type to search or create...' }}
                                />
                              </div>
                            );
                          }

                          Async

                          Preview
                          Dark
                            import { useState } from 'react';
                            import Select from '@/components/ui/Select';
                            
                            type Repo = { value: string; label: string };
                            
                            const allRepos: Repo[] = [
                              { value: 'core', label: 'platform-core' },
                              { value: 'ui', label: 'design-system' },
                              { value: 'docs', label: 'docs-site' },
                              { value: 'infra', label: 'infra-tooling' },
                            ];
                            
                            export default function AsyncDemo() {
                              const [repos, setRepos] = useState<Repo[]>([]);
                              const [loading, setLoading] = useState(false);
                            
                              const loadOnOpen = () => {
                                if (repos.length > 0) {
                                  return;
                                }
                                setLoading(true);
                                // Mock a request; replace with a real fetch in your app.
                                setTimeout(() => {
                                  setRepos(allRepos);
                                  setLoading(false);
                                }, 1200);
                              };
                            
                              return (
                                <div className="w-full max-w-sm">
                                  <Select
                                    placeholder="Select a repository"
                                    options={repos}
                                    isLoading={loading}
                                    onMenuOpen={loadOnOpen}
                                    noOptionsMessage={loading ? 'Loading...' : 'No options'}
                                  />
                                </div>
                              );
                            }

                            Controlled

                            Preview
                            Dark
                              Selected: In progress
                              import { useState } from 'react';
                              import Select from '@/components/ui/Select';
                              
                              type Option = { value: string; label: string };
                              
                              const statuses: Option[] = [
                                { value: 'todo', label: 'To do' },
                                { value: 'in-progress', label: 'In progress' },
                                { value: 'review', label: 'In review' },
                                { value: 'done', label: 'Done' },
                              ];
                              
                              export default function ControlledDemo() {
                                const [status, setStatus] = useState<Option>(statuses[1]);
                              
                                return (
                                  <div className="flex w-full max-w-sm flex-col gap-4">
                                    <Select
                                      placeholder="Select a status"
                                      value={status}
                                      onChange={setStatus}
                                      options={statuses}
                                    />
                                    <div className="text-sm text-muted-foreground">
                                      Selected:{' '}
                                      <span className="font-semibold text-foreground">{status.label}</span>
                                    </div>
                                  </div>
                                );
                              }

                              API

                              Select ships two controls from one import: Select for a single choice and Select.Multi for several. Both take the same options shape — a flat array of { label, value } items, or an array of { label, options } groups — and share the search, loading, sizing, and validation props. Select.ValueWithPrefix and Select.OptionWithPrefix are helpers for the custom render props.

                              Select

                              PropDescriptionTypeDefault
                              optionsSelectable options: a flat list, or groups of options.Options<T>[]
                              valueSelected option in controlled mode.SingleOption<T> | null-
                              defaultValueInitial selected option when uncontrolled.SingleOption<T> | null-
                              onChangeFires with the newly selected option.(value: SingleOption<T>) => void-
                              placeholderText shown when nothing is selected.string-
                              isSearchableShow a filter input inside the menu.booleanfalse
                              isCreatableLet the filter text become a new option.booleanfalse
                              isDisabledDisable the whole control.booleanfalse
                              isLoadingShow a spinner in the control.booleanfalse
                              invalidApply the invalid style; also inherited from an invalid Form.Field.booleanfalse
                              sizeControl size; falls back to InputGroup, Form, then ConfigProvider.ControlSize'md'
                              placementMenu placement relative to the control.Placement-
                              customInputDisplayRender the selected value in the control yourself.(selectedItem: SingleOption<T> | null) => ReactNode-
                              customOptionRender each option row yourself.CustomOption<T>-
                              formatGroupLabelRender a group's header label.(group: GroupOption<T>) => ReactNode-
                              filterReplace the default option filtering.FilterFunction<T>-
                              noOptionsMessageMessage shown when no options match.string | ((ListElement) => ReactNode)'No options'
                              onInputChangeFires with the filter text as the user types.(inputValue: string) => void-
                              onMenuOpenFires when the menu opens.() => void-
                              searchInputPropsExtra props for the filter input.Omit<ComponentProps<'input'>, 'onChange' | 'value' | 'onKeyDown' | 'ref'>-
                              inputIdid for the underlying input, for label association.string-
                              classNameClass names for the control element.string-
                              ...nativePropsNative div props (except defaultValue and onChange).Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'onChange'>-

                              Select.Multi

                              Select.Multi shares every Select prop above except customInputDisplay, and replaces the single-value props with array equivalents.

                              PropDescriptionTypeDefault
                              valueSelected options in controlled mode.SelectedOptions<T>-
                              defaultValueInitial selected options when uncontrolled.SelectedOptions<T>-
                              onChangeFires with the full array of selected options.(value: SelectedOptions<T>) => void-
                              customLabelRender each selected tag's content yourself.(selectedItem: SingleOption<T>) => ReactNode-
                              showClearAllButtonShow the per-tag remove buttons and the clear-all control.booleantrue
                              filterReplace the default option filtering.FilterFunctionMulti<T>-

                              Select.ValueWithPrefix

                              PropDescriptionTypeDefault
                              labelThe value's main content.ReactNode-
                              prefixNode rendered before the label, such as an avatar or icon.ReactNode-
                              showPrefixWhether to render the prefix.booleantrue

                              Select.OptionWithPrefix

                              PropDescriptionTypeDefault
                              labelThe option's main content.ReactNode-
                              prefixNode rendered before the label, such as an avatar or icon.ReactNode-
                              selectedWhether this option is currently selected.booleanfalse
                              checkIconNode shown on the right when selected; pass the CheckIcon from customOption.ReactNode-

                              Types

                              type ControlSize = 'sm' | 'md' | 'lg'
                               
                              // A single option. Extra keys of T (e.g. an avatar or color) are kept
                              // on the option and passed back to the custom render props.
                              type SingleOption<T> = {
                                label: string
                                value: any
                                disabled?: boolean
                              } & T
                               
                              // A labelled group of single options.
                              type GroupOption<T> = {
                                label: string
                                options: SingleOption<T>[]
                              }
                               
                              // options accepts a flat list or a list of groups.
                              type Options<T> = SingleOption<T>[] | GroupOption<T>[]
                               
                              // Select.Multi value / defaultValue.
                              type SelectedOptions<T> = SingleOption<T>[]
                               
                              type CustomOption<T> = (props: {
                                option: SingleOption<T>
                                hovered: boolean
                                selected: boolean
                                CheckIcon: ReactNode
                              }) => ReactNode
                               
                              type FilterFunction<T> = (props: {
                                inputValue: string
                                options: Options<T>
                                selectedItem?: SingleOption<T> | null
                              }) => Options<T>
                               
                              type FilterFunctionMulti<T> = (props: {
                                inputValue: string
                                options: Options<T>
                                selectedItems: SingleOption<T>[]
                              }) => Options<T>
                               
                              // Placement is re-exported from @floating-ui/react:
                              // 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start'
                              //   | 'bottom-end' | 'left' | 'left-start' | 'left-end'
                              //   | 'right' | 'right-start' | 'right-end'