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

@daboss2003/react-native-calender-range-picker

v1.0.1

Published

Mobile-first, declarative date / range picker for React Native with tap- and drag-to-select, theming, virtualized vertical months, and worklet-driven gestures.

Readme

@daboss2003/react-native-calender-range-picker

Mobile-first, declarative date & range picker for React Native.

  • Single tap or drag to select a range — no separate mode toggle.
  • Vertical, virtualized month list (feels like the iOS Calendar app).
  • Worklet-driven gestures via react-native-reanimated and react-native-worklets — pan events stay on the UI thread.
  • Continuous range bar with rounded endpoints (not pill-per-day).
  • Single theme object — full color control, no dozen-prop API.
  • Zero date-library dependency — pure JS date math.
  • TypeScript first.

Install

npm install @daboss2003/react-native-calender-range-picker \
  react-native-gesture-handler \
  react-native-reanimated \
  react-native-worklets

App setup

  1. Wrap your root in GestureHandlerRootView:
import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      {/* ... */}
    </GestureHandlerRootView>
  );
}
  1. Make sure the worklets babel plugin is active.

    • Expo (SDK 50+) — nothing to do. babel-preset-expo already includes react-native-worklets/plugin automatically.

    • Bare React Native (Community CLI) — add it to your babel.config.js:

      module.exports = {
        presets: ['module:@react-native/babel-preset'],
        plugins: ['react-native-worklets/plugin'], // must be last in the plugins array
      };

    react-native-worklets/plugin replaces the old react-native-reanimated/plugin for Reanimated 4+.

Usage

Range mode (default)

import { CalendarRangePicker, type DateRange } from '@daboss2003/react-native-calender-range-picker';

function Screen() {
  return (
    <CalendarRangePicker
      onChange={(value) => {
        const range = value as DateRange;
        console.log(range.start, range.end);
      }}
    />
  );
}

Tap once to set the start, tap again for the end. Long-press + drag to select a range in one gesture.

Single mode

<CalendarRangePicker
  mode="single"
  onChange={(value) => console.log(value as Date)}
/>

With bounds and disabled dates

<CalendarRangePicker
  mode="range"
  minDate={new Date(2026, 0, 1)}
  maxDate={new Date(2026, 11, 31)}
  disabledDates={[new Date(2026, 4, 25)]} // or a predicate (d) => boolean
  onChange={(v) => setRange(v as DateRange)}
/>

Theming

<CalendarRangePicker
  theme={{
    primary: '#FF6B6B',
    onPrimary: '#fff',
    rangeFill: '#FFE3E3',
    text: '#1A1A1A',
    headerText: '#1A1A1A',
    weekdayText: '#8A8A8A',
    todayBorder: '#FF6B6B',
  }}
/>

All theme keys are optional — anything you omit falls back to defaultTheme.

Imperative ref

const ref = useRef<CalendarRangePickerRef>(null);

<CalendarRangePicker ref={ref} />

ref.current?.scrollToToday();
ref.current?.scrollToDate(new Date(2027, 0, 1));
ref.current?.reset();

API

<CalendarRangePicker>

| Prop | Type | Default | Description | |---|---|---|---| | mode | "single" \| "range" | "range" | Selection mode. | | initialDate | Date | — | Initial value when mode="single". Optional. | | initialRange | { start: Date; end: Date } | — | Initial value when mode="range". Optional. | | minDate | Date | 24 months before today | Earliest selectable / visible day. | | maxDate | Date | 24 months after today | Latest selectable / visible day. | | disabledDates | Date[] \| (d: Date) => boolean | — | Disabled days. | | onChange | (v: Date \| DateRange) => void | — | Fires on completed selection. | | theme | Partial<Theme> | defaultTheme | Color overrides (deep-merged). | | locale | string | "en" | BCP-47 locale for month/weekday labels. | | firstDayOfWeek | 0..6 | 0 | 0 = Sunday, 1 = Monday, … | | monthsBefore | number | 24 | How many months back to render when minDate is omitted. | | monthsAfter | number | 24 | How many months forward when maxDate is omitted. | | style | ViewStyle | — | Container style. |

Ref API

type CalendarRangePickerRef = {
  scrollToDate: (date: Date, animated?: boolean) => void;
  scrollToToday: (animated?: boolean) => void;
  reset: () => void;
};

Theme

type Theme = {
  primary: string;       // selected day fill + today ring
  onPrimary: string;     // text on selected day
  rangeFill: string;     // continuous range bar color
  text: string;          // in-month day text
  mutedText: string;     // spill-over day text (prev/next month)
  disabled: string;      // disabled day text
  background: string;    // overall background
  headerText: string;    // "May 2026" header
  weekdayText: string;   // "Mon Tue Wed..." row
  todayBorder: string;   // ring around today (unselected)
};

How it works

  • Months are rendered in a vertical, virtualized Animated.SectionList. Only ~3 months stay mounted at a time.
  • Each Day cell is React.memo'd — re-renders only when its own selection state changes.
  • Pan gestures use Reanimated 4 worklets: hit-testing (finger → cell → date) runs on the UI thread. Only when the targeted cell changes does it schedule a JS update via scheduleOnRN.
  • No date library — dateUtils.ts provides everything needed (~70 lines).

Example app

A runnable Expo example lives in example/. From the repo root:

cd example
npm install
npm run ios     # builds a native dev client (first run: ~5–15 min)

Why run:ios instead of start + Expo Go? Reanimated + Worklets + Gesture Handler with the New Architecture require their native modules to be linked into a custom dev client. The vanilla Expo Go binary does not load RNGestureHandlerModule / WorkletsModule under bridgeless mode, so it will red-screen at startup. A one-time expo run:ios build fixes this; subsequent reloads use npm start and are instant.

Heads-up: avoid duplicate copies of native modules

If you consume this library from a sibling directory via "file:.." (or in a monorepo / workspace setup), make sure your library directory does not have its own node_modules containing different versions of react-native-gesture-handler, react-native-reanimated, or react-native-worklets. Two copies = the JS side imports one, the native binary registered the other, and you get:

TurboModuleRegistry.getEnforcing(...): 'RNGestureHandlerModule' could not be found.

Fix: either rm -rf <library-root>/node_modules so the app's copy is the only one, or pin matching versions in your consumer app's package.json overrides. The gesture-handler troubleshooting docs cover the same scenario.

License

MIT © daboss2003