Two lists and the movement between them, for choosing a subset from a large pool. Both sides are searchable — filtering the target is what makes a 200-item selection usable.
src/components/ui/transfer.tsx
About
From the component's own source, where it was written.
Two lists and the movement between them: choose a subset from a large pool.
**The reason this exists rather than a multi-select** is that it shows the chosen set as a *list* instead of a row of chips. Once a selection passes roughly a dozen items, chips wrap into a wall you cannot scan, cannot search and cannot sort — and the question "did I already add Maria?" stops being answerable at a glance. Permissions, mailing lists and column pickers are all this shape.
**Both sides are searchable independently.** Filtering the source is obvious; filtering the target is what makes a 200-item selection usable, and it is the half most implementations leave out.
Each side is a listbox with aria-multiselectable, and moving items is a button — not a double-click only, which is undiscoverable and impossible on a keyboard. Selection state is per-side and clears after a move, because the items are no longer where you left them.
Install
npx astralyx-ui add transfer
Usage
import { Transfer } from '@/components/ui/transfer'
<Transfer items={items} value={selected} onChange={setSelected} />