react-native-multicalendar-picker
v0.1.0-beta.1
Published
React Native single-date and range picker with 13 calendars, Persian/Arabic/English localization, RTL, themes and custom fonts
Downloads
376
Maintainers
Readme
react-native-multicalendar-picker
A controlled React Native date and date-range picker with 13 calendar systems, configurable calendar segments, Persian/English/Arabic UI, RTL, light/dark themes, and custom typography. Values stay in Gregorian ISO format when the visible calendar changes.
راهنمای فارسی · API reference · Release notes
Features
- Single-date, optional-date and inclusive start/end range selection.
- Calendar segments generated from your
calendarslist. - Month/year menus, descending years and persistent browsing highlights.
- Date limits, whole-year limits and optional future-date restrictions.
- Range highlighting and built-in date/range shortcuts.
- Locale-aware labels, digits, week starts and RTL/LTR layout.
- System/light/dark themes, semantic colors, custom fonts and proportional sizing.
- Optional descriptions supplied by your own cultural-event data.
- TypeScript definitions; no network calls, Moment dependency or required provider.
Installation
The first release is available on the beta channel:
npm install react-native-multicalendar-picker@beta react-native-svgFor a bare React Native iOS app, install pods after adding react-native-svg:
cd ios
pod installThe picker adds no native module of its own. Its included chevrons use
react-native-svg; no SVG transformer is needed. If your app already has that peer
installed and linked, no extra native setup is required for this picker.
| Requirement | Supported range |
| ---------------- | ---------------------------------------------------------- |
| React | >=18.2.0 <20 |
| React Native | >=0.77.0 <1 |
| react-native-svg | >=15.0.0 <16 |
| Intl | DateTimeFormat data for the selected calendar and locale |
The tested baseline is React 18.3.1 / React Native 0.77.3. Runtime results include Android API 36 and iOS 26.2 simulators. These checks do not establish compatibility with every version in the peer ranges or with every physical device.
Select a date
import {useState} from 'react';
import {ScrollView} from 'react-native';
import {
MultiCalendarPicker,
localToday,
} from 'react-native-multicalendar-picker';
export default function DateExample() {
const [date, setDate] = useState(localToday);
return (
<ScrollView>
<MultiCalendarPicker
value={date}
onChange={setDate}
calendars={['persian', 'gregory']}
locale="fa-IR"
theme="system"
allowFutureDates={false}
/>
</ScrollView>
);
}date and onChange always use a Gregorian ISO date-only string such as
2026-10-17, even when the UI displays ۱۴۰۵/۰۷/۲۵. Do not convert date-only
values through new Date(iso) and UTC/local timestamp arithmetic. Use the exported
calendarDate helper to read a value in a different calendar.
The component is inline. Place it in your own screen, modal or bottom sheet. For confirm/cancel behavior, keep a draft value in the host and commit it only when the user confirms. Allow vertical scrolling, especially with large text or ranges.
Select a range
import {useState} from 'react';
import {ScrollView, Text} from 'react-native';
import {MultiCalendarPicker} from 'react-native-multicalendar-picker';
import type {DateRange} from 'react-native-multicalendar-picker';
export default function RangeExample() {
const [range, setRange] = useState<DateRange | null>(null);
const complete = range !== null && range.end !== null;
return (
<ScrollView>
<MultiCalendarPicker
selectionMode="range"
value={range}
onChange={setRange}
clearable
calendars={['persian', 'gregory']}
locale="fa-IR"
allowFutureDates={false}
/>
<Text>{complete ? 'Range ready' : 'Choose both dates'}</Text>
</ScrollView>
);
}The first tap emits {start: '2026-10-12', end: null}. The second tap emits the
complete inclusive range, for example
{start: '2026-10-12', end: '2026-10-17'}. Update the controlled state after each
callback, including the pending start. Earlier second taps are sorted; tapping the
same day twice creates a one-day range. A tap after completion starts a new range.
Clearing emits null.
Both endpoints and the day band survive calendar switches. Range shortcuts select Today, Last 7 days, Last 30 days, or the active calendar's month through today.
Optional dates
const [date, setDate] = useState<string | null>(null);
<MultiCalendarPicker
clearable
value={date}
onChange={setDate}
locale="fa-IR"
/>;Single-date null values require clearable. Required fields retain a string-only
setter. In range mode, an empty null range is always valid; clearable controls
whether the clear button is shown.
Limit dates and years
<MultiCalendarPicker
value={date}
onChange={setDate}
calendars={['persian', 'gregory']}
locale="fa-IR"
yearCalendar="persian"
minYear={1391}
maxYear={1405}
allowFutureDates={false}
/>minDate/maxDate are inclusive Gregorian ISO bounds. minYear/maxYear use
complete years in yearCalendar (Gregorian by default). Reference bounds stay
fixed when switching segments. For a rolling 15-year interval, derive the current
year with calendarDate(localToday(), 'persian').year, then use currentYear - 14
and currentYear.
Future dates are allowed by default; set allowFutureDates={false} to cap selection
at today. All date/year/future limits intersect. An empty intersection throws
RangeError. Navigation stays within overlapping periods. See the
bound semantics for partial years and eras.
Fonts, sizing and themes
<MultiCalendarPicker
value={date}
onChange={setDate}
fontFamily="Vazirmatn-Regular"
fontSize={14}
theme="dark"
colors={{accent: '#24cbbb', rangeBackground: '#194d47'}}
/>Register custom fonts in the host app first. fontSize defaults to 14 dp and
scales text roles, icons and spacing proportionally; it overrides
textStyle.fontSize. Native text scaling remains enabled. Day circles and range
bands fit the measured seven-column grid. Theme colors and font assets belong to
the consuming app; no font is bundled or downloaded.
Supported calendars
| Identifier | Calendar |
| ------------------ | ------------------------------------- |
| gregory | Gregorian |
| persian | Persian / Solar Hijri |
| islamic-civil | Islamic civil |
| islamic-tbla | Islamic tabular |
| islamic-umalqura | Umm al-Qura |
| hebrew | Hebrew, including 13-month leap years |
| buddhist | Buddhist |
| japanese | Japanese imperial eras |
| roc | Taiwan / Republic of China |
| indian | Indian national / Saka |
| coptic | Coptic |
| ethiopic | Ethiopic |
| ethioaa | Ethiopic Amete Alem |
Calendar arithmetic is provided by @internationalized/date. This list does not
include every historical calendar. Islamic calculation variants may differ from
local moon-sighting announcements. Engine ranges and era restrictions apply;
Japanese dates before 1868 are unsupported. The runtime must provide matching Intl
calendar data; unsupported formatting throws instead of silently showing Gregorian
labels. All engine implementations are bundled, regardless of visible segments.
Localization and events
UI messages ship in Persian, English and Arabic. Other locales use Intl-formatted dates with English UI messages until you supply overrides:
messages={{today: 'Aujourd’hui', calendars: {gregory: 'Grégorien'}}}Optional event descriptions come from a synchronous getEvents(iso, locale)
callback. Fetch/cache data in the host app. No official holiday feed or remote
service is bundled. See the event example.
Documentation
- Full API, types, callbacks, bounds and customization
- راهنمای فارسی و مثالهای استفاده
- Development and release workflow
- Changes in this release
The package includes source, compiled JavaScript and TypeScript declarations. Native button labels/selected states and simulator flows have been checked; spoken VoiceOver/TalkBack output and physical-device coverage remain incomplete in this beta.
License
MIT, Copyright 2026 Mohammad Kasiri.
