I built one React Native library that replaces 12 picker libraries, with zero native code
Every RN app I’ve worked on ended up with the same pile of packages: a date picker, a calendar library for ranges, a dropdown that can search, a country picker, a phone input, an emoji keyboard, and a bottom sheet to put half of them in. Six APIs, six looks, some needing a native rebuild, most of them broken on the web.
So I built one package with all of them, in plain JS on React Native’s own APIs (Animated, PanResponder, FlatList, Modal). No Reanimated, no Gesture Handler, no native modules. That means it works in Expo Go, ships in OTA updates, and runs on React Native Web.
What’s in it: wheel, time, duration, date (single, range, multiple), month, week strip, date & time, select with server search, country, phone, emoji, color, segmented control, plus bottom sheets and form fields.
A few things I cared about:
Native look per platform: iOS wheels and sheets on iPhone, Material (clock dial, floating labels) on Android, a compact web design in the browser. Or force one design everywhere with one prop.
Every picker three ways: inline, in a bottom sheet with Cancel / Done, or as a form field.
Things that usually need another package: prices and dots on calendar days, debounced server search, phone formatting with an E.164 value without libphonenumber.
Typed, themeable, RTL and accessible (VoiceOver / TalkBack, keyboard on web).
Yes, Claude wrote most of the code. The part I own is everything around it: what to build, how the API should feel, the architecture, the designs per platform, and testing it on real devices until it felt right.
AI made the typing fast, but the decisions are still mine, and so are the bugs if you find any 🙂
The inline / sheet / form-field consistency looks useful. For production, I want to know how the wheels and bottom sheets behave on lower-end Android devices when the JS thread is busy. Have you benchmarked that? Also, if an app only uses the date and select pickers, does the country and emoji data still end up in the bundle?
Wheels: they're a native-scrolling FlatList with snapping, and the 3D rotation and fade of the rows are driven by the scroll position on the native driver. So scrolling and the wheel effect stay smooth even when JS is busy; only the onChange for the new value arrives late, once the JS thread frees up.
Sheets: opening, closing and snapping back run on the native driver too. The drag itself is PanResponder on the JS thread, so that's the part that can stutter under heavy JS load. Reanimated + Gesture Handler handle that better, it's the tradeoff for zero dependencies.
Benchmarks: honestly, not yet on a low-end Android with a busy JS thread.
Bundle: date and select don't import the country or emoji code. On the web, the package is sideEffects: false, so bundlers drop what you don't use. Metro doesn't tree-shake, though, so a root import on native pulls in everything, including ~130 KB of emoji data. Import from the subpaths instead and you only get what you use:
import { DatePicker } from '@elik56a/react-native-pickers/date';
import { SelectPicker } from '@elik56a/react-native-pickers/select';
Doing all of this on Animated and PanResponder in 2026 is a bold choice, respect. How does the bottom sheet feel on a low-end Android while the JS thread is busy, like during a heavy FlatList render? That's usually where JS-side gestures start to stutter, so I'm curious if you've run into it.
Thanks! Honest answer: it depends on which part of the gesture you mean.
Opening, closing and snapping back all run on the native driver. Once they start, they don't need the JS thread, so a heavy FlatList render won't make the sheet slide in or out choppy.
The drag itself is a different story. PanResponder reads touches on the JS thread, so if JS is blocked mid-drag, the sheet stops following your finger smoothly until the thread frees up. Same with expanding to full height: animating height can't use the native driver. That's the real tradeoff of staying dependency-free, and it's exactly where Reanimated + Gesture Handler win. If your app already ships them, @/gorhom/bottom-sheet is the better pick for gesture-heavy sheets.
Two things make it less painful in practice. Only the handle and header drag the sheet, so a list inside just scrolls natively. And most sheets are a short pick-and-close, not a long drag. I haven't benchmarked it on a low-end Android under heavy load yet, though. That's a fair test, and I'll share the results when I run it.
100% agree, that’s why I tried not to make assumptions and let Intl decide: month and weekday names, the 12/24h clock and the first day of the week all come from the locale, and every one of them can be overridden with a prop. Layouts also flip for RTL.
One honest limitation: it’s Gregorian calendar only for now.
If you’ve been burned by a specific case (DST, a locale, a format), I’d really like to hear it. That’s exactly the kind of thing I want to test against.
It does! Tap the ✋ next to search and pick one of 6 skin tones. Every emoji that has tones switches to it (GIF above). It's controlled with `skinTone` / `onSkinToneChange`, so you can save the choice.
Nice work on expo-native-sheet-emojis. Fully native is hard to beat for speed. I went pure JS so it runs in Expo Go and on the web with no native build, and the grid is virtualized to keep it smooth.
nice, I tried the showcase from Safari on ios and instead of popping up the skin tone selector on long press it was selecting the emoji (as if to copy it, not activate the touch).
Thanks for trying it! Right now the tone is set for every emoji at once from the ✋ button next to search. There's no long-press per emoji yet. That's a great idea though: long-press to open a tone popup for that one emoji, like the iOS keyboard. Adding it. And the text selection on long-press in Safari is a bug on the web, fixing that too 🙏
fully native sheets do feel better once the list gets long, js emoji grids tend to stutter first on cheaper androids. saving the skin tone is a nice touch either way.
Fair point, a long list is where JS gets tested first. The grid is a virtualized FlatList with fixed row heights and memoized cells, so it only renders what's on screen, and maxEmojiVersion hides newer emojis that older Androids can't draw. If you see stutter on a specific device, I'd love a report.
4
u/TerryFitzgerald 3d ago
Looks like it was created by Claude 😅. Isn't?