cupertino-datetime-picker

Wheel and SegmentedControl

One column of an iOS picker, and the AM/PM control, exported for building pickers of your own.

Wheel

One column of an iOS picker. Scrolling is native with snap points, so touch and trackpad flings come from the platform; mouse drag has its own deceleration; a click on any row picks it; the keyboard steps with the arrows and jumps on typed digits. Rows tilt away from the centre and fade at the edges. The centre row is the value.

import { Wheel, WheelHighlight } from "@/components/ui/cupertino/wheel";

const minutes = Array.from({ length: 60 }, (_, m) => ({
  value: m,
  label: String(m).padStart(2, "0"),
}));

<div className="cdp cdp-wheel-mask relative flex justify-center">
  <WheelHighlight />
  <Wheel
    options={minutes}
    value={minute}
    onChange={setMinute}
    loop
    aria-label="Minute"
    className="w-16"
  />
</div>;

A group of wheels shares one WheelHighlight, the translucent bar behind the centre row, positioned by a relative container. cdp-wheel-mask fades the top and bottom rows.

PropTypeDefault
options{ value: number; label: string }[]The rows, top to bottom.
valuenumberThe centred option's value.
onChange(value: number) => voidCalled when the wheel settles on a new row.
loopbooleanfalseWrap around, like the hour and minute wheels.
itemHeightnumber32Row height in px. Pass the same to WheelHighlight.
visibleRowsnumber5Rows shown; the wheel is this many rows tall.
aria-labelstringRequired. The wheel is a spinbutton.
classNamestringSet the width here.

onChange fires once per settle, not per pixel, and not when the wheel comes to rest where it already was.

SegmentedControl

The iOS segmented control: a translucent track and a thumb that slides to the chosen segment. Underneath it is a native radio group, so arrow keys, focus and form semantics come from the browser.

import { SegmentedControl } from "@/components/ui/cupertino/segmented-control";

<SegmentedControl
  options={[
    { value: "am", label: "AM" },
    { value: "pm", label: "PM" },
  ]}
  value={period}
  onChange={setPeriod}
  aria-label="Day period"
/>;
PropType
options{ value: T; label: string }[]T is a string, number or boolean.
valueTThe chosen segment.
onChange(value: T) => void
aria-labelstringRequired. Names the radio group.
classNamestring

On this page