---
title: Picker
description: A momentum wheel picker with one or more columns.
---
import Source from './_generated/picker.mdx';

A momentum wheel picker — the classic spinning drum (iOS `UIPickerView` / `ion-picker`). Flick a column to spin it with velocity, momentum and exponential deceleration; it snaps to the nearest row and rubber-bands at the ends. Tap a row to select it, scroll with a trackpad, or drive it programmatically. Compose one or more `picker-column`s for single- or multi-column pickers. The component is standalone and **inline** — embed it anywhere; hosting it in a [Bottom Sheet](/docs/components/bottom-sheet) is just composition.

<DemoFrame component="picker" height={520} />

> **Info**
>
> On iOS this reads as the classic dimmed drum (flat translate with an edge-fade mask; the 3D barrel rotation is optional future polish). Material 3 has no wheel idiom, so the same wheel renders with MD tokens — Material apps usually prefer a Select or time input, but the wheel exists for parity and iOS-style flows. Under `prefers-reduced-motion` there is no momentum: a release snaps directly to the nearest row.

## Installation

```bash
npx touchcn add picker
```

## Usage

<CodeGroup>

```html Angular
<tcn-picker>
  <tcn-picker-column label="Hour" [options]="hours" [(value)]="hour" />
  <tcn-picker-column label="Minute" [options]="minutes" [(value)]="minute" />
</tcn-picker>
```

```tsx React
import { TcnPicker, TcnPickerColumn } from '@/components/ui/picker';

<TcnPicker>
  <TcnPickerColumn label="Hour" options={hours} value={hour} onValueChange={(v) => setHour(Number(v))} />
  <TcnPickerColumn label="Minute" options={minutes} value={minute} onValueChange={(v) => setMinute(Number(v))} />
</TcnPicker>
```

```vue Vue
<script setup lang="ts">
import { TcnPicker, TcnPickerColumn } from '@/components/ui/picker';
</script>

<template>
  <TcnPicker>
    <TcnPickerColumn label="Hour" :options="hours" v-model="hour" />
    <TcnPickerColumn label="Minute" :options="minutes" v-model="minute" />
  </TcnPicker>
</template>
```

</CodeGroup>

Columns are **data-driven**: pass `options` as strings/numbers or `{ label, value }` objects, and bind the selected `value`. All physics — pointer tracking with velocity sampling, momentum, snap, rubber-band overscroll, tap-to-select, wheel/trackpad and keyboard — lives per column in the engine (`TcnPickerColumnDirective` / `usePickerColumn`); the copied components own only markup. The [Datepicker](/docs/components/datepicker)'s iOS `datetime` time drum is built on this same engine.

## Accessibility

Each column is a focusable `role="listbox"` of `role="option"` rows, with the selected row carrying `aria-selected`. Keyboard support (when a column is focused): <kbd>↑</kbd>/<kbd>↓</kbd> move by one, <kbd>PageUp</kbd>/<kbd>PageDown</kbd> by three, <kbd>Home</kbd>/<kbd>End</kbd> jump to the ends. This is an **honest baseline** — `aria-activedescendant` and richer screen-reader narration are not yet implemented, so verify against your own SR requirements.

## Props

### Picker Column

| Prop                        | Type                                            | Default | Description                                        |
| --------------------------- | ----------------------------------------------- | ------- | -------------------------------------------------- |
| `options`                   | `Array<string \| number \| { label, value }>`   | `[]`    | The rows to spin through.                          |
| `value` / `onValueChange`   | `string \| number` / `(value) => void`          | —       | The selected value (two-way on Angular).           |
| `label`                     | `string`                                        | —       | Accessible label for the column.                   |
| `itemHeight`                | `number`                                        | `34`    | Row height in px; must match the theme.            |
| `disabled`                  | `boolean`                                        | `false` | Disable the gesture (programmatic value still snaps). |

<Source />
