Transfer List
A transfer list is a dual list box for moving items between two lists. It groups a source list, optional action buttons, and a target list into a single labeled region.
Use it for permission assignment, attendee selection, tag picking, or any workflow where users move items from a candidate list to a chosen list.
Example
Show demo markup
<div class="transfer-list" aria-label="a dual list box for moving items between two lists">TransferList</div>
Usage example
<script lang="ts">
import TransferList from './TransferList.svelte';
</script>
<TransferList label="Roles" sourceLabel="Available" targetLabel="Selected">
{#snippet source()}
<ul role="listbox" aria-label="Available">…</ul>
{/snippet}
{#snippet actions()}
<button type="button" aria-label="Move right">→</button>
<button type="button" aria-label="Move left">←</button>
{/snippet}
{#snippet target()}
<ul role="listbox" aria-label="Selected">…</ul>
{/snippet}
</TransferList>
Show Svelte source
// In your Svelte component:
import TransferList from "lily-design-system-svelte-headless/components/TransferList/TransferList.svelte";
<TransferList>
<!-- list children -->
</TransferList>
Usage
<TransferList
label="Assign roles"
sourceLabel="Available roles"
targetLabel="Selected roles"
>
<template #source>
<ul role="listbox">...</ul>
</template>
<template #actions>
<button type="button" aria-label="Move to selected">→</button>
<button type="button" aria-label="Move to available">←</button>
</template>
<template #target>
<ul role="listbox">...</ul>
</template>
</TransferList>
Props
label: string (required) -- aria-label on the outer group
sourceLabel: string (required) -- aria-label for the source section
targetLabel: string (required) -- aria-label for the target section
source: slot (required) -- source list content
target: slot (required) -- target list content
actions: slot (optional) -- action buttons between the lists
...restProps: any additional HTML attributes
Keyboard Interactions
- None at the container level — interactive behavior lives on the consumer-provided listboxes and action buttons.
ARIA
role="group" on the container with aria-label from the label prop
- Each
<section> has aria-label from sourceLabel / targetLabel
- Consumer-supplied listboxes carry their own ARIA roles
When to Use
- Use to move items between two lists when both lists need to be visible at once.
- Use for assignment workflows: roles, tags, attendees, permissions.
When Not to Use
- Do not use for picking a single value — use Select or Listbox.
- Do not use when the candidate list is very long without filtering — provide a search input or use a different pattern.
Headless
Renders semantic group and section markup with no styling. Consumer provides the listboxes, the action buttons, the move logic, and the visual layout.
Styles
Consumer CSS targets transfer-list, transfer-list-source, transfer-list-target, and transfer-list-actions. Use a horizontal flex layout with the actions column between the two lists.
Testing
- Verify the component renders a
<div> element with class transfer-list
- Verify
role="group" is set
- Verify
aria-label equals the label prop
- Verify the source content renders inside
<section class="transfer-list-source"> with aria-label from sourceLabel
- Verify the target content renders inside
<section class="transfer-list-target"> with aria-label from targetLabel
- Verify the actions content renders inside
<div class="transfer-list-actions"> only when provided
Advice
- Designers: Show counts and a search field above each list when the option set is large. Use clear directional icons on the move buttons.
- Developers: Use a Listbox component in each slot for proper ARIA. Manage the source and target arrays in the consumer.
Implementation Notes
- Renders a
<div class="transfer-list" role="group"> container
label is required and sets aria-label on the group
- Source content renders inside
<section class="transfer-list-source" aria-label={sourceLabel}>
- Target content renders inside
<section class="transfer-list-target" aria-label={targetLabel}>
- Optional
actions content renders inside <div class="transfer-list-actions">
sourceLabel and targetLabel are required and set aria-label on each section
- Consumer typically supplies a Listbox in source and target slots and move buttons in actions
Related components
listbox — a list of selectable options with keyboard navigation
select — a dropdown select element for choosing one option
checkbox-group — a group component that manages a collection of checkboxes with shared state
References
- WAI-ARIA Listbox Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/listbox/
- Ant Design Transfer: https://ant.design/components/transfer
Canonical contract
Generated from this component's canonical metadata — the machine-checked source the implementations are held to.
Metadata
- Component: transfer-list
- PascalCase: TransferList
- Description: a dual list box for moving items between two lists
- Status: beta — implemented and unit-tested in all seven frameworks with an axe-clean demo page; not yet exercised in composed flows
- HTML tag: <div>
- CSS class: .transfer-list
- Interactive: yes
ARIA
- role="group" on container with aria-label from label prop
- Each <section> has aria-label from sourceLabel / targetLabel
- Consumer-supplied listboxes carry their own ARIA roles
Keyboard
- None at the container level — keyboard behavior lives on consumer-supplied listboxes and action buttons
Props
label: string (REQUIRED) sourceLabel: string (REQUIRED) targetLabel: string (REQUIRED) source: slot (REQUIRED) target: slot (REQUIRED) actions: slot (optional) ...restProps: any additional HTML attributes