Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelog App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip
ARC UI ARC Radiant Components
v3.2 Docs Components Tokens Synthesizer
Getting StartedFrameworksServer Rendering Design TokensThemingTheme SynthesizerTypographyUtilities All ComponentsAccessibilityBrowser SupportChangelogContributingStats App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip

Transfer List

Dual-listbox for moving items between an available and a selected pane, ideal for permissions and settings UIs.

Components Transfer List
input interactive beta
<arc-transfer-list>

Overview

TransferList presents the full universe of options split across two panes — everything not yet chosen on the left ("Available") and the current value on the right ("Selected"). Users mark items with a checkbox-style highlight, then move them across with the center controls, or move a single item instantly with a double-click or the Enter key. Pane titles are customisable via `sourceLabel` and `targetLabel`, and each pane header shows a live "checked of total" count. Options are supplied as an array of `{ value, label, disabled? }` objects and the component's `value` is the array of values currently in the Selected pane, kept in options order. With the `searchable` flag each pane gains its own case-insensitive filter input that narrows only that pane, and the move-all buttons respect the active filter. Disabled options render dimmed and can never be moved. The component is form-associated: give it a `name` and it submits one form entry per selected value, participates in `form.reset()`, and honours `<fieldset disabled>`. Both listboxes follow the WAI-ARIA multi-select listbox pattern — one tab stop each with a roving tabindex, arrow-key navigation, Space to toggle, and Ctrl+A to check every visible item — and moves are announced through a polite live region.

Guidelines

When to use

  • Use for medium-sized sets (roughly 5-100 items) where users assign a subset, such as role permissions or report columns
  • Enable `searchable` whenever a pane can hold more than a dozen items
  • Keep option labels short — one line each — so they do not truncate in narrow panes
  • Override `sourceLabel`/`targetLabel` with domain terms ("All permissions" / "Granted") for clearer context
  • Listen to `arc-change` to persist the selection; the detail carries the full value array after every move

When not to use

  • Do not use for a handful of options — a checkbox group or multi-select is lighter
  • Do not use for thousands of items without server-side narrowing; all options render in the panes
  • Do not repurpose the checked highlight as the selection itself — only items in the right pane are the value
  • Do not disable options without conveying elsewhere why they cannot be moved
  • Avoid placing two transfer lists side by side; each already spans two panes and needs the width

Features

  • Two labeled panes with live "checked of total" counts and customisable titles
  • Checkbox-style multi-highlight: mark any number of items, then transfer them in one action
  • Center controls to move checked items or all (filtered) items in either direction, auto-disabled when inapplicable
  • Double-click or Enter moves a single item across instantly
  • Optional per-pane case-insensitive filtering via the `searchable` flag
  • Full APG listbox keyboard support: roving tabindex, ArrowUp/Down, Home/End, Space to check, Ctrl+A to check all visible items
  • Focus stays in the same pane on the nearest remaining item after a move; moves are announced via a polite live region
  • Form-associated: submits one entry per selected value under `name` and supports form reset
  • Disabled options render dimmed and are excluded from every move operation
  • Responsive: panes stack vertically and controls rotate horizontal below ~560px container width

Preview

Usage

This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.

<arc-transfer-list
  id="permissions"
  name="permissions"
  searchable
  source-label="Available"
  target-label="Granted"
></arc-transfer-list>

<script>
  const tl = document.getElementById('permissions');
  tl.options = [
    { value: 'read', label: 'Read content' },
    { value: 'write', label: 'Write content' },
    { value: 'admin', label: 'Administer', disabled: true },
  ];
  tl.value = ['read'];
  tl.addEventListener('arc-change', (e) => {
    console.log(e.detail.value);
  });
</script>
import { TransferList } from '@arclux/arc-ui-react';

const options = [
  { value: 'read', label: 'Read content' },
  { value: 'write', label: 'Write content' },
  { value: 'admin', label: 'Administer', disabled: true },
];

<TransferList
  options={options}
  value={['read']}
  searchable
  sourceLabel="Available"
  targetLabel="Granted"
  onArcChange={(e) => console.log(e.detail.value)}
/>
<script setup>
import { TransferList } from '@arclux/arc-ui-vue';

const options = [
  { value: 'read', label: 'Read content' },
  { value: 'write', label: 'Write content' },
];
</script>

<template>
  <TransferList
    :options="options"
    :value="['read']"
    searchable
    source-label="Available"
    target-label="Granted"
    @arc-change="(e) => console.log(e.detail.value)"
  />
</template>
<script>
  import { TransferList } from '@arclux/arc-ui-svelte';

  const options = [
    { value: 'read', label: 'Read content' },
    { value: 'write', label: 'Write content' },
  ];
</script>

<TransferList
  {options}
  value={['read']}
  searchable
  source-label="Available"
  target-label="Granted"
  on:arc-change={(e) => console.log(e.detail.value)}
/>
import { Component } from '@angular/core';
import { TransferList } from '@arclux/arc-ui-angular';

@Component({
  imports: [TransferList],
  template: `
    <arc-transfer-list
      [options]="options"
      [value]="['read']"
      searchable
      sourceLabel="Available"
      targetLabel="Granted"
      (arcChange)="onChange($event)"
    ></arc-transfer-list>
  `,
})
export class PermissionsComponent {
  options = [
    { value: 'read', label: 'Read content' },
    { value: 'write', label: 'Write content' },
  ];

  onChange(e: CustomEvent) {
    console.log(e.detail.value);
  }
}

API

size 'sm' | 'md' | 'lg' 'md'
Control size. `md` is the default; `sm` and `lg` scale the row height and list panels.
options Array<{value:string,label:string,disabled?:boolean}> []
The full universe of items. Items whose value is in `value` render in the Selected pane; the rest render in Available.
value string[] []
Values currently in the Selected pane, kept in options order. Updated after every move and emitted via `arc-change`.
name string ''
Form field name. When set, the component submits one form entry per selected value.
disabled boolean false
Disables the whole control, preventing interaction and reducing opacity.
searchable boolean false
Adds a filter input to each pane that narrows that pane only, case-insensitively. Move-all respects the filter.
source-label string 'Available'
Heading for the left (available) pane. Attribute: `source-label`.
target-label string 'Selected'
Heading for the right (selected) pane. Attribute: `target-label`.
readonly boolean false
Prevents moving items between panes while the lists stay focusable and filterable; the selected values still submit with the form.
formAssociated boolean true
properties object { required: { type: Boolean, reflect: true }, readonly: { type: Boolean, reflect: true }, }
Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. `required` participates in constraint validation below; `readonly` reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
autoValidates boolean true
Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
form
validity
validationMessage
required boolean false

Events

arc-change detail: { value: string[] }
Fired after every move with `{ value }` -- the current array of selected values.

See Also