caliper uidocs

Transfer list

Free

Choose a subset from a long list: tick items and move them across, and they slide into the other side from where they came.

Installs components/ui/transfer-list.tsxDepends on @radix-ui/react-slot, lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/transfer-list.json

Columns6

In the export3

When to use #

Use it for

  • Permissions, columns to show, fields to export.
  • Building a list in a chosen order.

Skip it when

  • A few options; use checkboxes or a multi-select.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/transfer-list.json

Adds its 6 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { TransferList } from "@/components/ui/transfer-list"

const fields = [
  { id: "date", label: "Date", description: "Invoice date" },
  { id: "number", label: "Number", description: "INV-2041" },
  { id: "customer", label: "Customer", description: "Company name" },
  { id: "net", label: "Net amount", description: "Before tax" },
  { id: "tax", label: "Tax", description: "VAT or sales tax" },
  { id: "total", label: "Total", description: "Net plus tax" },
  { id: "currency", label: "Currency", description: "ISO code" },
  { id: "status", label: "Status", description: "Paid, due, overdue" },
  { id: "owner", label: "Owner", description: "Account owner" },
];

export function TransferListDemo() {
  const [chosen, setChosen] = useState(["date", "number", "total"]);
  return <TransferList className="w-full max-w-2xl" items={fields} chosen={chosen} onChosenChange={setChosen} availableLabel="Columns" chosenLabel="In the export" searchLabel="Search columns" />;
}

API reference #

TransferList

component
PropTypeDefaultDescription
items *readonly TransferItem[]—
chosen *readonly string[]—Ids on the chosen side, in order.
onChosenChange *(ids: string[]) => void—
availableLabelstring"Available"
chosenLabelstring"Chosen"
searchLabelstring"Search available"
classNamestring—

Accessibility #

It exposes group and status semantics, with aria-labelledby, aria-label and aria-hidden kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.