|Kit

MultiSelect

Pick several values, shown as removable pills in a wrapping, searchable input — the whole family working together (Combobox + PillsInput + Pill).

MultiSelect lets the user pick several values, rendered as removable Pills in a wrapping PillsInput-style field, searchable by default. It's the whole family in one component: the Combobox engine (in multiple mode) drives selection, the control is the pills shell, and each value is a Pill.

import { MultiSelect } from '@42/ui-react/multi-select';

<MultiSelect
  data={['Libft', 'ft_printf', 'Get Next Line', 'Minishell']}
  defaultValue={['Libft']}
  placeholder="Pick projects"
/>

Playground

Component

Loading preview

Props

Code

<Component  placeholder="Pick a project"  clearSectionMode="replace"  size="md"/>

Adding & removing

Type to filter, pick to add — a check marks selected options. Remove a value with its pill's ×, Backspace on the empty field, or the clear button (clearable). data accepts the same shapes as Select, including groups.

Hide picked options

With hidePickedOptions, chosen values drop out of the dropdown so the list only ever shows what's still addable.

Born2berootCPP Modules

Max values

maxValues caps the selection — once reached, adding more is rejected (removals still work).

Born2berootCPP Modules

Not searchable

searchable={false} turns it into a click-to-open multi-picker — the field no longer filters, it just toggles the dropdown.

paris

Max lines

maxLines caps the pills at N rows; any beyond collapse into a static +N counter (it's measured, so it adapts to the field's width). The hidden values stay selected and submitted — deselect them from the dropdown.

Born2beroot

Start & end slots

startSlot and endSlot place content at the field's inline-start / inline-end with the same slot layout as Input. endSlot renders before the built-in clear button and chevron.

Libft

Code

import { Search } from 'lucide-react';<MultiSelectdata={['Libft', 'ft_printf', 'Get Next Line', 'Minishell']}defaultValue={['Libft']}startSlot={<Search />}placeholder="Pick projects"/>

Custom rendering

renderItem owns each dropdown option's content — the same (item, state) => ReactNode renderer as Select, with item being { value, label, disabled } and state { selected, highlighted }. The pills in the field still render from the label; only the dropdown rows change. Define the function in a "use client" module so it survives the server → client boundary.

Born2beroot

Typed payload

Attach arbitrary data to an item via payload and read it back as item.payload in renderItem — inferred from data, and undefined for bare-string or payload-less items. Only the dropdown rows see it; the pills still render the label.

'use client';
import { Badge } from '@42/ui-react/badge';

const projects = [
  { value: 'libft', label: 'Libft', payload: { xp: 420 } },
  { value: 'webserv', label: 'Webserv', payload: { xp: 1340 } },
];

// item.payload is inferred as { xp: number } | undefined
const renderOption = (item) => (
  <span className="flex flex-1 items-center justify-between gap-2">
    {item.label}
    {item.payload && <Badge>{item.payload.xp} XP</Badge>}
  </span>
);

<MultiSelect data={projects} renderItem={renderOption} />

Controlled

value is a string[]; onChange hands you the full selection on every add or remove.

'use client';
import { useState } from 'react';
import { MultiSelect } from '@42/ui-react/multi-select';

export function ProjectPicker() {
  const [value, setValue] = useState<string[]>([]);
  return (
    <MultiSelect
      data={['Libft', 'ft_printf', 'Get Next Line', 'Minishell']}
      value={value}
      onChange={setValue}
      clearable
    />
  );
}

Label, description & error

Built-in via Field, like the rest of the family:

Libft
Pick all that apply

Code

<MultiSelectlabel="Projects"description="Pick all that apply"data={['Libft', 'ft_printf', 'Get Next Line']}defaultValue={['Libft']}/>

API

Prop

Type

Accessibility

  • Full combobox keyboard model from Ark: type to filter, / to move, Enter to toggle an option, Backspace on the empty field to remove the last value, Esc to close.
  • Each pill's × is an independently focusable, labelled button; role="combobox" / listbox / option and the active-descendant wiring come from the Ark machine, with label / description / error associated via the built-in Field.
  • RTL: the pills, field, and dropdown all mirror under dir="rtl".

On this page