Dropdown

Present a list of options to the user to select from.

Props

name
string
Identifier for the dropdown. Should be unique.
ariaLabel
string
Defines how the selected value will be translated for the screen reader. If not specified it will fall back to the name.
ariaLabelledBy
string
The aria-labelledby attribute identifies the element(or elements) that labels the dropdown it is applied to. Normally it is the id of the label.
value
string | undefined
Stores the value of the item selected from the dropdown.
filterable
boolean
When true the dropdown will have the ability to filter options by typing into the input field.
Defaults to false.
leadingIcon
GoAIconType
Show an icon to the left of the dropdown option.
maxHeight
string
Maximum height of the dropdown menu items popover. Non-native only.
Defaults to 276px.
placeholder
string
The text displayed for the dropdown before a selection is made. Non-native only.
width
string
Overrides the autosized menu width. Non-native only.
maxWidth
string
Sets the maximum width of the dropdown. Use a CSS unit (px, %, ch, rem, em).
disabled
boolean
Disable this control.
Defaults to false.
error
boolean
Show an error state.
Defaults to false.
multiselect
boolean
When true, allows multiple items to be selected.
Defaults to false.
native
boolean
When true will render the native select HTML element.
Defaults to false.
size
default | compact
Sets the size of the dropdown. Compact reduces height for dense layouts.
Defaults to default.
version
1 | 2
The design system version for styling purposes.
Defaults to 1.
relative
string
This property has no effect and will be removed in a future version.
autoComplete
string
Specifies the autocomplete attribute for the dropdown input. Native only.
testId
string
Sets a data-testid attribute for automated testing.
disableGlobalClosePopover
boolean
Prevents the popover from closing when clicking outside. Used for nested dropdowns or complex interactions.
Defaults to false.
mt, mr, mb, ml
none | 3xs | 2xs | xs | s | m | l | xl | 2xl | 3xl | 4xl
Apply margin to the top, right, bottom, and/or left of the component.

Events

onChange
(event: Event) => void
_change
CustomEvent
Examples

Add another item in a modal

ReactAngularWeb Components
const [open, setOpen] = useState(false);
  const [type, setType] = useState<string>();
  const [name, setName] = useState<string>();
  const [description, setDescription] = useState<string>();
<GoabButton type="tertiary" leadingIcon="add" onClick={() => setOpen(true)}>
        Add another item
      </GoabButton>
      <GoabModal
          heading="Add a new item"
          open={open}
          actions={
            <GoabButtonGroup alignment="end">
              <GoabButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
                Cancel
              </GoabButton>
              <GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
                Save new item
              </GoabButton>
            </GoabButtonGroup>
          }
        >
          <p>Fill in the information to create a new item</p>
          <GoabFormItem label="Type" mt="l">
            <GoabDropdown onChange={(e) => setType(e.value)} value={type}>
              <GoabDropdownItem value="1" label="Option 1" />
              <GoabDropdownItem value="2" label="Option 2" />
            </GoabDropdown>
          </GoabFormItem>
          <GoabFormItem label="Name" mt="l">
            <GoabInput
              onChange={(e) => setName(e.value)}
              value={name}
              name="name"
              width="100%"
            />
          </GoabFormItem>
          <GoabFormItem label="Description" mt="l">
            <GoabTextarea
              name="description"
              rows={3}
              width="100%"
              onChange={(e) => setDescription(e.value)}
              value={description}
            />
          </GoabFormItem>
      </GoabModal>

Add a record using a drawer

ReactAngularWeb Components
const [open, setOpen] = useState(false);
<GoabButton leadingIcon="add" onClick={() => setOpen(true)}>
        Add Record
      </GoabButton>
      <GoabDrawer
        maxSize="492px"
        open={open}
        heading="Add Record"
        position="right"
        onClose={() => setOpen(false)}
        actions={
          <GoabButtonGroup>
            <GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
              Add record
            </GoabButton>
            <GoabButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
              Cancel
            </GoabButton>
          </GoabButtonGroup>
        }
      >
        <GoabFormItem label="Level of education">
          <GoabDropdown onChange={() => {}} name="education" value="university">
            <GoabDropdownItem value="high-school" label="High School Diploma" />
            <GoabDropdownItem value="college" label="College Diploma" />
            <GoabDropdownItem value="university" label="University Degree" />
            <GoabDropdownItem value="masters" label="Master's Degree" />
            <GoabDropdownItem value="doctorate" label="Doctorate" />
          </GoabDropdown>
        </GoabFormItem>
        <GoabFormItem label="Educational institution" mt="l">
          <GoabInput name="education" type="text" onChange={() => {}} />
        </GoabFormItem>
        <GoabFormItem label="Field of study" requirement="optional" mt="l">
          <GoabInput name="fieldOfStudy" type="text" onChange={() => {}} />
        </GoabFormItem>
        <GoabFormItem label="Is the person currently attending?" mt="l">
          <GoabRadioGroup name="attendTraining" orientation="horizontal" onChange={() => {}}>
            <GoabRadioItem value="yes" label="Yes" />
            <GoabRadioItem value="no" label="No" />
          </GoabRadioGroup>
        </GoabFormItem>
        <GoabFormItem label="Start date" mt="l">
          <GoabDatePicker onChange={() => {}} value={new Date("2022-09-01")} />
          <GoabCheckbox name="startDateApproximate" text="Approximate date" value="y" mt="s" />
        </GoabFormItem>
        <GoabFormItem label="Credential received?" mt="l">
          <GoabRadioGroup name="credentialReceived" orientation="horizontal" onChange={() => {}}>
            <GoabRadioItem value="yes" label="Yes" />
            <GoabRadioItem value="no" label="No" />
          </GoabRadioGroup>
        </GoabFormItem>
      </GoabDrawer>

Add and edit lots of filters

ReactAngularWeb Components
const [open, setOpen] = useState(false);
<GoabButton onClick={() => setOpen(true)}>Filters</GoabButton>
      <GoabDrawer
          heading="Filters"
          open={open}
          onClose={() => setOpen(false)}
          position="right"
          actions={
            <GoabButtonGroup>
              <GoabButton type="primary" size="compact" onClick={() => setOpen(false)}>
                Apply filters
              </GoabButton>
              <GoabButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
                Cancel
              </GoabButton>
            </GoabButtonGroup>
          }
        >
          <GoabFormItem label="Entry status">
            <GoabCheckboxList name="entryStatus" onChange={() => {}}>
              <GoabCheckbox name="draft" text="Draft" value="draft" />
              <GoabCheckbox name="published" text="Published" value="published" />
            </GoabCheckboxList>
          </GoabFormItem>
          <GoabFormItem label="Assigned to - Region" mt="l">
            <GoabCheckboxList name="region" onChange={() => {}}>
              <GoabCheckbox name="calgary" text="Calgary" value="calgary" />
              <GoabCheckbox name="central" text="Central" value="central" />
              <GoabCheckbox name="edmonton" text="Edmonton" value="edmonton" />
              <GoabCheckbox name="north" text="North" value="north" />
              <GoabCheckbox name="south" text="South" value="south" />
            </GoabCheckboxList>
          </GoabFormItem>
          <GoabFormItem label="Assigned to" mt="l">
            <GoabDropdown name="assignedTo" onChange={() => {}}>
              <GoabDropdownItem value="1" label="Person 1" />
              <GoabDropdownItem value="2" label="Person 2" />
            </GoabDropdown>
          </GoabFormItem>
          <GoabFormItem label="Date taken" mt="l">
            <GoabRadioGroup name="dateTaken" onChange={() => {}}>
              <GoabRadioItem value="24" label="Last 24 hours" />
              <GoabRadioItem value="72" label="Last 72 hours" />
            </GoabRadioGroup>
          </GoabFormItem>
      </GoabDrawer>

Ask a user for an address

ReactAngularWeb Components
const [address, setAddress] = useState("");
  const [suite, setSuite] = useState("");
  const [city, setCity] = useState("");
  const [province, setProvince] = useState("");
  const [postalCode, setPostalCode] = useState("");
<GoabText size="heading-l" mt="none" mb="xl">What is your address?</GoabText>
      <GoabFormItem label="Street Address">
        <GoabInput
          name="address"
          type="text"
          value={address}
          onChange={(e) => setAddress(e.value)}
          width="100%"
        />
      </GoabFormItem>
      <GoabFormItem label="Suite or unit #" mt="l">
        <GoabInput
          name="suite"
          type="text"
          value={suite}
          onChange={(e) => setSuite(e.value)}
          width="100%"
        />
      </GoabFormItem>
      <GoabFormItem label="City or town" mt="l">
        <GoabInput
          name="city"
          type="text"
          value={city}
          onChange={(e) => setCity(e.value)}
          width="100%"
        />
      </GoabFormItem>
      <GoabBlock direction="row" gap="l" mt="l">
        <GoabFormItem label="Province or territory">
          <GoabDropdown
            onChange={(e) => setProvince(e.value ?? "")}
            name="province"
            value={province}
          >
            <GoabDropdownItem label="Alberta" value="AB" />
            <GoabDropdownItem label="British Columbia" value="BC" />
            <GoabDropdownItem label="Manitoba" value="MB" />
            <GoabDropdownItem label="New Brunswick" value="NB" />
            <GoabDropdownItem label="Newfoundland and Labrador" value="NL" />
            <GoabDropdownItem label="Northwest Territories" value="NT" />
            <GoabDropdownItem label="Nova Scotia" value="NS" />
            <GoabDropdownItem label="Nunavut" value="NU" />
            <GoabDropdownItem label="Ontario" value="ON" />
            <GoabDropdownItem label="Prince Edward Island" value="PE" />
            <GoabDropdownItem label="Quebec" value="QC" />
            <GoabDropdownItem label="Saskatchewan" value="SK" />
            <GoabDropdownItem label="Yukon" value="YT" />
          </GoabDropdown>
        </GoabFormItem>
        <GoabFormItem label="Postal Code">
          <GoabInput
            name="postalCode"
            type="text"
            value={postalCode}
            onChange={(e) => setPostalCode(e.value)}
            width="7ch"
          />
        </GoabFormItem>
      </GoabBlock>
      <GoabButtonGroup alignment="start" mt="2xl">
        <GoabButton type="primary" onClick={() => {}}>
          Save and continue
        </GoabButton>
        <GoabButton type="secondary" onClick={() => {}}>
          Cancel
        </GoabButton>
      </GoabButtonGroup>

Dynamically add an item to a dropdown list

ReactAngularWeb Components
type Task = {
  value: string;
  label: string;
  mount: GoabDropdownItemMountType;
};

const DEFAULT_TASKS: Task[] = [
    { label: "Finish Report", value: "finish-report", mount: "append" },
    { label: "Attend Meeting", value: "attend-meeting", mount: "append" },
    { label: "Reply Emails", value: "reply-emails", mount: "append" },
  ];

  const [tasks, setTasks] = useState<Task[]>(DEFAULT_TASKS);
  const [newTask, setNewTask] = useState<string>("");
  const [mountType, setMountType] = useState<string>("append");
  const [selectedTask, setSelectedTask] = useState<string>("");
  const [taskError, setTaskError] = useState<boolean>(false);
  const [isReset, setIsReset] = useState<boolean>(false);

  function onMountTypeChange(value: string | undefined) {
    setMountType(value as string);
  }

  function addTask() {
    if (newTask === "") {
      setTaskError(true);
      return;
    }
    setTaskError(false);

    const task: Task = {
      label: newTask,
      value: newTask.toLowerCase().replace(" ", "-"),
      mount: mountType as GoabDropdownItemMountType,
    };
    setTasks([...tasks, task]);
    setNewTask("");
    setIsReset(false);
  }

  function reset() {
    setTasks(DEFAULT_TASKS);
    setMountType("append");
    setNewTask("");
    setSelectedTask("");
    setTaskError(false);
    setIsReset(true);
  }
<GoabFormItem
          requirement="required"
          label="Name of item"
          error={taskError ? "Please enter item name" : undefined}
          helpText="Add an item to the dropdown list below">
          <GoabInput
            onChange={(event: GoabInputOnChangeDetail) => setNewTask(event.value)}
            name="item"
            value={newTask}
          />
        </GoabFormItem>

        <GoabFormItem mt="l" label="Add to">
          <GoabRadioGroup
            name="mountType"
            onChange={(event: GoabRadioGroupOnChangeDetail) => onMountTypeChange(event.value)}
            value={mountType}
            orientation="horizontal">
            <GoabRadioItem value="prepend" label="Start" />
            <GoabRadioItem value="append" label="End" />
          </GoabRadioGroup>
        </GoabFormItem>

        <GoabButtonGroup alignment="start" gap="relaxed" mt="l">
          <GoabButton type="primary" onClick={addTask}>
            Add new item
          </GoabButton>
          <GoabButton type="tertiary" onClick={reset}>
            Reset list
          </GoabButton>
        </GoabButtonGroup>

        <GoabDivider mt="l" />

        <GoabFormItem mt="l" label="All items">
          <div style={{ width: isReset ? "320px" : "auto" }}>
            <GoabDropdown
              key={tasks.length}
              onChange={(event: GoabDropdownOnChangeDetail) =>
                setSelectedTask(event.value as string)
              }
              value={selectedTask}
              name="selectedTask">
              {tasks.map(task => (
                <GoabDropdownItem
                  key={task.value}
                  value={task.value}
                  mountType={task.mount}
                  label={task.label}
                />
              ))}
            </GoabDropdown>
          </div>
      </GoabFormItem>

Dynamically change items in a dropdown list

ReactAngularWeb Components
const [children, setChildren] = useState<string[]>([]);
  const parents = ["All", "Big", "Small"];
  const childrenAll = ["Bus", "Elephant", "Key", "Pen", "Watch", "Truck"];
  const childrenBig = ["Elephant", "Truck", "Bus"];
  const childrenSmall = ["Key", "Pen", "Watch"];

  const loadItems = (value: string) => {
    if (value === "All") setChildren(childrenAll);
    else if (value === "Big") setChildren(childrenBig);
    else setChildren(childrenSmall);
  };

  const logSelection = () => {
    console.log("Item selected");
  };
<GoabFormItem
          label="Size"
          requirement="optional"
          helpText="Choose the size to change the list below">
          <GoabDropdown
            name="parent"
            placeholder="Select a value"
            onChange={(event: GoabDropdownOnChangeDetail) =>
              loadItems(event.value as string)
            }>
            {parents.map(parent => (
              <GoabDropdownItem key={parent} value={parent} label={parent} />
            ))}
          </GoabDropdown>
        </GoabFormItem>

        <GoabFormItem label="Items" requirement="optional" mt="xl">
          <GoabDropdown name="children" placeholder="Select a value" onChange={logSelection}>
            {children.map((child) => (
              <GoabDropdownItem
                key={crypto.randomUUID()}
                value={child}
                label={child}
                mountType="reset"
              />
            ))}
          </GoabDropdown>
      </GoabFormItem>
ReactAngularWeb Components
const [addressLine1, setAddressLine1] = useState("");
  const [addressLine2, setAddressLine2] = useState("");
  const [townCity, setTownCity] = useState("");
  const [province, setProvince] = useState("");
  const [postalCode, setPostalCode] = useState("");
<GoabLink leadingIcon="arrow-back" size="small" mb="none">
        Back
      </GoabLink>

      <GoabText as="h2" mt="xl" mb="m">Your address</GoabText>
      <GoabText mt="none" mb="xl">This is the home address of the person applying</GoabText>

      <GoabFormItem label="Address line 1">
        <GoabInput
          onChange={(event) => setAddressLine1(event.value)}
          value={addressLine1}
          name="address-line-1"
          ariaLabel="Address line 1"
          width="100%"
        />
      </GoabFormItem>

      <GoabFormItem label="Address line 2" mt="l">
        <GoabInput
          onChange={(event) => setAddressLine2(event.value)}
          value={addressLine2}
          name="address-line-2"
          ariaLabel="Address line 2"
          width="100%"
        />
      </GoabFormItem>

      <GoabFormItem label="Town or city" mt="l">
        <GoabInput
          onChange={(event) => setTownCity(event.value)}
          value={townCity}
          name="town-city"
          ariaLabel="Town or city name"
          width="460px"
        />
      </GoabFormItem>

      <GoabFormItem label="Province or territory" mt="l" id="provinceLabel">
        <GoabDropdown
          onChange={(event) => setProvince(event.value ?? "")}
          value={province}
          name="province-territory"
          ariaLabelledBy="provinceLabel"
        >
          <GoabDropdownItem value="AB" label="Alberta" />
          <GoabDropdownItem value="BC" label="British Columbia" />
          <GoabDropdownItem value="MB" label="Manitoba" />
          <GoabDropdownItem value="NB" label="New Brunswick" />
          <GoabDropdownItem value="NL" label="Newfoundland and Labrador" />
          <GoabDropdownItem value="NS" label="Nova Scotia" />
          <GoabDropdownItem value="ON" label="Ontario" />
          <GoabDropdownItem value="PE" label="Prince Edward Island" />
          <GoabDropdownItem value="QC" label="Quebec" />
          <GoabDropdownItem value="SK" label="Saskatchewan" />
          <GoabDropdownItem value="NT" label="Northwest Territories" />
          <GoabDropdownItem value="NU" label="Nunavut" />
          <GoabDropdownItem value="YT" label="Yukon" />
        </GoabDropdown>
      </GoabFormItem>

      <GoabFormItem label="Postal code" mt="l">
        <GoabInput
          onChange={(event) => setPostalCode(event.value)}
          value={postalCode}
          name="postal-code"
          width="105px"
        />
      </GoabFormItem>

      <GoabButton type="submit" mt="2xl">
        Save and continue
      </GoabButton>

Review and action

ReactAngularWeb Components
<GoabGrid minChildWidth="315px">
      <GoabContainer accent="thin" type="non-interactive">
        <GoabText size="heading-m" mt="none" mb="m">Appearance details</GoabText>
        <GoabGrid minChildWidth="200px" gap="m">
          <GoabBlock direction="column" gap="xs">
            <GoabText size="body-s" color="secondary" mt="none" mb="none">Accused name</GoabText>
            <GoabText size="body-m" mt="none" mb="none">Doe, John Scott</GoabText>
          </GoabBlock>

          <GoabBlock direction="column" gap="xs">
            <GoabText size="body-s" color="secondary" mt="none" mb="none">Date of birth</GoabText>
            <GoabText size="body-m" mt="none" mb="none">Mar 14, 2021</GoabText>
          </GoabBlock>

          <GoabBlock direction="column" gap="xs">
            <GoabText size="body-s" color="secondary" mt="none" mb="none">Court location</GoabText>
            <GoabText size="body-m" mt="none" mb="none">Calgary</GoabText>
          </GoabBlock>

          <GoabBlock direction="column" gap="xs">
            <GoabText size="body-s" color="secondary" mt="none" mb="none">Upcoming appearance date(s)</GoabText>
            <GoabText size="body-m" mt="none" mb="none">Sep 20, 2021</GoabText>
          </GoabBlock>
        </GoabGrid>

        <GoabText size="heading-xs" mt="l" mb="s">Docket number(s) &amp; charges</GoabText>
        <GoabContainer type="non-interactive" padding="compact">
          <GoabText size="heading-xs" mt="none" mb="xs">1

Show number of results per page

ReactAngularWeb Components
interface User {
  id: string;
  firstName: string;
  lastName: string;
  age: number;
}

const [users, setUsers] = useState<User[]>([]);
  const [pageUsers, setPageUsers] = useState<User[]>([]);
  const [page, setPage] = useState<number>(1);
  const [perPage, setPerPage] = useState<number>(10);

  useEffect(() => {
    // Generate sample data
    const firstNames = ["Emma", "Liam", "Olivia", "Noah", "Ava", "James", "Sophia", "William", "Isabella", "Oliver", "Mia", "Benjamin", "Charlotte", "Elijah", "Amelia", "Lucas", "Harper", "Mason", "Evelyn", "Logan"];
    const lastNames = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Wilson", "Anderson", "Taylor", "Thomas", "Moore", "Jackson", "Martin", "Lee", "Thompson", "White"];
    const _users: User[] = [];
    for (let i = 1; i <= 100; i++) {
      _users.push({
        id: `user-${i}`,
        firstName: firstNames[(i - 1) % firstNames.length],
        lastName: lastNames[(i - 1) % lastNames.length],
        age: 20 + (i % 40),
      });
    }
    setUsers(_users);
    setPageUsers(_users.slice(0, perPage));
  }, [perPage]);

  function changePage(newPage: number) {
    const offset = (newPage - 1) * perPage;
    const _users = users.slice(offset, offset + perPage);
    setPage(newPage);
    setPageUsers(_users);
  }

  function handlePerPageCountChangeEvent(event: GoabDropdownOnChangeDetail) {
    const perPageValue = parseInt(event.value || "10");
    setPage(1);
    setPerPage(perPageValue);
    const _users = users.slice(0, perPageValue);
    setPageUsers(_users);
  }
<GoabTable width="100%" mb="xl">
        <thead>
          <tr>
            <th>First name</th>
            <th>Last name</th>
            <th>Age</th>
          </tr>
        </thead>
        <tbody>
          {pageUsers.map((u) => (
            <tr key={u.id}>
              <td>{u.firstName}</td>
              <td>{u.lastName}</td>
              <td>{u.age}</td>
            </tr>
          ))}
        </tbody>
      </GoabTable>

      <GoabBlock alignment="center" width="100%">
        <GoabBlock mb="m" alignment="center">
          Show
          <GoabDropdown
            onChange={handlePerPageCountChangeEvent}
            value={perPage.toString()}
            width="9ch"
          >
            <GoabDropdownItem value="10" label="10" />
            <GoabDropdownItem value="20" label="20" />
            <GoabDropdownItem value="30" label="30" />
          </GoabDropdown>
          <span style={{ width: "75px" }}>per page</span>
        </GoabBlock>
        <GoabSpacer hSpacing="fill" />
        <GoabPagination
          itemCount={users.length}
          perPageCount={perPage}
          pageNumber={page}
          onChange={(event) => changePage(event.page)}
        />
      </GoabBlock>

States

Submit

When you must disable a button or input:

  • Provide nearby text explaining what needs to happen first
  • Consider showing the element enabled with validation on submit instead
  • Use aria-describedby to link the disabled element to explanatory text
Don't disable buttons or inputs without explaining why. Disabled controls can be confusing and users may not understand why they can't interact with an element.

Sizing

Define dropdown widths based on the widest dropdown in the form.
Use the default width for dropdowns. The dropdown automatically sets its width based on the length of the options.

Content

Don't truncate labels. Longer labels should wrap to the next line.

Placeholder text disappears when users start typing, leaving them without context for what the field is asking for.

Always use a visible label above or beside the input field. Placeholder text can provide an example of the expected format, but should never be the only indication of what information is needed.

Don't use placeholder text as a label

Positioning

Don't allow the dropdown menu to hide below the viewport.

Other

The form item automatically associates the label with the input for screen readers, ensuring your form is accessible.

Use a form item wrapper on all inputs to add a label, helper text, error message, and more.
All GoA Design System components are built to meet WCAG 2.2 AA standards. The following guidelines provide additional context for accessible implementation.

No accessibility-specific guidelines have been documented for this component yet.