React Wheel Picker: iOS Wheels for Times and Dates
You want a React wheel picker because a phone user should not type 14:30 on a keyboard that covers half the form, and paging a calendar back 36 years to a birth date is worse. The hard part is the feel: the React wheel packages we checked replace the browser's scrolling with their own touch physics. This guide builds the wheel on a native snapping scroller with rows turned on a 3D cylinder, then covers mouse and keyboard paths, the time and date fields built on it, and the pitfalls. How wheel pickers compare across libraries is in our best React component library for mobile comparison.
When is a wheel picker the right control?
When the list is medium to long, ordered, and the user can guess what is hidden: hours, minutes, durations, quantities, a birth year. Apple's Human Interface Guidelines for pickers (opens in a new tab) say to "consider using a picker to offer medium-to-long lists of items", to prefer a pull-down button for "a fairly short list of choices", and a list or table for "a very large set of items". It also suggests minute steps that divide 60 evenly, such as quarter hours.
On the web that gives four rules:
- Two to five options: a Segmented Control, so every option is visible.
- 12 hours, 60 minutes, 1 to 99 items, the years since 1920: a wheel.
- Hundreds of unordered names (countries, products): a searchable Select. A wheel only jumps to a typed prefix.
- A date near today (a delivery, a booking): a calendar, because the weekday matters.
Why build a wheel on a native snapping scroller?
Because then the finger, the momentum and the rubber band at the ends are the phone's own, and a trackpad or a mouse wheel turns it with no extra code. A search for "ios picker web" in October 2026 mixed a Mendix widget, React Native packages and Flutter's CupertinoPicker in with the web answers. The two React DOM packages we found both run their own physics:
Both work. @ncdai/react-wheel-picker, which also handles arrows, Home / End and type-ahead, is the better pick if you want a free, unstyled wheel and will build the field yourself. Its feel is tuned through dragSensitivity and scrollSensitivity; a native scroller decelerates like every other list on that phone.
The native recipe has three parts. The list scrolls with scroll-snap-type: y mandatory, every row snaps to the center, and two rows of padding let the first and last rows reach it. One requestAnimationFrame per scroll reads scrollTop and writes rotateX and opacity straight to the rows, never through React state. And the value commits once, at rest: on scrollend, which MDN (opens in a new tab) lists from Chrome 114, Firefox 109 and Safari 26.2, with a 120ms idle timer for older Safari.
That is the core of the Wingo UI Picker Wheel. The sketch misses a starting value, disabled rows, looping, every input that is not a finger, and wheels mounted while hidden.
How do mouse and keyboard users turn the wheel?
A mouse wheel and a trackpad scroll it natively; dragging and keys need their own code. In the Picker Wheel only a mouse goes through pointer events: past 8 pixels of movement, snapping turns off and scrollTop follows the pointer. On release, a motion inertia animation continues from the measured velocity, and its modifyTarget rounds the landing point to a row. A click on a visible row glides it to the center.
Each column is one Tab stop with role="listbox". ArrowUp and ArrowDown move a row, PageUp and PageDown five, Home and End jump to the ends, typing jumps to the next option that starts with the text ("19", then "199" on a year wheel), and ArrowLeft and ArrowRight change column. aria-activedescendant points at the centered option, which carries aria-selected. The focus ring hugs the center row, where the edge fade cannot hide it.
What props does the Picker Wheel need?
columns (or options for a single wheel), a value and onValueChange. Everything else has a default:
The props worth knowing:
onValueChangefires once per settle, never per row passed, so it is safe to save from it.loop(on the wheel or per column) wraps 59 back to 00: the column renders extra copies and jumps silently to the middle one at rest. Only the middle copy hasrole="option".sizesets 36, 44 or 52px rows;visibleRows(odd, default 5) makes the wheel 220px tall atmd;perspectivetilts rows 20, 10 or 0 degrees each (ios,subtle,flat).- A disabled option is struck through, and a wheel that lands on one glides on to the nearest enabled row:
pickerRange(36, 46, { disabled: (n) => n === 38 })for a sold-out shoe size. hapticsplays a selection tick each time a finger, a mouse drag or the scroll wheel brings a new row to the center, never for keys. It works on Chrome for Android; why a scroll cannot tick on an iPhone is in our web vibration API guide.
PickerWheelField puts the same wheels in a form field: a bottom sheet with Done and Cancel below 768px, a popover that applies live on desktop.
How do you build a React time picker wheel that still types on desktop?
Use the Time Picker, which switches by width. Below 768px the field is read-only with inputMode="none", so no keyboard opens, and a tap opens a bottom sheet with hour and minute wheels. From 768px up it is a text input that reads "1430", "2:30pm" or "2p", steps minutes with ArrowUp and ArrowDown, and opens a panel of columns on click. The value is always "HH:mm" in 24 hours:
$ npx wingo-ui@latest add time-pickerThe sheet edits a draft: Done commits it, and dragging the sheet away keeps the old time. Times outside min and max or blocked by disabledTimes are disabled rows on the wheels too.
Should a date be a wheel or a calendar?
A wheel for a date the user knows by heart and that sits far from today, like a birth date: three flicks instead of paging through years. A calendar for dates near today, where the weekday matters. That is why the Date Picker opens a calendar, in a bottom sheet on phones.
For the birth date, useDateWheel is a React date wheel picker in one hook: day, month and year columns in the locale's order (month, day, year for en-US), month names in that language, days that follow the month (February 29 only in leap years) and years between min and max. Values are ISO dates such as "1990-05-17".
$ npx wingo-ui@latest add picker-wheelFor the wheel on phones and a typed field on desktop, render both and switch with CSS, so the server HTML matches the first paint (the mobile-first React components guide explains why a useIsMobile check would flash). One ISO string feeds both:
One catch: useDateWheel always holds a date (today when you pass none), so the field is never empty. That suits editing a profile; a required date that starts blank belongs in the Date Picker.
What goes wrong with a wheel picker on a phone?
Most bugs come from the wheel's surroundings. The Picker Wheel handles these, and you will meet them if you build your own:
- The sheet steals the drag. A wheel at its first row hands a downward drag to the drawer. The columns carry
data-vaul-no-drag, andoverscroll-behavior: containkeeps a hard flick from scrolling the page. - A wheel mounted while hidden. Inside a closed sheet the list has no height, so setting
scrollTopdoes nothing. AResizeObserverscrolls to the value once the list has a size. - Committing per row. Saving on every row that passes floods your API and fires validation mid-spin.
- Reduced motion. With
prefers-reduced-motionthe rows stay flat and programmatic moves jump instead of gliding. - Feedback only in the tick. Desktop and iPhone users feel nothing, so the centered row and the field text must carry the value.
Where should you start?
Open this page on your phone and flick the two demos above. The Picker Wheel, Time Picker and Date Picker are part of Wingo UI Pro; with a Pro account signed in through npx wingo-ui@latest login, npx wingo-ui@latest add picker-wheel copies the component and everything it imports into your project. A React wheel picker component is one piece of a phone-friendly form; the other patterns are in Mobile UI in React.
FAQ
Is there an iOS-style wheel picker for React web apps?
Yes. As of October 2026 the open source options are @ncdai/react-wheel-picker (1.2.3, MIT) and react-mobile-picker (1.2.0, MIT), which both run their own touch physics. Wingo UI's Picker Wheel (Pro) scrolls natively and adds a form field that opens a bottom sheet on phones, a date hook and haptic ticks.
Should I use input type=time instead of a wheel picker?
Use the native input when its look and behavior are good enough, because it costs nothing and is accessible by default. You cannot style the picker it opens, it differs between browsers and OS versions, and it cannot show blocked times such as a lunch break, which is when a custom wheel earns its code.
How do I make a wheel picker loop around in React?
Render the options several times and jump back to the middle copy whenever the wheel comes to rest, which looks identical. In the Wingo UI Picker Wheel, loop on the wheel or on one column does this, with at least 40 rows on each side of the middle copy so a hard flick never reaches the end.
Does a web wheel picker vibrate on iPhone?
No. Safari has no Vibration API, and the iPhone's one web haptic, the tick of a native switch, plays only for a tap, never for a scroll. On Chrome for Android the Picker Wheel ticks each time a new row reaches the center.
Is the Wingo UI Picker Wheel free?
No. The Picker Wheel, Time Picker and Date Picker are Pro components; the Drawer and Field they build on are free. Pro costs $8 a month, $80 a year or $150 once.
- Mobile UI
- Wheel picker
- Time picker
- iOS
- React