Date reelPro
A 3D wheel date and time picker (like iOS reels) with momentum scrolling, snapping, curved perspective, and full keyboard support.
Live · keyboard ready
Send later
- Scheduling a send, reminder or post where a phone-style wheel is familiar and faster than a calendar grid.
- Touch-first flows and mobile web, where flicking a reel beats typing a date.
- Birthdays and other dates far from today, using date mode with its month, day and year reels.
- Use date-picker or date-range-picker when people need to see a whole month or pick a range.
- Use time-picker for a compact field inside a dense form.
- Use time-dial to compare times across cities.
Installation
Pro source and install commands unlock with a Pro plan.
example.tsx
import { DateReel } from "@/registry/components/date-reel/date-reel"; export function SendLater({ onSchedule }: { onSchedule: (at: Date) => Promise<void> }) { return ( <DateReel title="Send later" today={new Date(2026, 8, 24)} defaultValue={new Date(2026, 8, 25, 9, 0)} onConfirm={onSchedule} /> );}DateReel
A 3D reel date and time picker, like the wheels on a phone. Each column is a curved drum laid on a real cylinder: flick it and it keeps your momentum, glides and snaps into a glass lens, and a drag past the first or last entry stretches like rubber. A summary above reads the moment back in words as the reels turn ("Tomorrow at 9:00 AM"), quick picks spin every reel to a preset, and an optional confirm button settles in place.
PropTypeDefaultDescription
mode"datetime" | "date" | "time""datetime"Which reels to show: day, hour, minute and AM or PM; month, day and year; or time only.valueDate–Controlled value. When it changes from outside, every reel spins to it along the short way around.defaultValueDateSep 24, 2026 9:30 AMStarting value when uncontrolled. Pass a fixed date so the first render matches on server and client.onChange(value: Date) => void–Called once when the reels come to rest on a new moment, not on every frame.todayDateDay of the first valueStart of today, used for the Today entry and relative words such as Tomorrow or In 12 days. Pass new Date() after mount in an app.minDateDate30 days before today (100 years in date mode)Earliest selectable day. The day and year reels stop here with a rubber band.maxDateDateA year after today (10 years in date mode)Latest selectable day.minuteStep1 | 5 | 10 | 15 | 305Minutes between entries on the minute reel.hourCycle12 | 241212 hour clock with an AM or PM reel, or a 24 hour clock.localestring"en-US"BCP 47 locale for day and month names, the time format, the AM and PM words and relative phrases.presets{ label: string; value: Date | ((today: Date) => Date) }[] | falseThis evening, Tomorrow, MondayQuick picks under the reels. Choosing one spins every reel to it with a small stagger. The chip that matches the current value is marked pressed. false hides them.titlestring–Optional heading above the summary, such as "Send later".onConfirm(value: Date) => void | Promise<void>–Adds a primary button. Return a promise to show pending, success and failure in place without changing its width.labelsDateReelLabels–Words for localization: reel names (day, month, date, year, hour, minute, period), the at joiner, the quick picks group name and the confirm button states.introbooleantrueSpins the reels into place, one after another, the first time the picker scrolls into view.speednumber1Multiplies the speed of glides, snaps and the intro.pausedbooleanfalseStops automatic motion. Reels still follow a drag and land on the nearest entry at once.accentstring–Any CSS color for the active reel in the lens and the chosen quick pick. Defaults to the theme accent.labelstring"Date and time"Accessible name of the picker group.classNamestring–Class on the root element.styleCSSProperties–Inline styles on the root element.- Tab
- Moves between reels, the quick picks and the confirm button.
- Arrow uporArrow down
- Turns the focused reel one entry.
- Page uporPage down
- Turns by a larger step: a week of days, 3 or 6 hours, 15 minutes, 10 years.
- HomeorEnd
- Jumps to the first or last entry of the reel.
- Arrow leftorArrow right
- Moves focus to the neighbouring reel.
- Type a numberorletters
- Jumps to the matching entry, such as 4 for 4 o'clock, 45 for :45, p for PM or ma for March.
- The picker is a named group; each reel is a role="spinbutton" with its own name (Day, Hour, Minute, AM or PM) and aria-valuetext that reads the entry, such as "Fri, Sep 25".
- A polite live region announces the full moment once the reels come to rest after a change, and again when the confirm button succeeds or fails.
- No focus rings: the focused reel shows its entry in the accent color inside the lens, and quick picks use aria-pressed.
- Dates that do not exist, such as February 30, are dimmed on the reel and the day reel turns back to the last real day by itself.
- Drawn entries and the visual summary are hidden from assistive technology so a moving reel never floods the reader.
- One requestAnimationFrame loop runs only while a reel moves and writes transforms, opacity and text straight to recycled DOM nodes; React re-renders only when a value is committed.
- Each reel is projected onto a cylinder: entries rise on a sine curve, compress with the cosine of their angle and fade toward the edges, and a second copy clipped to the lens shows the crisp selected text.
- A flick keeps its measured velocity on an exponential glide that is solved to land exactly on an entry, then hands the last half row to a critically damped spring; drags past the ends stretch like rubber and bounce back.
- Keyboard steps, taps, wheel scrolling and quick picks all spin on the same spring, so pressing again mid-spin adds to the motion instead of restarting it.
- The loop stops when the picker leaves the viewport or the tab is hidden and resumes where it left off. With reduced motion every change lands at once and there is no intro.
- A container query sets the row height and type size, so the reels fit a 320px phone and read larger on desktop without changing proportions.
- Reels size to their widest entry and sit centered under one lens that spans the full width.
- Below about 480px the confirm button moves under the quick picks at full width and 44px tall.
- Reels use touch-action pan-x, so a vertical swipe turns the reel and does not scroll the page.
- Eleven recycled nodes per reel (twice, for the lens), regardless of how many days or years the range holds.
- Text is written only when a node shows a new entry; the loop sleeps when every reel is at rest.
- Wheel scrolling is attached as a non-passive listener only on the reels.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use it where choosing a moment is the task: schedule send, reminders, booking a slot, a birthday on sign up.
- Pass today and defaultValue as fixed dates for server rendering, then update today after mount if you need the real clock.
- onChange fires when the reels rest, so it is safe to save or validate there. For a confirm flow return the promise from onConfirm.
The full library index for assistants is at /llms.txt.