New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

react-native-drum-picker

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-native-drum-picker

Cross-platform native drum/wheel picker for React Native (Fabric)

latest
Source
npmnpm
Version
0.3.1
Version published
Maintainers
1
Created
Source

react-native-drum-picker

CI codecov npm version license platform - android platform - ios React Native

A smooth cross-platform native iOS-style drum/wheel picker for React Native (Fabric / New Architecture).

Preview

Recordings of the picker in real screens — basic wheel, time picker, height/weight onboarding, date columns, debounced updates and large lists — are inlined throughout Examples.

An iOS recording is still pending; iOS behaviour is verified in CI by ios-build and by pod lib lint with XCTest in ios-unit-tests.

Features

  • Android native implementation (Kotlin + RecyclerView)
  • iOS native implementation (Swift + UIPickerView)
  • iOS-style wheel / drum picker with smooth snapping
  • Center selection indicator (optional)
  • Transparent background by default
  • Custom text colors and sizes
  • TypeScript API
  • Flexible DateDrumPicker wrapper (day / month / year columns)
  • Fabric View / New Architecture
  • Zero runtime dependencies — nothing is added to your dependency tree

Installation

yarn add react-native-drum-picker
npm install react-native-drum-picker

This package includes native Android and iOS code. Rebuild your app after installing:

npx pod-install
npx react-native run-ios
npx react-native run-android

Platform support

PlatformStatus
AndroidSupported
iOSSupported — requires 0.3.1 or newer when installed from npm
WebRead-only preview stub (DrumPicker.tsx); ref API works, no native wheel

Requires React Native 0.76+ with the New Architecture enabled.

iOS on 0.2.4 and 0.3.0: those releases shipped without DrumPicker.podspec, so CocoaPods could not autolink the native code and iOS rendered Unimplemented component: <DrumPickerView>. Upgrade to 0.3.1+; no code changes are needed on your side.

Compatibility

EnvironmentStatus
React Native New ArchitectureRequired
FabricRequired
AndroidSupported
iOSSupported
iOS Old Architecture (Paper)Not supported — Fabric required
Expo GoNot supported (native library)
Expo dev build / prebuildWorks; not covered by CI (see below)
react-native-screens navigationTested (use 0.1.4+ for detach safety)

What CI actually verifies

Every PR runs against the example app, so this table reflects real builds rather than intent:

ToolVersionJobs
React Native0.85.0android-build, ios-build
New Architectureenabledall native jobs
Androidemulator, API 34android-instrumented
iOSXcode 26ios-unit-tests (pod lib lint + XCTest)
Published tarballpackage-contents (what npm pack would actually ship)

Intended range: react-native >= 0.76 with New Architecture. Older 0.76–0.84 may work but are not CI-guaranteed.

Expo: the example app is bare React Native, so Expo is not exercised in CI. The library needs no config plugin — a development build or expo run:ios / expo run:android after expo prebuild picks it up through autolinking. Expo Go will never work, because it cannot load custom native code.

React Native 0.81+ event dispatch

If Android Kotlin compile fails with No value passed for parameter 'uiManagerType', upgrade to 0.1.3+ (Fabric UIManagerType.FABRIC).

Basic usage

import { DrumPicker } from 'react-native-drum-picker';

export function Example() {
  return (
    <DrumPicker
      items={['Mon 7 Sep', 'Tue 8 Sep', 'Wed 9 Sep']}
      selectedIndex={1}
      itemHeight={44}
      visibleItemCount={5}
      onChange={(event) => {
        console.log(event.nativeEvent.index, event.nativeEvent.value);
      }}
      style={{ width: 150, height: 220 }}
    />
  );
}

Layout defaults (0.1.5+)

The JS wrapper applies minWidth: 64 and, unless you use flex or pass height / minHeight, height: itemHeight * visibleItemCount (default 220). Native Android also sets matching minimumWidth / minimumHeight.

For production layouts, still pass explicit dimensions:

style={{ width: 120, height: itemHeight * visibleItemCount }}

In __DEV__, a one-time warning is logged if neither height nor flex sizing is provided.

Examples

Basic picker

Basic picker demo

Time picker (hour · minute)

Time picker demo

const hours = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));
const minutes = Array.from({ length: 60 }, (_, i) =>
  String(i).padStart(2, '0')
);

<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <DrumPicker items={hours} style={{ width: 72, height: 220 }} />
  <DrumPicker items={minutes} style={{ width: 72, height: 220 }} />
</View>;

Height / weight row (onboarding style)

Height/weight demo

<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <DrumPicker items={heightsCm} style={{ width: 90, height: 220 }} />
  <DrumPicker items={weightsKg} style={{ width: 96, height: 220 }} />
</View>

Controlled selectedIndex

Controlled picker demo

const [index, setIndex] = useState(1);

<DrumPicker
  items={items}
  selectedIndex={index}
  onChange={(e) => setIndex(e.nativeEvent.index)}
  style={{ width: 120, height: 220 }}
/>;

Locking the picker while a form submits

const [saving, setSaving] = useState(false);

<View style={{ opacity: saving ? 0.4 : 1 }}>
  <DrumPicker items={items} disabled={saving} onChange={handleChange} />
</View>;

disabled blocks drags and taps, and swallows touches so nothing behind the picker reacts either. Programmatic movement is deliberately left working — selectedIndex and the scrollToIndex / scrollToValue ref methods still scroll the wheel, so you can reset or correct a locked form. Appearance is up to you: the prop changes behaviour only, so wrap it as above if it should also look inert.

onChange and expensive side effects

Debounce demo

Native emits onChange when the wheel snaps to idle and the centered index changes (duplicate indices are ignored). Use it for UI state. For AsyncStorage, APIs, or analytics, debounce in your app:

const save = useMemo(() => {
  let t: ReturnType<typeof setTimeout> | undefined;
  return (value: string) => {
    if (t) clearTimeout(t);
    t = setTimeout(() => {
      // persist value
    }, 300);
  };
}, []);

<DrumPicker onChange={(e) => save(e.nativeEvent.value)} ... />

Live sync between pickers

Use onValueChanging to update a preview while the user is still scrolling — before they lift their finger:

const [previewIndex, setPreviewIndex] = useState(1);

<DrumPicker
  items={['AM', 'PM']}
  onValueChanging={({ nativeEvent }) => {
    setPreviewIndex(nativeEvent.index);
  }}
  onChange={({ nativeEvent }) => {
    setPreviewIndex(nativeEvent.index);
  }}
/>;

onValueChanging can fire many times per second while the wheel moves. Keep the handler light; for UI updates prefer a ref or debounce/requestAnimationFrame instead of heavy setState on every tick. Use onChange for the final committed value.

See the example app (example/src/App.tsx) for basic, time, height/weight, date, controlled, and debounced demos.

Imperative ref API

Control the picker programmatically using a ref. scrollToIndex / scrollToValue default to animated: true and invoke onChange when the selection changes (so controlled selectedIndex stays in sync). withVirtualized forwards the same DrumPickerRef using real list indices.

import { useRef } from 'react';
import { Button } from 'react-native';
import { DrumPicker, type DrumPickerRef } from 'react-native-drum-picker';

const months = ['Jan', 'Feb', 'Mar', 'Jun'];

function MyPicker() {
  const ref = useRef<DrumPickerRef>(null);

  return (
    <>
      <DrumPicker
        ref={ref}
        items={months}
        onChange={({ nativeEvent }) => console.log(nativeEvent.value)}
      />
      <Button
        title="Jump to June"
        onPress={() => ref.current?.scrollToValue('Jun')}
      />
      <Button
        title="Reset to first"
        onPress={() => ref.current?.scrollToIndex(0, { animated: true })}
      />
    </>
  );
}

DateDrumPicker ref — "Today" button

Date picker demo

import { useRef, useState } from 'react';
import { Button } from 'react-native';
import {
  DateDrumPicker,
  type DateDrumPickerRef,
  type DateDrumPickerValue,
} from 'react-native-drum-picker';

function DateWithToday() {
  const [date, setDate] = useState<DateDrumPickerValue>({
    day: 1,
    month: 1,
    year: 2026,
  });
  const today = new Date();
  const dateRef = useRef<DateDrumPickerRef>(null);

  return (
    <>
      <DateDrumPicker
        ref={dateRef}
        mode="day-month-year"
        value={date}
        onChange={setDate}
      />
      <Button
        title="Today"
        onPress={() =>
          dateRef.current?.scrollToDate(
            {
              day: today.getDate(),
              month: today.getMonth() + 1,
              year: today.getFullYear(),
            },
            { animated: true }
          )
        }
      />
    </>
  );
}

DrumPickerRef API

MethodDescription
scrollToIndex(index, options?)Scroll to index. Clamped to valid range.
scrollToValue(value, options?)Scroll to first matching value. No-op if not found.
getCurrentIndex()Returns current selected index.
getCurrentValue()Returns current selected value string.

DateDrumPickerRef API

MethodDescription
scrollToDate(date, options?)Scroll columns to given date. Partial updates supported. Clamps invalid days and calls onChange when set.
getCurrentDate()Returns clamped { day, month, year } of current selection.

PickerGroup - synchronize multiple pickers

Connect multiple DrumPickers so they can react to each other:

import { useState } from 'react';
import { View } from 'react-native';
import {
  DrumPicker,
  usePickerGroup,
  usePickerGroupChangedEffect,
  usePickerGroupChangingEffect,
} from 'react-native-drum-picker';

const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));
const MINUTES = [
  '00',
  '05',
  '10',
  '15',
  '20',
  '25',
  '30',
  '35',
  '40',
  '45',
  '50',
  '55',
];

function TimePickerGroup() {
  const group = usePickerGroup();
  const [time, setTime] = useState({ hour: 0, minute: 0 });

  usePickerGroupChangedEffect(group, ({ pickerName, index }) => {
    setTime((prev) => ({ ...prev, [pickerName]: index }));
  });

  usePickerGroupChangingEffect(group, ({ pickerName, value }) => {
    console.log(`${pickerName} is at ${value}`);
  });

  return (
    <View style={{ flexDirection: 'row' }}>
      <DrumPicker
        pickerGroup={group}
        pickerName="hour"
        items={HOURS}
        onChange={() => {}}
      />
      <DrumPicker
        pickerGroup={group}
        pickerName="minute"
        items={MINUTES}
        onChange={() => {}}
      />
    </View>
  );
}

PickerGroup API

ExportDescription
usePickerGroup()Creates a group handle. Call once per component.
usePickerGroupChangedEffect(group, cb)Fires when any picker settles.
usePickerGroupChangingEffect(group, cb)Fires on every scroll tick.
group.getState()Snapshot of all current picker values.

DrumPicker group props

PropTypeDescription
pickerGroupPickerGroupHandleGroup from usePickerGroup()
pickerNamestringUnique name within the group

Custom item rendering

Use renderItem to replace the default text label with your own React UI:

import { DrumPicker } from 'react-native-drum-picker';
import { Text, View } from 'react-native';

const countries = [
  { label: 'Uzbekistan', value: 'UZ', flag: '🇺🇿' },
  { label: 'Russia', value: 'RU', flag: '🇷🇺' },
  { label: 'USA', value: 'US', flag: '🇺🇸' },
];

<DrumPicker
  items={countries}
  renderItem={({ item, isSelected }) => (
    <View style={{ flexDirection: 'row', gap: 8 }}>
      <Text style={{ fontSize: 24 }}>{item.flag}</Text>
      <Text
        style={{
          fontSize: 18,
          color: isSelected ? '#000' : '#999',
          fontWeight: isSelected ? '600' : '400',
        }}
      >
        {item.label}
      </Text>
    </View>
  )}
  onChange={({ nativeEvent }) => console.log(nativeEvent.value)}
/>;

renderItem info object

FieldTypeDescription
itemTRaw item from items
labelstringDisplay label string
indexnumberIndex in items
isSelectedbooleanWhether the row is centered

Performance note: keep renderItem lightweight. It can rerender frequently during wheel movement. For complex rows, wrap heavy subtrees with React.memo and pass a stable renderer via useCallback.

const Row = React.memo(function Row({
  flag,
  label,
  isSelected,
}: {
  flag: string;
  label: string;
  isSelected: boolean;
}) {
  return (
    <View style={{ flexDirection: 'row', gap: 8 }}>
      <Text>{flag}</Text>
      <Text style={{ color: isSelected ? '#111' : '#999' }}>{label}</Text>
    </View>
  );
});

const renderCountry = useCallback(
  ({ item, isSelected }) => (
    <Row flag={item.flag} label={item.label} isSelected={isSelected} />
  ),
  []
);

DateDrumPicker

Higher-level date columns (TypeScript only). Renders wheels only — no built-in titles; add labels in your app if needed.

import { useState } from 'react';
import { DateDrumPicker } from 'react-native-drum-picker';

export function DateExample() {
  const [date, setDate] = useState({ day: 21, month: 5, year: 2026 });

  return (
    <DateDrumPicker mode="day-month-year" value={date} onChange={setDate} />
  );
}
<DateDrumPicker
  mode="month-year"
  monthFormat="long"
  minYear={2020}
  maxYear={2035}
  value={{ month: 5, year: 2026 }}
  onChange={(value) => console.log(value)}
/>

Controlled: pass value and update in onChange.
Uncontrolled: omit value; internal state updates and onChange still fires.

Day count follows month/year (e.g. February has 28/29 days).

Date range constraints

Booking constraints demo

import { useState } from 'react';
import {
  DateDrumPicker,
  type DateDrumPickerValue,
} from 'react-native-drum-picker';

function BookingDatePicker() {
  const [bookingDate, setBookingDate] = useState<DateDrumPickerValue>({});
  const today = new Date();
  const nextYear = new Date();
  nextYear.setFullYear(today.getFullYear() + 1);

  return (
    <DateDrumPicker
      mode="day-month-year"
      minDate={{
        day: today.getDate(),
        month: today.getMonth() + 1,
        year: today.getFullYear(),
      }}
      maxDate={{
        day: nextYear.getDate(),
        month: nextYear.getMonth() + 1,
        year: nextYear.getFullYear(),
      }}
      value={bookingDate}
      onChange={setBookingDate}
    />
  );
}

minDate / maxDate take precedence over minYear / maxYear when both are set.

onValueChanging, if used, receives the column key first: (column, event) => … where column is 'day' | 'month' | 'year'. Event nativeEvent.index uses calendar indices (month 1–12 → index 0–11; day uses day-of-month minus 1).

withVirtualized

Large list virtualized demo

For large item lists (cities, timezones, country codes), wrap DrumPicker with withVirtualized to render only items near the visible window:

import { DrumPicker, withVirtualized } from 'react-native-drum-picker';

const VirtualizedDrumPicker = withVirtualized(DrumPicker);

const CITIES = ['Tashkent', 'Moscow', 'London' /* ... */]; // 1000+ items

<VirtualizedDrumPicker
  items={CITIES}
  selectedIndex={selectedIndex}
  windowSize={20} // items above + below visible area (default: 20)
  onChange={({ nativeEvent }) => setIndex(nativeEvent.index)}
/>;

windowSize controls the render buffer. Higher = smoother fast flings, higher memory. Default of 20 works for most cases.

Optional windowRecenterDebounceMs (default 100) debounces slice recentering when you reach the first or last row of the current window — this prevents scroll feedback loops during fast flings on large lists.

Platforms: iOS and Android only (wrap DrumPicker from the package — not web). The native wheel always receives a small sliced items array on both platforms.

Requirements: each entry in items must be a unique string. Duplicate labels break index recovery during slice swaps and on iOS tap hit-testing.

Not intended for DateDrumPicker (small fixed column lists).

onValueChanging is supported: indices are remapped to the full list (same as onChange), so live preview works on large lists.

Circular scroll

Enable infinite looping scroll - when user scrolls past the last item it wraps to the first:

// Minutes: 58 -> 59 -> 00 -> 01
<DrumPicker
  circular
  items={minutes}
  onChange={({ nativeEvent }) => setMinute(nativeEvent.index)}
/>

// Combine with TimeDrumPicker
<TimeDrumPicker circular />

Best for: hours, minutes, seconds, days of week, months. Not recommended for: years, long lists (cities, countries).

Note: circular and withVirtualized can be used together but for lists > 100 items prefer one or the other.

withVirtualized(DrumPicker) props

In addition to all DrumPicker props (on the wrapped instance):

PropTypeDefaultDescription
windowSizenumber20Rows rendered above and below the selection
windowRecenterDebounceMsnumber100Debounce before shifting the slice when scrolling hits the window edge

API reference

DrumPicker

PropTypeDefaultDescription
itemsT[]requiredPicker items typed by generic T (strings or objects). Display/value mapping follows built-in label resolution and/or renderItem.
selectedIndexnumber0Selected row index
itemHeightnumber44Row height (dp)
visibleItemCountnumber5Visible rows (odd recommended)
textColorstring#8E8E93Unselected text
selectedTextColorstring#1C1C1ESelected text
textSizenumber20Unselected size (sp)
selectedTextSizenumber22Selected size (sp)
backgroundColorstringtransparentRoot view background
containerBackgroundColorstringtransparentRecyclerView background
itemBackgroundColorstringtransparentRow background
showSelectionIndicatorbooleantrueCenter lines
selectionIndicatorColorstring#D1D1D6Line color
selectionIndicatorHeightnumber1Line thickness (dp)
hapticFeedbackbooleanfalseLight haptic on snap (Android + iOS)
disabledbooleanfalseBlock user drags and taps. Programmatic scrolling still works.
circularbooleanfalseEnable infinite loop scroll. Wraps last->first and first->last.
enableScrollByTapOnItembooleanfalseTap a visible row to scroll it to center (Android + iOS)
onChangefunctionnativeEvent: { index, value }
onValueChangingfunctionFires on each scroll tick while dragging. Use for live sync; debounce heavy UI work.
renderItem(info) => ReactNodeCustom row renderer. Receives { item, label, index, isSelected }.
styleViewStyleSize and layout

DateDrumPicker

PropTypeDefaultDescription
modeDateDrumPickerModeday-month-yearWhich columns to show
value{ day?, month?, year? }Controlled value
onChangefunction{ day, month, year }
onValueChangingfunction(column, event) => … while scrolling; column is day / month / year
minYearnumbernow − 100Year range start (use minDate for full date bounds)
maxYearnumbernow + 50Year range end (use maxDate for full date bounds)
minDateDateConstraintMinimum selectable date (inclusive). Partial — omit any field.
maxDateDateConstraintMaximum selectable date (inclusive). Partial — omit any field.
monthFormat'short' | 'long' | 'number'shortMonth labels
localestringenIntl locale for month names
itemHeightnumber44Passed to each column
visibleItemCountnumber5Passed to each column
textColorstringPassed to each column
selectedTextColorstringPassed to each column
textSizenumberPassed to each column
selectedTextSizenumberPassed to each column
showSelectionIndicatorbooleanPassed to each column
selectionIndicatorColorstringPassed to each column
selectionIndicatorHeightnumberPassed to each column
backgroundColorstringtransparentPassed to each column
itemBackgroundColorstringtransparentPassed to each column
containerBackgroundColorstringtransparentPassed to each column
hapticFeedbackbooleanfalsePassed to each column
disabledbooleanfalsePassed to each column
enableScrollByTapOnItembooleanfalsePassed to each column
styleViewStyleRow container
columnStyleViewStyleAll columns
columnStylesobjectPer column: day, month, year

DateDrumPicker modes

Column order is left → right:

modeColumns
dayday
monthmonth
yearyear
day-monthday, month
month-yearmonth, year
day-month-yearday, month, year
month-day-yearmonth, day, year
year-month-dayyear, month, day
type DateDrumPickerMode =
  | 'day'
  | 'month'
  | 'year'
  | 'day-month'
  | 'month-year'
  | 'day-month-year'
  | 'month-day-year'
  | 'year-month-day';

Styling

Backgrounds are transparent by default. Only text and optional indicator lines are visible.

<DrumPicker
  items={['Small', 'Medium', 'Large']}
  selectedTextColor="#111827"
  textColor="#9CA3AF"
  selectionIndicatorColor="#D1D1D6"
  backgroundColor="transparent"
  style={{ width: 120, height: 220 }}
/>

Use an odd visibleItemCount (e.g. 5) for a symmetric wheel.

Common problems

Opening the picker on a given date

The most frequent case: a section that renders the picker only while it is open, and wants it to open already showing a date. A controlled value is all you need — no ref calls, no delayed mounting, no key remount.

const [open, setOpen] = useState(false);
const [date, setDate] = useState({ day: 26, month: 7, year: 2026 });

return (
  <>
    <Button title="Pick a date" onPress={() => setOpen(true)} />
    {open ? (
      <DateDrumPicker
        mode="day-month-year"
        locale="ru"
        value={date}
        onChange={setDate}
        minDate={{ day: 1, month: 1, year: 1966 }}
        maxDate={{ day: 31, month: 12, year: 2031 }}
        itemHeight={44}
        visibleItemCount={5}
      />
    ) : null}
  </>
);

On open the wheel sits on 26 июль 2026 with its neighbours (24, 25, 26, 27, 28) already drawn, and onChange stays silent until the user actually spins something.

Mounting inside a hidden or zero-height container

Accordions, bottom sheets and {isOpen ? <Picker/> : null} all mount the picker before it has a size. Since 0.3.0 the picker parks its centering request and applies it the moment it is first laid out, so this works out of the box. Two things still matter:

  • the container must eventually reach a non-zero height — an ancestor stuck at height: 0, or display: 'none', means the picker is never measured and never becomes visible. In __DEV__ the picker warns about this after ~1.5s;
  • keep value / selectedIndex controlled across the open/close cycle; do not reset it to 0 while the section is collapsed.

Inside a React Native <Modal>

Up to 0.2.4 the picker was effectively unusable inside Modal: rows clipped, offset from the selection indicator, and swiping did nothing. Both halves of that are addressed in 0.3.0, for reasons worth spelling out because they explain the rest of this section too.

Layout. Modal hosts its children in a separate Android window under DialogRootViewGroup, whose size only reaches the shadow tree asynchronously (onSizeChanged → state update → new mount transaction). So the picker is mounted, laid out at height 0, and only gets real bounds several frames later — the same situation as a collapsed accordion, just slower. The 0.3.0 centering fix is layout-driven and applies whenever the real height arrives, however late that is.

Gestures. DialogRootViewGroup.requestDisallowInterceptTouchEvent is an empty method, so the usual "this gesture is mine" signal a scrolling view sends to its ancestors is discarded there. The picker now also calls NativeGestureUtil.notifyNativeGestureStarted, which routes through onChildStartedNativeGesture — the one channel that still reaches the touch dispatcher inside a dialog root.

Caveat: this was derived from React Native 0.86's own layout and touch plumbing, not from a reproduction on a device — the reporter's original scenario was never re-run inside a Modal. The underlying centering fix is covered by instrumented tests, the gesture handoff is not. If Modal still misbehaves for you, please open an issue with your RN version; the alternatives below are known-good either way.

If you would rather not depend on it, any of these avoids the extra window entirely:

  • an absolutely positioned overlay View inside your normal screen tree;
  • a bottom-sheet library that renders into the same root (@gorhom/bottom-sheet, react-native-modalize);
  • a dedicated screen / route.
// instead of <Modal> …
{open ? (
  <View style={StyleSheet.absoluteFill} pointerEvents="box-none">
    <Pressable style={StyleSheet.absoluteFill} onPress={() => setOpen(false)} />
    <View style={styles.sheet}>
      <DateDrumPicker value={date} onChange={setDate} />
    </View>
  </View>
) : null}

Dependent columns (days in month, ranges that change)

Changing items — a shorter month, a widened year range, a new minDate/maxDate — keeps the selected value, not the scroll position, and does not emit onChange. You do not need a ref flag to suppress events while reconfiguring props, and you do not need to widen the range in two phases.

If the selected value falls outside the new range it is clamped: the year to minDate.year…maxDate.year, then the month to that year's allowed months, then the day to that month's length. A clamp caused by your value being out of range does emit onChange, once, so controlled parents can store the corrected value.

Controlled usage

value is authoritative on every render. onChange fires only for user input, an explicit scrollToDate / scrollToIndex call, or the clamp described above — never as a side effect of changing other props.

Troubleshooting

IssueWhat to try
Must rebuild after installNative library — run a full Android rebuild
Metro shows old codenpx react-native start --reset-cache
Gradle / build errorscd android && ./gradlew clean (Windows: .\gradlew clean)
adb not foundInstall Android SDK Platform-Tools; add to PATH
Empty or white pickerEnable New Architecture; upgrade to 0.1.5+ for layout defaults; or set style={{ width, height: itemHeight * visibleItemCount }}
Wrong initial row / off-centerUpgrade to 0.3.0+; see Mounting inside a hidden or zero-height container
Only the centre row is drawnUpgrade to 0.3.0+
Value jumps when the range changesUpgrade to 0.3.0+; see Dependent columns
Clipped / unscrollable in <Modal>Upgrade to 0.3.0+; see Inside a React Native <Modal>
Props not appliedRebuild app after native changes; run yarn build in the library before packing
iOS build issuesRun pod install in example/ios; use New Architecture; see CONTRIBUTING.md
Expo GoUse prebuild + dev build; this library is not in Expo Go
RN 0.81 uiManagerType compile errorUpgrade to 0.1.3+
Crash when leaving a screenUpgrade to 0.1.4+ (safe onDetachedFromWindow with react-native-screens)
npm shows “no README”Often a registry UI lag; run npm view react-native-drum-picker readme — if content appears, hard-refresh the package page

Android build fails in Expo / React Native 0.81+

cd android
./gradlew clean

Windows:

cd android
.\gradlew clean

Then rebuild the native app (npx expo run:android or npx react-native run-android).

Crash when leaving a screen

If the app crashes when navigating away from a screen with DrumPicker (especially with react-native-screens transitions), upgrade to 0.1.4+, which avoids unsafe RecyclerView cleanup during onDetachedFromWindow.

New Architecture: This library is a Fabric view. Ensure New Architecture is enabled in your app (required for RN 0.76+).

Development & contributing

git clone https://github.com/scrollDynasty/react-native-drum-picker.git
cd react-native-drum-picker
yarn
yarn build
cd example
yarn android   # or yarn ios

Before a pull request:

yarn lint
yarn build
yarn typecheck
yarn test

Contributing: CONTRIBUTING.md — setup, where to put code and tests, what CI runs on every PR, and review checklist.
Example app: example/README.md.

License

MIT © Umar Matyokubov

Keywords

react-native

FAQs

Package last updated on 29 Jul 2026

Related posts