npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-wheel-kit

v0.1.0

Published

Wheel pickers for React Native that look and feel the same on iOS and Android: a snapping WheelPicker, plus ready-made TimePicker and DatePicker. Haptic tick per row, virtualised, UI-thread animation, accessible. Built on Reanimated.

Readme

react-native-wheel-kit

npm license types platforms

Wheel pickers for React Native that look and behave the same on iOS and Android: a snapping WheelPicker for any list, plus a ready-made TimePicker and DatePicker.

Android has no wheel picker of its own, and the iOS one can't be styled much. This is one wheel, drawn the same on both platforms: the selected row sits on a band, its neighbours are greyed and shrunk, and the edges fade out.

  • A haptic tick per row. onTick fires once for every row that passes the band while a finger spins the wheel, including the glide after a flick, just like the iOS wheel. When your code sets the value, the wheel stays quiet, so opening a sheet never buzzes.
  • Long lists stay fast. The list is virtualised: a wheel of 500 weights or a century of years only keeps the rows near the viewport mounted.
  • The animation runs on the UI thread. Rows are scaled, not re-laid out, so nothing reflows while it spins.
  • It works inside a ScrollView. A wheel inside a scrolling screen doesn't trigger React Native's "VirtualizedLists should never be nested" error, and it scrolls on Android.
  • Screen readers get one control. Each wheel is a single adjustable element: swipe up or down to step, and the selected label is read out.
  • Dates don't drift. DatePicker works on { year, month, day }, not on a Date, so there are no time-zone surprises. The day wheel always has the days that month has.
  • It fits any locale. Month names and wheel order come from Intl, e.g. en-US month-day-year, de-DE day-month-year, ja-JP year-month-day.
  • No extra dependencies. Only Reanimated is needed, with no gradient or SVG library.

Install

npm install react-native-wheel-kit

Reanimated is a peer dependency you most likely already have:

npx expo install react-native-reanimated

WheelPicker

import { WheelPicker } from 'react-native-wheel-kit';
import * as Haptics from 'expo-haptics';

const weights = Array.from({ length: 171 }, (_, i) => ({ value: 30 + i, label: String(30 + i) }));

<WheelPicker
  options={weights}
  value={weight}
  onChange={setWeight}
  onTick={() => Haptics.selectionAsync()}
  accessibilityLabel="Weight"
/>

Values can be strings or numbers. Put two wheels in a row with style={{ flex: 1 }} for a number and its unit.

Keep options stable (a constant, or useMemo). A new array tells the wheel it's a different list, and it jumps to the value instead of gliding.

TimePicker

import { TimePicker, type Time } from 'react-native-wheel-kit';

const [time, setTime] = useState<Time>({ hours: 7, minutes: 30 });

<TimePicker value={time} onChange={setTime} minuteInterval={5} />
<TimePicker value={time} onChange={setTime} format="12h" />

The value is always on the 24-hour clock (hours 0–23). format="12h" only changes what's shown: hours read 12, 1…11, and a third wheel shows AM/PM. A value off the minuteInterval grid shows as the nearest row. Use snapTime(time, interval) to get the same value without waiting for a turn.

DatePicker

import { DatePicker, fromDate, toDate, type CalendarDate } from 'react-native-wheel-kit';

const [date, setDate] = useState<CalendarDate>({ year: 1994, month: 1, day: 31 });

<DatePicker
  value={date}
  onChange={setDate}
  minimumDate={{ year: 1920, month: 1, day: 1 }}
  maximumDate={fromDate(new Date())}
  monthFormat="long"
/>

month is 1–12. Turning from 31 March to February lands on the 28th (or the 29th in a leap year). The wheels never offer a day outside minimumDate–maximumDate. locale names the months and orders the wheels, defaulting to the device's locale; order overrides the order.

Need a Date? toDate(date) gives midnight in the device's time zone, and fromDate(new Date()) gives today.

Styling

Every styling prop below works on WheelPicker. Pass the same props to TimePicker or DatePicker and they go to each of their wheels, so the wheels always match.

| Prop | Default | | |---|---|---| | itemHeight | 44 | Row height. | | visibleRows | 5 | Odd: 3, 5 or 7. | | fontSize | 22 | | | neighbourScale | 0.8 | How far the rows next to the selected one shrink. | | selectedColor | #3266AE | Selected row's text. | | textColor | #6B7788 | Other rows' text. | | backgroundColor | #FFFFFF | The field, and the colour the edges fade into. | | bandColor | #EAF0F9 | transparent for no band. | | borderColor | #DCE3EC | transparent for no border. | | borderRadius | 16 | | | fadeHeight | itemHeight | 0 for no fade. | | textStyle | | Font family, weight… | | bandStyle | | | | maxFontSizeMultiplier | 1.3 | Caps system font scaling, since a row has a fixed height. | | onTick | | Haptic hook, see above. | | disabled | false | |

style sets the wheel's own field on WheelPicker. On TimePicker and DatePicker, style sets the row the wheels sit in and wheelStyle sets each wheel.

Dark mode: set backgroundColor, bandColor, borderColor, selectedColor and textColor from your theme.

API

WheelPicker<T extends string | number>

options: { value: T; label: string }[], value: T, onChange(next: T), accessibilityLabel: string, plus the styling props above and testID.

TimePicker

| Prop | Default | | |---|---|---| | value, onChange | | { hours: 0–23, minutes: 0–59 } | | minuteInterval | 1 | Should divide 60. | | format | '24h' | '24h' or '12h'. | | amLabel, pmLabel | 'AM', 'PM' | | | separator | ':' | A string or a node. null for none. | | accessibilityLabels | Hours, Minutes, AM or PM | { hours, minutes, period } | | gap | 10 | |

DatePicker

| Prop | Default | | |---|---|---| | value, onChange | | { year, month: 1–12, day } | | minimumDate, maximumDate | 1900-01-01, 2100-12-31 | | | locale | device | BCP 47, e.g. 'de-DE'. | | monthFormat | 'short' | 'long', 'short' or 'numeric'. | | order | from locale | 'dmy', 'mdy' or 'ymd'. | | accessibilityLabels | Day, Month, Year | | | gap | 8 | |

Helpers

snapTime, fromDate, toDate, daysInMonth, isLeapYear, clampDate, compareDates, monthNames(locale, format), dateOrder(locale).

Testing

Each wheel is an adjustable element, so tests can step it the way a screen reader does:

fireEvent(screen.getByLabelText('Hours'), 'accessibilityAction', {
  nativeEvent: { actionName: 'increment' },
});
expect(screen.getByLabelText('Hours').props.accessibilityValue.text).toBe('08');

License

MIT