Skip to content

Import#

import { Field } from '@dnb/eufemia/extensions/forms'
render(<Field.MultiSelection />)

Description#

Field.MultiSelection is a component for selecting multiple items from a fixed set with a confirmation flow. The selection interface appears in a popover overlay, keeping your form layout clean.

There is a corresponding Value.MultiSelection component.

This is ideal when users need to:

  1. Browse and filter through many options.
  2. See their selections clearly before confirming.
  3. Cancel and discard changes without applying them.
  4. Work with structured data beyond simple strings.
import { Field } from '@dnb/eufemia/extensions/forms'
const data = [
{
value: 'option1',
title: 'Option 1',
text: 'Text',
description: 'Description',
},
{ value: 'option2', title: 'Option 2', text: 'Text' },
{ value: 'option3', title: 'Option 3', description: 'Description' },
]
render(
<Field.MultiSelection
label="Select items"
data={data}
showSearchField
showSelectedTags
/>
)

Using with dataPath#

You can also use the dataPath property to provide the data from the Form context:

import { Field, Form } from '@dnb/eufemia/extensions/forms'
render(
<Form.Handler
data={{
myItems: [
{ value: 'oslo', title: 'Oslo', text: 'Capital of Norway' },
{
value: 'stockholm',
title: 'Stockholm',
text: 'Capital of Sweden',
},
{
value: 'copenhagen',
title: 'Copenhagen',
text: 'Capital of Denmark',
},
],
}}
>
<Field.MultiSelection
label="Select cities"
dataPath="/myItems"
showSearchField
showSelectedTags
/>
</Form.Handler>
)

Features#

Popover Interface#

The selection interface appears in a popover overlay, keeping your form clean and maintaining focus on the current field. Click the trigger button to open the selection interface, showing the search field, items list, and action buttons.

Selected Items Display#

Shows currently selected items as removable tags when showSelectedTags is enabled.

Collapsible Selected Items (selectedItemsCollapsibleThreshold)#

When showSelectedTags is enabled and the total number of available items exceeds selectedItemsCollapsibleThreshold (default: 20), the selected items are hidden by default and a collapsible header appears with two controls:

  • Toggle button: Shows the count of selected items relative to the total (e.g. "22 of 30 selected") with a rotating chevron icon. Announces its state to screen readers via aria-expanded. Click to expand or collapse the tag list.
  • Clear all button: A tertiary button with a close icon that deselects all items at once. Hidden when nothing is selected.

The tag list expands and collapses with a smooth height animation.

Set openOnFind to let browser find-in-page expand matching selected tags. This uses HeightAnimation; see its accessibility notes for behavior details and browser support.

Set selectedItemsCollapsibleThreshold to a custom number to control when the collapsible header kicks in:

<Field.MultiSelection
showSelectedTags
selectedItemsCollapsibleThreshold={10}
{/* ... */}
/>

Search Functionality#

When showSearchField is enabled, users can filter through items by typing in the search field. The filtering applies to both the item title and text and description.

Scrollable Inline Item List#

Use maxHeight with the inline variant to constrain only the options list. The selected tags and search field remain visible while the options scroll. The property accepts any CSS max-height value, such as maxHeight="28rem". Numbers are interpreted as rem.

Item Details#

Each item can include optional text and description content below the main title. text renders as black primary lines, while description renders as grey secondary lines.

Disabled Items#

Individual items can be disabled by setting their disabled property to true.

Confirmation Flow#

When showConfirmButton is enabled, users must click "Confirm selection" to apply changes, or "Cancel" to discard them without modifying the form value.

When more than 20 enabled options are available, keyboard users are offered a SkipContent action after the optional search field. Activating it skips the option checkboxes and moves focus directly to the confirm button. Filtering the list hides the skip action once 20 or fewer enabled options remain.

Accessibility#

Keyboard interaction#

KeyBehavior
Enter / SpaceOpens or closes the popover when the trigger button is focused. Toggles an item when a checkbox is focused.
ArrowDownWhen the trigger is focused: opens the popover and moves focus to the first checkbox. When inside the popover: moves focus to the next checkbox.
ArrowUpWhen the trigger is focused: opens the popover and moves focus to the last checkbox. When inside the popover: moves focus to the previous checkbox.
EscapeCloses the popover and discards any pending changes (same as Cancel).
TabMoves focus through the interactive elements inside the popover. With confirmation and more than 20 enabled options, a skip action can move focus directly to Confirm.

Screen reader announcements#

  • The trigger button exposes the current selection count via aria-describedby. Screen readers will read e.g. "2 of 5 selected" when the button receives focus.
  • After each checkbox toggle (or tag removal), a live region (aria-live="assertive") announces the updated count, e.g. "3 of 5 selected". This announcement does not fire when the popover is closed via Cancel, since changes are discarded.
  • The trigger button uses role="combobox" with aria-haspopup="listbox" so assistive technologies describe it as a combobox that opens a list.
  • Each checkbox announces its own checked/unchecked state natively. No duplicate descriptions are added.
  • Nested item groups are rendered as sibling <ul> elements after their parent <li>, so assistive technologies announce them as a new list level (e.g. "list, 3 items, level 2").

Focus management#

  • Opening the popover with the mouse moves focus to the popover content area so arrow-key navigation works immediately.
  • Opening the popover with ArrowDown moves focus directly to the first checkbox; ArrowUp moves focus to the last checkbox.
  • During keyboard navigation inside the popover, the focused item is automatically scrolled into view.
  • Closing the popover (confirm, cancel, or Escape) returns focus to the trigger button.

Semantic structure#

  • The item list is rendered as a <ul> with each option as an <li>, giving assistive technologies correct list semantics.
  • Nested item groups use a child <ul> inside the parent <li>.
  • The field label is linked to the trigger button via a <label for="…"> relationship provided by FieldBlock.

Relevant links#

Demos#

Basic#

const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
    description: 'Capital of Norway',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
    description: 'Capital of Sweden',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
    description: 'Capital of Denmark',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
    description: 'Capital of Finland',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
    description: 'Capital of Iceland',
  },
]
render(<Field.MultiSelection label="Select cities" data={cities} />)

With Select all#

This example uses field width medium.

const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
    text: 'Capital of Norway',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
    text: 'Capital of Sweden',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
    text: 'Capital of Denmark',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
    text: 'Capital of Finland',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
    text: 'Capital of Iceland',
  },
]
render(
  <Field.MultiSelection
    label="Select cities"
    data={cities}
    showSelectAll
    width="medium"
  />
)

With search#

render(
  <Field.MultiSelection
    label="Select fruits"
    data={fruits}
    showSearchField
  />
)

With confirm and cancel#

const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
    text: 'Capital of Norway',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
    text: 'Capital of Sweden',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
    text: 'Capital of Denmark',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
    text: 'Capital of Finland',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
    text: 'Capital of Iceland',
  },
]
render(
  <Field.MultiSelection
    label="Select cities"
    data={cities}
    showSearchField
    showSelectAll
    showConfirmButton
  />
)

With nested items#

<Field.MultiSelection
  label="Select regions"
  showSelectAll
  data={[
    {
      value: 'scandinavia',
      title: 'Scandinavia',
      children: [
        {
          value: 'oslo',
          title: 'Oslo',
          text: 'Capital of Norway',
        },
        {
          value: 'stockholm',
          title: 'Stockholm',
          text: 'Capital of Sweden',
        },
        {
          value: 'copenhagen',
          title: 'Copenhagen',
          text: 'Capital of Denmark',
        },
      ],
    },
    {
      value: 'nordic',
      title: 'Other Nordic',
      children: [
        {
          value: 'helsinki',
          title: 'Helsinki',
          text: 'Capital of Finland',
        },
        {
          value: 'reykjavik',
          title: 'Reykjavik',
          text: 'Capital of Iceland',
        },
      ],
    },
  ]}
/>

With selected item tags#

const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
    text: 'Capital of Norway',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
    text: 'Capital of Sweden',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
    text: 'Capital of Denmark',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
    text: 'Capital of Finland',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
    text: 'Capital of Iceland',
  },
]
render(
  <Field.MultiSelection
    label="Select cities"
    data={cities}
    value={['stockholm']}
    showSelectedTags
  />
)

With many selected items#

When showSelectedTags is enabled and the number of items exceeds the selectedItemsCollapsibleThreshold (default: 10), an accordion appears to collapse/expand the selected items. A "Clear all" button also appears to the right for quickly deselecting all items.

const items = Array.from(
  {
    length: 30,
  },
  (_, i) => ({
    value: `item${i + 1}`,
    title: `Item ${i + 1}`,
    description: `Description for item ${i + 1}`,
  })
)
render(
  <Field.MultiSelection
    label="Select items"
    data={items}
    value={items.slice(0, 25).map((item) => item.value)}
    showSelectedTags
    showSelectAll
    showSearchField
  />
)

With disabled items#

<Field.MultiSelection
  label="Select available languages"
  data={[
    {
      value: 'nodejs',
      title: 'Node.js',
      text: 'JavaScript runtime',
    },
    {
      value: 'python',
      title: 'Python',
      text: 'General purpose language',
      disabled: true,
    },
    {
      value: 'rust',
      title: 'Rust',
      text: 'Systems programming language',
    },
    {
      value: 'golang',
      title: 'Go',
      text: 'Compiled language',
      disabled: true,
    },
    {
      value: 'java',
      title: 'Java',
      text: 'Enterprise language',
    },
  ]}
/>

With search options#

Use the search prop to configure search behavior. This example uses numbers: true so account numbers can be searched by typing digits.

const accounts = [
  {
    value: 'acc1',
    title: 'Brukskonto',
    text: '2000 12 34567',
  },
  {
    value: 'acc2',
    title: 'Sparekonto',
    text: '2223 33 44425',
  },
  {
    value: 'acc3',
    title: 'BSU',
    text: '1234 56 78901',
  },
  {
    value: 'acc4',
    title: 'Felleskonto',
    text: '9876 54 32100',
  },
]
render(
  <Field.MultiSelection
    label="Select accounts"
    data={accounts}
    showSearchField
    search={{
      numbers: true,
    }}
  />
)

Inline variant#

The inline variant renders the item list inline without a popover. This is useful when you want the options to be always visible.

Select cities
const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
  },
]
render(
  <Field.MultiSelection
    label="Select cities"
    variant="inline"
    value={['stockholm']}
    data={cities}
    showSelectAll
    showSearchField
    showSelectedTags
  />
)

With a scrollable item list#

Set maxHeight to constrain the inline item list. Only the options scroll, while selected tags and the search field remain visible.

Select fruits
  • Red fruit
  • Yellow fruit
  • Orange fruit
  • Small green fruit
  • Red berry
  • Small blue fruit
  • Red berry
  • Dark berry
  • Large red fruit
  • Tropical fruit
  • Spiky tropical
  • Orange tropical
  • Green fruit
  • Fuzzy fruit
  • Purple fruit
  • Small red fruit
  • Yellow citrus
  • Green citrus
  • Large citrus
  • Green creamy
render(
  <Field.MultiSelection
    label="Select fruits"
    variant="inline"
    data={fruits}
    value={['apple', 'banana']}
    maxHeight="16rem"
    selectedItemsCollapsibleThreshold={20}
    showSearchField
    showSelectAll
    showSelectedTags
  />
)

Using dataPath from Form context#

<Form.Handler
  data={{
    colors: [
      {
        value: 'red',
        title: 'Red',
        text: 'Primary color',
      },
      {
        value: 'green',
        title: 'Green',
        text: 'Secondary color',
      },
      {
        value: 'blue',
        title: 'Blue',
        text: 'Tertiary color',
      },
      {
        value: 'yellow',
        title: 'Yellow',
        text: 'Accent color',
      },
    ],
  }}
>
  <Field.MultiSelection
    label="Select colors"
    dataPath="/colors"
    value={['red']}
    help={{
      title: 'Help text',
      content: 'Additional information about this field',
    }}
  />
</Form.Handler>

Disabled field#

const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
    text: 'Capital of Norway',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
    text: 'Capital of Sweden',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
    text: 'Capital of Denmark',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
    text: 'Capital of Finland',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
    text: 'Capital of Iceland',
  },
]
render(
  <Field.MultiSelection label="Disabled field" disabled data={cities} />
)

With minimum required items#

const cities = [
  {
    value: 'oslo',
    title: 'Oslo',
    text: 'Capital of Norway',
  },
  {
    value: 'stockholm',
    title: 'Stockholm',
    text: 'Capital of Sweden',
  },
  {
    value: 'copenhagen',
    title: 'Copenhagen',
    text: 'Capital of Denmark',
  },
  {
    value: 'helsinki',
    title: 'Helsinki',
    text: 'Capital of Finland',
  },
  {
    value: 'reykjavik',
    title: 'Reykjavik',
    text: 'Capital of Iceland',
  },
]
render(
  <Form.Handler>
    <Form.Card>
      <Field.MultiSelection
        label="Select cities"
        path="/cities"
        required
        minItems={2}
        data={cities}
        showSearchField
        showSelectedTags
      />
      <Form.SubmitButton />
    </Form.Card>
  </Form.Handler>
)

Large data sets#

Virtualization is opt-in. Install the optional peer dependency:

yarn add @tanstack/react-virtual

Import the driver separately so the default Forms bundle does not include the virtualization dependency:

import { Field } from '@dnb/eufemia/extensions/forms'
import { createMultiSelectionVirtualization } from '@dnb/eufemia/extensions/forms/Field/MultiSelection/Virtualization'
const listDriver = createMultiSelectionVirtualization()
<Field.MultiSelection data={items} listDriver={listDriver} />
<Field.MultiSelection
  label="Select from 10,000 items"
  data={virtualizedItems}
  listDriver={virtualizedMultiSelection}
  showSearchField
  showSelectAll
  showConfirmButton
/>
Suggest an edit