Multiselect - National Design System

A form field for picking multiple options from grouped lists, with selections shown as removable chips and committed on Apply.

Grouped Options

Checkboxes live inside labeled fieldsets. Changes stage in a draft set and only commit when the user clicks Apply, so rapid toggling never churns the outer state.

Multi-option select
Select options…

Built-in Features

Auto-initialization

Any .nds-multiselect on the page wires up on load. The inner dropmenu boots itself so the trigger, panel, and keyboard flow work without extra JS.

Draft and Apply Commit

Checkbox toggles stage into a draft set and only commit when the user clicks Apply. Closing the panel without applying discards the draft, so rapid exploration never churns the chips or change events.

Removable Chips

Applied values render as chips inside the form-control. Clicking a chip drops the value and fires a change event immediately, no need to reopen the panel.

Form-Ready Submission

Applied values ship as <input type="hidden" name="field[]"> entries inside the form-control, so wrapping the field in a <form> posts the selection as an array without extra wiring.

Full Keyboard Navigation

Arrow keys walk the checkboxes, Space toggles, Enter triggers Apply, and Escape closes the panel and returns focus to the trigger. A visible focus ring follows each option.

Programmatic Control

Every field exposes an instance on the DOM node with apply(), reset(), and removeValue(). Listen for nds:multiselect:change to react to committed selections.

Usage Guidelines

Best Practices

  • Use multiselect for form fields where users pick several values from a list of ten or more options and benefit from seeing the applied set as chips they can remove individually
  • Group related options inside <fieldset> elements with a <legend>. Groups make long lists scannable and let assistive technology announce the category
  • For a short, flat list (three to six options) without a category axis, reach for a plain Checkbox Group instead. Multiselect's overhead (dropmenu trigger, Apply button, chip track) adds friction when the options fit inline
  • For a single-value choice, use Radio Button or the select-mode of Dropmenu. Multiselect's draft/apply pattern is wasted on single-pick fields
  • Set data-multiselect-name when the field lives inside a <form>. Without it, no hidden inputs are rendered and the selection does not post
  • Set a meaningful data-label on each checkbox so chip text stays readable when the surrounding label changes, wraps, or contains extra markup
  • Keep the placeholder short (two to four words). It shares the row with chips once any are applied, so a long placeholder fights for space
  • Add a <hr class="nds-divider"> between fieldsets in the panel to make the grouping visually clear, matching the provided markup contract
  • When the number of selections carries meaning (quota, pricing tier), listen for nds:multiselect:change and show a count or validation hint outside the field rather than overloading the placeholder

Data Attributes

AttributeDescription
data-multiselect-nameSet on .nds-multiselect. Name used for hidden form inputs, posted as name[]. Omit to opt out of form submission
data-chip-classSet on .nds-multiselect. Classes applied to generated chips. Defaults to nds-primary nds-sm
data-multiselect-dropmenuMarks the wrapping .nds-form-action.nds-prefix.nds-dropmenu as the host of the options panel
data-multiselect-chipsMarks the container that receives rendered chips. Required inside .nds-form-control
data-multiselect-actionSet on footer buttons. Values: apply commits the draft and fires nds:multiselect:change; reset clears the draft without touching the applied set
data-labelSet on each checkbox input. Overrides the visible label text when rendering chips, useful when the visible label contains extra markup or context
data-no-auto-closeSet on the checkbox fieldset and the Reset button so the dropmenu stays open while the user toggles options or clears the draft

JavaScript API

The NDS.Multiselect namespace initializes all .nds-multiselect fields on load. Each instance lives on its DOM node as element.ndsMultiselect and exposes methods for programmatic control. Listen for nds:multiselect:change to react to committed selections.

// ── Initialize (auto-runs on load) ───────────────────── // Call again after injecting multiselect HTML dynamically. NDS.Multiselect.init(); // ── Create an instance manually ──────────────────────── // Returns the NDSMultiselect instance. Useful for markup // added after the initial page load. const field = document.querySelector('.nds-multiselect'); NDS.Multiselect.create(field); // ── Access the instance on a live field ──────────────── // Every auto-initialized root stores its instance here. const instance = field.ndsMultiselect; // ── Read the applied selection ───────────────────────── instance.applied; // ['ai', 'cloud'] // committed values instance.draft; // ['ai', 'cloud'] // in-panel staging set // ── Commit, clear, or remove programmatically ────────── instance.apply(); // promote draft → applied, emit change instance.reset(); // clear draft only (applied untouched) instance.removeValue('cloud'); // drop value from both sets, emit change // ── Listen for committed selection changes ───────────── // Fires on apply() and on chip removal, never on draft toggles. field.addEventListener('nds:multiselect:change', (e) => { const { name, values, labels } = e.detail; // name: form field name (from data-multiselect-name) // values: array of applied checkbox values // labels: array of display labels in the same order });
Was this page useful?
60% of users said Yes from 2843 Feedbacks