Use cases
- Assigning a subset of N items into a bucket — roles ↔ permissions, designations ↔ job categories, modules ↔ a role template.
- The user needs to see both pools side by side and shuttle items between them.
When NOT to use
- For a flat multi-select with bulk actions → use CheckboxGrid.
- For a tiny fixed list → use multiple Switch rows.
- For a single binary choice → use Checkbox.
Platform support
Web: side-by-side dual panes with four move buttons between them.
Native: vertically stacked Available/Assigned sections, each with a FlatList and filter input. Items toggle between panes via tap. Bulk "Assign All ↓" / "Remove All ↑" buttons sit between the sections.
Anatomy
┌──────────────────┬─────┬──────────────────┐
│ Available │ › │ Assigned │
│ Showing 15 of 18 │ » │ 3 items │
├──────────────────┼─────┼──────────────────┤
│ 🔍 Filter… │ ‹ │ 🔍 Filter… │
├──────────────────┼─────┼──────────────────┤
│ ☐ Engineer │ « │ ☐ Senior Eng │
│ ☐ Product │ │ ☐ Designer │
│ ☐ Marketing │ │ ☐ Researcher │
│ ☐ Sales │ │ │
│ … │ │ │
└──────────────────┴─────┴──────────────────┘
↑ ↑ ↑
left pane move buttons right pane
Move buttons
| Glyph | Action |
|---|---|
› | Move selected left → right |
» | Move all left → right |
‹ | Move selected right → left |
« | Move all right → left |
Each button disables when its action would be a no-op (no left selection → › and » disabled, no right items → « disabled, etc.).
Controlled state
You control only the assignment — per-side multi-select is internal to the component:
const [assigned, setAssigned] = useState<Record<string, boolean>>({});
<TransferList
items={ALL_DESIGNATIONS}
assigned={assigned}
onAssignedChange={setAssigned}
/>This is intentional: per-side selection is transient UI state that doesn't belong in your form model.
Labels
The pane titles default to "Available" / "Assigned". Customise via leftLabel / rightLabel to match the domain:
<TransferList
items={…}
assigned={…}
onAssignedChange={…}
leftLabel="Available designations"
rightLabel="Linked to this role"
/>Responsive and custom rendering
layout="responsive"stacks panes on small screens and switches to a horizontal shuttle atmd.layout="vertical"keeps the panes stacked for narrow drawers and mobile-first workflows. Move controls stay horizontal, but their glyphs switch to down/up arrows to match the vertical flow.actionsPlacement="top" | "between" | "bottom"controls where move buttons appear.allowMoveAll={false}removes bulk movement when the workflow requires deliberate assignment.renderItemsupports rich rows with descriptions, badges, or metadata. Keep the returned content text-readable because each row remains a button withrole="option".
Composition
- Each pane has a built-in search field. For long lists (100+ items), the search is the primary navigation.
- Use
leftLabel/rightLabelto match your domain ("Available designations" / "Linked to this role"). assignedis aSet<string>— the component never mutates it.- Pair with SectionHeader above the transfer list for page context.
Related
- CheckboxGrid — simpler multi-select without dual-pane semantics.
- Select — single selection from a dropdown.
- DataTable — tabular data with row selection.