‹ components

TokenSelect

draftv0.9.0·open in canvas

Searchable token picker — icon + symbol trigger, Dialog list with balances.

Installnpx shadcn add @cids/token-select
Live demo

selected · sol

Motion · tap to replay

honors reduced-motion

Trigger: `transition-[background-color,border-color,transform] duration-150` + `active:scale-[0.96]` (control press). Dialog enter/ exit via Radix `data-[state]` presets. List highlight is instant (keyboard/mouse tracking — never animate data surfaces).

Brand chip remounts on tap · highlighted tokens are named in this component's doc · spring is budgeted to human feedback only.

import { TokenSelect } from "@/design-system";

const tokens = [
  { id: "sol", symbol: "SOL", name: "Solana", balance: "12.40" },
  { id: "usdc", symbol: "USDC", name: "USD Coin", balance: "1,204.00" },
  { id: "jup", symbol: "JUP", name: "Jupiter", balance: "840.2" },
];

<TokenSelect tokens={tokens} value={token} onValueChange={setToken} />

Tokens · live

Resolves in the active theme — flip the toggle above to verify.

States · from doc

Parsed from the doc. Interact with the live demo for hover/active.

**no matches** — `emptyText` in the listbox.

Searchable token picker — icon + symbol trigger, Dialog list with balances.

Anatomy

┌ trigger ──────────────────┐
│ [◎] SOL                ▾  │  ← TokenIcon + symbol (or placeholder)
└───────────────────────────┘
         │ opens Dialog
         ▼
┌ Select token ────────── × ┐
│ [ Search tokens…        ] │
├───────────────────────────┤
│ [◎] SOL                   │
│     Solana         12.40  │  ← icon · symbol/name · balance
│ [◎] USDC                  │
│     USD Coin    1,204.00  │
└───────────────────────────┘

Props

PropTypeDefaultNotes
tokensTokenOption[]—{ id, symbol, name, iconSrc?, balance?, disabled? }.
valuestring undefined`—Selected token id; undefined = none chosen.
onValueChange(id: string) => void—Fires on pick; closes the Dialog.
placeholderstring"Select token"Trigger text when nothing selected.
emptyTextstring"No tokens found"Shown when search matches nothing.
disabledboolean—Disables the trigger.
classNamestring—cn-merged onto the trigger.
aria-labelstring"Select token"Accessible name; appends selected symbol when set.

"use client" — owns ephemeral open/query/active state; domain value

is controlled by the caller.

Motion

Trigger: `transition-[background-color,border-color,transform]

duration-150 + active:scale-[0.96]` (control press). Dialog enter/

exit via Radix data-[state] presets. List highlight is instant

(keyboard/mouse tracking — never animate data surfaces).

A11y

Trigger: aria-haspopup="dialog", aria-expanded, label includes the

selected symbol when set. Dialog: Radix focus trap + Escape + overlay

dismiss. Search: ArrowUp/Down/Home/End move aria-activedescendant

through the listbox; Enter selects. Options expose aria-selected /

aria-disabled. Hit area: trigger h-9 (≥36) · rows py-2.

Self-contained: copy the folder into any Tailwind+React app, or use the install command above. Docs and this page render the same file on disk — they cannot drift.