SelectList displays a list of actions or options using the browser’s native select.

also known as Picklist, Picker

Figma:

Responsive:

Adaptive:

Props

Component props
Name
Type
Default
children
Required
React.Node
-

One or more SelectList.Option components, which may be grouped using SelectList.Group.

id
Required
string
-

A unique identifier to connect the underlying <select> with the associated label.

onChange
Required
(arg1: { event: React.ChangeEvent<HTMLSelectElement>; value: string }) => void
-

/**
Callback triggered when the user selects a new option. See the controlled component variant to learn more.

dataTestId
string
-

Available for testing purposes, if needed. Consider better queries before using this prop.

disabled
boolean
false

Used to disable the entire SelectList.

errorMessage
string
-

Used to communicate error information to the user. Be sure to localize the text. See the error message variant to learn more.

helperText
string
-

Used to provide more information about the form field. Be sure to localize the text. See the helper text variant to learn more.

label
string
-

The label shown above the input. Be sure to localize the label.

labelDisplay
"visible" | "hidden"
"visible"

Whether the legend should be visible or not. If hidden, the legend is still available for screen reader users, but does not appear visually. See the label visibility variant for more info.

name
string
-

Used to specify the name of the control.

onBlur
(arg1: { event: React.FocusEvent<HTMLSelectElement>; value: string }) => void
-

Callback triggered when the user blurs the input.

onFocus
(arg1: { event: React.FocusEvent<HTMLSelectElement>; value: string }) => void
-

Callback triggered when the user focuses the input.

placeholder
string
-

If not provided, the first item in the list will be shown. Be sure to localize the text. See the controlled component variant to learn more.

size
"md" | "lg"
"md"

md: 40px, lg: 48px. See the size variant to learn more.

value
string | null | undefined
-

The currently-selected value. See the controlled component variant to learn more.

Usage guidelines

When to use
  • When presenting users with a list of options that utilizes the native select functionality of the browser or device.
  • When presenting users with a list of options to choose from, like display settings.
When not to use
  • When more than 10 options are presented and the ability to filter the list would be beneficial. Use ComboBox instead.
  • When extra functionality, like groups, subtext or badges, is needed. Use Dropdown instead.
  • When the options are links and navigate users to different places. Use Dropdown instead.

Best practices

Do

Use SelectList when the user needs to select from a simple list of items.

Don't

Use SelectList when additional functionality such as subtext or images are needed. Use Dropdown instead.

Do

Order the list items in SelectList either alphabetically or by usage.

Don't

Use SelectList if there are fewer than 4 items in the list and there is space to display all options. Use RadioGroup instead.

Do

Keep the same type of selection for a group of items. An example of this might be a filter bar. If some items could use SelectList and some items need to use Dropdown, use Dropdown for all the items in the group.

Don't

Mix Dropdown and SelectList in a group of items.

Accessibility

Labels

SelectList comes with Label built-in: just use the label prop. We strongly encourage always supplying a label. Be sure to provide a unique id so the Label is associated with the correct SelectList.

If SelectList is labeled by content elsewhere on the page, or a more complex label is needed, the labelDisplay prop can be used to visually hide the label. In this case, it is still available to screen reader users, but will not appear visually on the screen.

import { useState } from 'react';
import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  const [range, setRange] = useState('');

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          id="selectlist-visible-label"
          label="Date range"
          onChange={({ value }) => setRange(value)}
          placeholder="Select a country"
          size="md"
          value={range}
        >
          {[
            { label: 'Last 5 days', value: '5' },
            { label: 'Last week', value: '7' },
            { label: 'Last 30 days', value: '30' },
            { label: 'Last sixth months', value: '6m' },
            { label: 'Last year', value: '365' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          id="selectlist-visible-label"
          label="Date range"
          onChange={({ value }) => setRange(value)}
          placeholder="Select a country"
          size="lg"
          value={range}
        >
          {[
            { label: 'Last 5 days', value: '5' },
            { label: 'Last week', value: '7' },
            { label: 'Last 30 days', value: '30' },
            { label: 'Last sixth months', value: '6m' },
            { label: 'Last year', value: '365' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Subcomponents

SelectList.Option

Use SelectList.Option to define the available options within SelectList.

SelectList.Option Props

SelectList.Option subcomponent props
Name
Type
Default
label
Required
string
-

The visible label for the option. Don't forget to localize!

value
Required
string
-

The underlying value of the option.

disabled
boolean
-

Used to disable the option.

SelectList.Group

Use SelectList.Group to group a subset of the options within SelectList.

SelectList.Group Props

SelectList.Group subcomponent props
Name
Type
Default
children
Required
React.Node
-

One or more SelectList.Option components.

label
Required
string
-

The label for the group. Don't forget to localize!

disabled
boolean
-

Used to disable the entire group of options.

Variants

Controlled component

SelectList must be used as a controlled component when the placeholder or value props are needed. When used in this manner, onChange and value are required, while placeholder is optional.

import { useState } from 'react';
import { Box, SelectList } from 'gestalt';

export default function Example() {
  const [country, setCountry] = useState('');
  return (
    <Box padding={8} width="100%">
      <SelectList
        id="selectlist-controlled"
        label="Country"
        name="country"
        onChange={({ value }) => setCountry(value)}
        placeholder="Select a country"
        value={country}
      >
        {[
          { label: 'Algeria', value: 'algeria' },
          { label: 'Belgium', value: 'belgium' },
          { label: 'Canada', value: 'canada' },
          { label: 'Denmark', value: 'denmark' },
          { label: 'Egypt', value: 'egypt' },
          { label: 'France', value: 'france' },
        ].map(({ label, value }) => (
          <SelectList.Option key={label} label={label} value={value} />
        ))}
      </SelectList>
    </Box>
  );
}

Size

Use lg as the recommended size within Pinterest products.
Use md on denser surfaces, such as business products or internal tools.

import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          id="selectlistMd"
          label="Country"
          onChange={() => {}}
          size="md"
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          id="selectlistLg"
          label="Country"
          onChange={() => {}}
          size="lg"
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

State

  1. Enabled
    The enabled state of Textfield that represents it can be interacted with.

  2. Disabled
    TextFields cannot be interacted with using the mouse or keyboard. They also do not need to meet contrast requirements, so do not use them to present info to the user (use "readOnly" instead).

  3. Error
    TextField can display an error message. Simply pass in an errorMessage when there is an error present and TextField will handle the rest. Don't use errorMessage to provide feedback on character count errors. See the maximum length variant for more details.x

Enabled
import { useState } from 'react';
import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  const [country, setCountry] = useState('');
  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          id="selectlist-enabled"
          label="Country"
          name="country"
          onChange={({ value }) => setCountry(value)}
          placeholder="Select a country"
          size="md"
          value={country}
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          id="selectlist-enabled"
          label="Country"
          name="country"
          onChange={({ value }) => setCountry(value)}
          placeholder="Select a country"
          size="lg"
          value={country}
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Disabled
import { useState } from 'react';
import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  const [country, setCountry] = useState('');
  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          disabled
          id="selectlist-disabled"
          label="Country"
          name="country"
          onChange={({ value }) => setCountry(value)}
          placeholder="Select a country"
          size="md"
          value={country}
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          disabled
          id="selectlist-disabled"
          label="Country"
          name="country"
          onChange={({ value }) => setCountry(value)}
          placeholder="Select a country"
          size="lg"
          value={country}
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Error
import { useState } from 'react';
import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  const [country, setCountry] = useState('');
  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          errorMessage="You must select a country"
          id="selectlist-error"
          label="Country"
          name="country"
          onChange={({ value }) => setCountry(value)}
          placeholder="Select a country"
          size="md"
          value={country}
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          errorMessage="You must select a country"
          id="selectlist-error"
          label="Country"
          name="country"
          onChange={({ value }) => setCountry(value)}
          placeholder="Select a country"
          size="lg"
          value={country}
        >
          {[
            { label: 'Algeria', value: 'algeria' },
            { label: 'Belgium', value: 'belgium' },
            { label: 'Canada', value: 'canada' },
            { label: 'Denmark', value: 'denmark' },
            { label: 'Egypt', value: 'egypt' },
            { label: 'France', value: 'france' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Label

'label' is an optional prop; however, TextField should always be properly labelled. Learn about accessibility best practices regarding labels.

  1. Built-in label. Preferred. Consistent Textfield design and tested accessibility.

In some cases, the label for a TextField is represented in a different way visually, as demonstrated below. We can take 2 approaches in this case.

  1. Labelled Textfield (Label + Textfield). This is the best approach when a custom label is needed. The label focuses the Textfield when pressed.

  2. Hidden built-in label (Label + Textfield). This is the best approach when there's significant visual distance between the label and the input. You can set labelDisplay="hidden" to ensure TextField is properly labeled for screen readers while using a different element to represent the label visually. The 'visual' label doesn't focus the Textfield when pressed.

Built-in label
import { useState } from 'react';
import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  const [range, setRange] = useState('');

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          id="selectlist-visible-label"
          label="Date range"
          onChange={({ value }) => setRange(value)}
          placeholder="Select a country"
          size="md"
          value={range}
        >
          {[
            { label: 'Last 5 days', value: '5' },
            { label: 'Last week', value: '7' },
            { label: 'Last 30 days', value: '30' },
            { label: 'Last sixth months', value: '6m' },
            { label: 'Last year', value: '365' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          id="selectlist-visible-label"
          label="Date range"
          onChange={({ value }) => setRange(value)}
          placeholder="Select a country"
          size="lg"
          value={range}
        >
          {[
            { label: 'Last 5 days', value: '5' },
            { label: 'Last week', value: '7' },
            { label: 'Last 30 days', value: '30' },
            { label: 'Last sixth months', value: '6m' },
            { label: 'Last year', value: '365' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Label + Textfield
import { Box, Flex, IconButton, Label, SelectList, Text } from 'gestalt';

export default function Example() {
  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <Flex direction="column" gap={2}>
          <Label htmlFor="selectList-labelled">
            <Flex alignItems="center" gap={1}>
              <Text size="300">Date range</Text>
              <IconButton
                accessibilityLabel="Info"
                icon="info-circle"
                size="sm"
                tooltip={{
                  text: 'Options available are based on your usage.',
                  idealDirection: 'right',
                }}
              />
            </Flex>
          </Label>
          <SelectList
            id="selectList-labelled"
            label="Date range"
            labelDisplay="hidden"
            onChange={() => {}}
            size="md"
          >
            {[
              { label: 'Last 5 days', value: '5' },
              { label: 'Last week', value: '7' },
              { label: 'Last 30 days', value: '30' },
              { label: 'Last sixth months', value: '6m' },
              { label: 'Last year', value: '365' },
            ].map(({ label, value }) => (
              <SelectList.Option key={label} label={label} value={value} />
            ))}
          </SelectList>
        </Flex>
        <Flex direction="column" gap={2}>
          <Label htmlFor="selectList-labelled">
            <Flex alignItems="center" gap={1}>
              <Text size="300">Date range</Text>
              <IconButton
                accessibilityLabel="Info"
                icon="info-circle"
                size="sm"
                tooltip={{
                  text: 'Options available are based on your usage.',
                  idealDirection: 'right',
                }}
              />
            </Flex>
          </Label>
          <SelectList
            id="selectList-labelled"
            label="Date range"
            labelDisplay="hidden"
            onChange={() => {}}
            size="lg"
          >
            {[
              { label: 'Last 5 days', value: '5' },
              { label: 'Last week', value: '7' },
              { label: 'Last 30 days', value: '30' },
              { label: 'Last sixth months', value: '6m' },
              { label: 'Last year', value: '365' },
            ].map(({ label, value }) => (
              <SelectList.Option key={label} label={label} value={value} />
            ))}
          </SelectList>
        </Flex>
      </Flex>
    </Box>
  );
}

Hidden label
import { useState } from 'react';
import { BannerSlim, Box, Flex, SelectList, Text } from 'gestalt';

export default function Example() {
  const [range, setRange] = useState('');

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6}>
        <Text size="400" weight="bold">
          Select a Pin release date range
        </Text>
        <BannerSlim
          iconAccessibilityLabel="Recommendation"
          message="Pin launches work better on weekends."
          type="recommendationBare"
        />
        <SelectList
          id="selectlist-hidden-label"
          label="Date range"
          labelDisplay="hidden"
          onChange={({ value }) => setRange(value)}
          placeholder="Select a country"
          value={range}
        >
          {[
            { label: 'Last 5 days', value: '5' },
            { label: 'Last week', value: '7' },
            { label: 'Last 30 days', value: '30' },
            { label: 'Last sixth months', value: '6m' },
            { label: 'Last year', value: '365' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Helper text

Helper text should be used when additional description may be required to understand the SelectList. Common examples include text that is legally required to be displayed, or instructions to fill out a form (e.g. proper formatting). If the text is optional, Tooltip could be used instead.

import { Box, Flex, SelectList } from 'gestalt';

export default function Example() {
  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <SelectList
          helperText="Product prices in your data source without an ISO currency code will default to this currency"
          id="selectlistHelperText"
          label="Default currency"
          onChange={() => {}}
          size="md"
        >
          {[
            { label: 'ARS - Argentine peso', value: 'ars' },
            { label: 'AUD - Australian dollar', value: 'aud' },
            { label: 'ERN - Eritrean nakfa', value: 'ern' },
            { label: 'EUR - Euro', value: 'eur' },
            { label: 'GBP - British pound', value: 'gbp' },
            { label: 'JPY - Japanese yen', value: 'jpy' },
            { label: 'USD - United States Dollar', value: 'usd' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
        <SelectList
          helperText="Product prices in your data source without an ISO currency code will default to this currency"
          id="selectlistHelperText"
          label="Default currency"
          onChange={() => {}}
          size="lg"
        >
          {[
            { label: 'ARS - Argentine peso', value: 'ars' },
            { label: 'AUD - Australian dollar', value: 'aud' },
            { label: 'ERN - Eritrean nakfa', value: 'ern' },
            { label: 'EUR - Euro', value: 'eur' },
            { label: 'GBP - British pound', value: 'gbp' },
            { label: 'JPY - Japanese yen', value: 'jpy' },
            { label: 'USD - United States Dollar', value: 'usd' },
          ].map(({ label, value }) => (
            <SelectList.Option key={label} label={label} value={value} />
          ))}
        </SelectList>
      </Flex>
    </Box>
  );
}

Grouping

SelectList.Group can be used to group related options. Note that disabling a group disables all of its options.

import { Box, SelectList } from 'gestalt';

export default function Example() {
  return (
    <Box padding={8} width="100%">
      <SelectList
        helperText="Note that the family members aren't secondary!"
        id="selectlistGrouping"
        label="Choose your favorite secondary character"
        onChange={() => {}}
        placeholder="Select a character"
      >
        <SelectList.Group disabled label="Family">
          {['Bart', 'Lisa', 'Homer', 'Marge', 'Maggie'].map((name) => (
            <SelectList.Option key={name} label={name} value={name} />
          ))}
        </SelectList.Group>
        <SelectList.Group label="Neighbors">
          {['Ned', 'Maude', 'Rod', 'Todd'].map((name) => (
            <SelectList.Option key={name} label={name} value={name} />
          ))}
        </SelectList.Group>
        <SelectList.Group label="Cartoons">
          {['Itchy', 'Scratchy', 'Poochie'].map((name) => (
            <SelectList.Option key={name} label={name} value={name} />
          ))}
        </SelectList.Group>
      </SelectList>
    </Box>
  );
}

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

Dropdown
If additional functionality is needed in the menu, such as subtext, headers or custom styling, use Dropdown.

ComboBox
If users need the ability to choose an option by entering text to filter a long list of options, use ComboBox.

RadioGroup
If users need the ability to choose between fewer than 4 options, use RadioButton.

Checkbox
If users need the ability to choose between a yes/no option, use Checkbox.