• English
  • Select

    Choose one or several options from a dropdown list.

    Examples

    Use options with unique nonempty string values. onChange returns the value and option; clearing returns an empty string and undefined. Arrow keys highlight options, Enter selects, Escape closes and Tab moves on.

    Live previewBasic selection
    basic.tsx
    import { Select } from '@sudden3/leaf-ui';
    
    const options = [
      { label: 'Design studio', value: 'design' },
      { label: 'Product team', value: 'product' },
      { label: 'Engineering team', value: 'engineering' },
      { label: 'Archived project', value: 'archived', disabled: true },
    ];
    
    export function SelectBasic() {
      return (
        <div className="leaf-demo-stack">
          <Select aria-label="Choose a team" options={options} placeholder="Select a team" />
          <Select aria-label="Default team" options={options} defaultValue="design" />
          <Select aria-label="Disabled team" options={options} disabled defaultValue="product" />
        </div>
      );
    }
    
    Live previewControlled value and validation
    Current:Not selected
    controlled.tsx
    import { Select } from '@sudden3/leaf-ui';
    import { useState } from 'react';
    
    export function SelectControlled() {
      const [value, setValue] = useState('');
      return (
        <div className="leaf-demo-stack">
          <Select
            aria-label="Project visibility"
            value={value}
            placeholder="Select visibility"
            allowClear
            onChange={setValue}
            options={[
              { label: 'Public', value: 'public' },
              { label: 'Team', value: 'team' },
              { label: 'Private', value: 'private' },
            ]}
          />
          <span className="leaf-demo-note">Current:{value || 'Not selected'}</span>
          <Select
            aria-label="Team required"
            status="error"
            placeholder="Select a team"
            options={[{ label: 'Design team', value: 'design' }]}
          />
        </div>
      );
    }
    
    Live previewInteractive example
    Open the list to see its purple theme and border radius.
    theme.tsx
    import { ConfigProvider, type LeafTheme, Select } from '@sudden3/leaf-ui';
    
    const theme: LeafTheme = {
      primaryColor: '#7654c6',
      borderRadius: 6,
    };
    
    export function SelectTheme() {
      return (
        <ConfigProvider className="leaf-demo-stack" theme={theme}>
          <Select
            aria-label="Themed team"
            allowClear
            defaultValue="design"
            options={[
              { value: 'design', label: 'Design studio' },
              { value: 'product', label: 'Product team' },
            ]}
          />
          <span className="leaf-demo-note">
            Open the list to see its purple theme and border radius.
          </span>
        </ConfigProvider>
      );
    }
    
    Live previewControl sizes
    sm
    md
    lg
    control-sizes.tsx
    import { Button, type ControlSize, Input, Select } from '@sudden3/leaf-ui';
    
    export function ControlSizes() {
      return (
        <div className="leaf-demo-stack leaf-demo-stack--wide">
          {(['sm', 'md', 'lg'] as const).map((size: ControlSize) => (
            <div key={size} className="leaf-demo-control-row">
              <span className="leaf-demo-size">{size}</span>
              <Input size={size} aria-label={`${size} Search`} placeholder="Search projects" />
              <Select
                size={size}
                aria-label={`${size} Status`}
                defaultValue="all"
                options={[
                  { label: 'All statuses', value: 'all' },
                  { label: 'In progress', value: 'active' },
                ]}
              />
              <Button size={size}>Search</Button>
            </div>
          ))}
        </div>
      );
    }
    

    Search and multiple selection

    showSearch filters existing options; multiple accepts an array and keeps the menu open while choosing.

    Live previewSearch and multiple selection
    Design
    design
    search.tsx
    import { Select, type SelectOption } from '@sudden3/leaf-ui';
    import { useState } from 'react';
    
    const options: SelectOption[] = [
      { value: 'design', label: 'Design' },
      { value: 'engineering', label: 'Engineering' },
      { value: 'marketing', label: 'Marketing' },
    ];
    export function SelectSearch() {
      const [teams, setTeams] = useState<string[]>(['design']);
      return (
        <div className="leaf-demo-stack">
          <Select
            aria-label="Search teams"
            showSearch
            allowClear
            options={options}
            placeholder="Type to search"
          />
          <Select
            aria-label="Choose several teams"
            multiple
            showSearch
            allowClear
            options={options}
            value={teams}
            onChange={setTeams}
          />
          <output className="leaf-demo-note">{teams.join(', ') || '—'}</output>
        </div>
      );
    }
    
    Live previewPopup width and content
    Leaf Studio+1
    popup.tsx
    import { Select, type SelectOption } from '@sudden3/leaf-ui';
    
    const options: SelectOption[] = [
      { value: 'garden', label: 'Leaf Garden Design System' },
      { value: 'studio', label: 'Leaf Studio' },
      { value: 'notes', label: 'Leaf Notes' },
    ];
    export function SelectPopup() {
      return (
        <div className="leaf-demo-stack">
          <Select
            aria-label="Automatic popup width"
            style={{ width: 180 }}
            options={options}
            defaultValue="studio"
          />
          <Select
            aria-label="Fixed popup width"
            popupWidth={300}
            popupMaxWidth={360}
            style={{ width: 180 }}
            options={options}
            optionRender={(option) => (
              <div>
                <strong>{option.label}</strong>
                <div style={{ fontSize: 12, opacity: 0.65 }}>{option.value}</div>
              </div>
            )}
          />
          <Select
            aria-label="Selection limit"
            multiple
            options={options}
            defaultValue={['studio', 'notes']}
            maxCount={2}
            maxTagCount={1}
            allowClear
          />
          <Select aria-label="Loading options" options={[]} loading placeholder="Loading" />
          <Select aria-label="Empty options" options={[]} notFoundContent="No projects yet" />
        </div>
      );
    }
    

    Select API

    PropertyTypeDefaultDescription
    size'sm' | 'md' | 'lg''md'Control size
    status'error' | 'warning'—Validation appearance
    optionsreadonly SelectOption[]RequiredOptions
    placeholderstringLocale defaultPlaceholder when empty; localized by ConfigProvider
    value / defaultValuestring | readonly string[]''Controlled value / initial uncontrolled value
    onChange(value: string, option?: SelectOption) => void or (values: string[], options: SelectOption[]) => void—Returns value and option; clearing returns an empty string and undefined
    multiplebooleanfalseUse arrays for multiple values; default []
    showSearchbooleanfalseAllow typing to filter options
    filterOptionboolean | ((query: string, option: SelectOption) => boolean)trueCustom filtering; false supports external filtering
    onSearch(query: string) => void—Search text changes
    allowClearbooleanfalseShow a clear button
    disabledbooleanfalseDisable the control
    name / formstring—Field name / associated form ID
    requiredbooleanfalseRequire a selection
    onOpenChange(open: boolean) => void—Option panel open-state changes
    refRef<HTMLInputElement>—DOM reference
    popupWidthnumber | string'auto'Popup width: auto fits content, trigger matches the field, or use pixels / a CSS length
    popupMaxWidthnumber | string420Maximum width, also limited by the viewport
    popupClassName / popupStylestring / CSSProperties—Popup styling
    loadingbooleanfalseLoading indicator
    notFoundContentReactNode—Content when no options match
    optionRender(option: SelectOption) => ReactNode—Custom option content
    maxCountnumber—Maximum selections; selected items remain removable
    maxTagCountnumber—Number of visible tags; the rest show as a count

    SelectOption

    PropertyTypeDefaultDescription
    valuestringRequiredUnique option value; empty strings are reserved
    searchLabelstring—Search text for node labels; falls back to value
    labelReactNodeRequiredOption content; a string or React node
    disabledbooleanfalseDisable the option

    Types

    Import SelectProps to describe component props. Option types in the API table link to their detailed fields above.

    import type { SelectProps, SelectOption } from '@sudden3/leaf-ui';

    Forms and localization

    Use name to include values in FormData. Uncontrolled fields follow form reset; reset controlled values in application state. Place controls in FormField for labels, help and errors. ConfigProvider supplies regional themes and translated built-in UI. Application labels and options are your responsibility.

    In multiple mode, clearing returns []. FormData submits repeated values under the same name; read them with formData.getAll(name). Empty-query Backspace removes the last enabled tag. Leaving clears the query; searches never create values. For node labels, supply searchLabel. The ref and other input attributes target the input.

    The field keeps its layout width. The popup is at least as wide as the field and grows to fit its options, up to popupMaxWidth. Use popupWidth="trigger" for a matching width or set popupWidth={300} for a fixed width. Content wraps only after reaching its width limit.