- Smooth and fast cross platform Material Design date picker and time picker for (react-native-paper)
- Tested on Android, iOS and the web
- Uses the native Date.Intl API's which work out of the box on the web / iOS an on Android with Hermes from RN version 0.66 (automatic day name, month translations without bundle size increase)
- For RN below 0.66 see for Android Intl support the android-caveats guide
- Simple API
- Typesafe
- Endless (virtual) scrolling
- Performant
- Great React Native Web support
- Dependencies are react-native-paper
View video in better frame on YouTube
Web demo: reactnativepaperdates.com
We want developers to be able to build software faster using modern tools like GraphQL, Golang and React Native.
Give us a follow on Twitter: RichardLindhout, web_ridge
Please contribute or donate so we can spend more time on this library
First install and follow the guides at react-native-paper
Yarn
yarn add react-native-paper-dates
npm
npm install react-native-paper-dates --save
Ideally you do this somewhere in your index.js
before react-native-paper-dates
is used.
Currently we have en/nl/de/pl/pt translations but it's really easy to add one extra since it are only some labels and error messages.
// e.g in your index.js
import {
// en,
// nl,
// de,
// pl,
// pt,
enGB,
registerTranslation,
} from 'react-native-paper-dates'
// registerTranslation('en', en)
// registerTranslation('nl', nl)
// registerTranslation('pl', pl)
// registerTranslation('pt', pt)
// registerTranslation('de', de)
registerTranslation('en-GB', enGB)
Please send a PR with your language to make sure all locales are there next time
import {
registerTranslation,
} from 'react-native-paper-dates'
registerTranslation("pl", {
save: 'Save',
selectSingle: 'Select date',
selectMultiple: 'Select dates',
selectRange: 'Select period',
notAccordingToDateFormat: (inputFormat) =>
`Date format must be ${inputFormat}`,
mustBeHigherThan: (date) => `Must be later then ${date}`,
mustBeLowerThan: (date) => `Must be earlier then ${date}`,
mustBeBetween: (startDate, endDate) =>
`Must be between ${startDate} - ${endDate}`,
dateIsDisabled: 'Day is not allowed',
previous: 'Previous',
next: 'Next',
typeInDate: 'Type in date',
pickDateFromCalendar: 'Pick date from calendar',
close: 'Close',
})
import * as React from 'react';
import { Button } from 'react-native-paper';
import { DatePickerModal } from 'react-native-paper-dates';
export default function ReadMeExampleSingle() {
const [date, setDate] = React.useState<Date | undefined>(undefined);
const [open, setOpen] = React.useState(false);
const onDismissSingle = React.useCallback(() => {
setOpen(false);
}, [setOpen]);
const onConfirmSingle = React.useCallback(
(params) => {
setOpen(false);
setDate(params.date);
},
[setOpen, setDate]
);
return (
<>
<Button onPress={() => setOpen(true)} uppercase={false} mode="outlined">
Pick single date
</Button>
<DatePickerModal
locale="en"
mode="single"
visible={open}
onDismiss={onDismissSingle}
date={date}
onConfirm={onConfirmSingle}
// validRange={{
// startDate: new Date(2021, 1, 2), // optional
// endDate: new Date(), // optional
// disabledDates: [new Date()] // optional
// }}
// onChange={} // same props as onConfirm but triggered without confirmed by user
// saveLabel="Save" // optional
// uppercase={false} // optional, default is true
// label="Select date" // optional
// animationType="slide" // optional, default is 'slide' on ios/android and 'none' on web
/>
</>
);
}
import * as React from 'react';
import { Button } from 'react-native-paper';
import { DatePickerModal } from 'react-native-paper-dates';
export default function ReadMeExampleRange() {
const [range, setRange] = React.useState<{
startDate: Date | undefined;
endDate: Date | undefined;
}>({ startDate: undefined, endDate: undefined });
const [open, setOpen] = React.useState(false);
const onDismiss = React.useCallback(() => {
setOpen(false);
}, [setOpen]);
const onConfirm = React.useCallback(
({ startDate, endDate }) => {
setOpen(false);
setRange({ startDate, endDate });
},
[setOpen, setRange]
);
return (
<>
<Button onPress={() => setOpen(true)} uppercase={false} mode="outlined">
Pick range
</Button>
<DatePickerModal
locale="en"
mode="range"
visible={open}
onDismiss={onDismiss}
startDate={range.startDate}
endDate={range.endDate}
onConfirm={onConfirm}
// validRange={{
// startDate: new Date(2021, 1, 2), // optional
// endDate: new Date(), // optional
// disabledDates: [new Date()] // optional
// }}
// onChange={} // same props as onConfirm but triggered without confirmed by user
// saveLabel="Save" // optional
// uppercase={false} // optional, default is true
// label="Select period" // optional
// startLabel="From" // optional
// endLabel="To" // optional
// animationType="slide" // optional, default is slide on ios/android and none on web
/>
</>
);
}
import * as React from 'react';
import { Button } from 'react-native-paper';
import { DatePickerModal } from 'react-native-paper-dates';
export default function ReadMeExampleMultiple() {
const [dates, setDates] = React.useState<Date[] | undefined>();
const [open, setOpen] = React.useState(false);
const onDismiss = React.useCallback(() => {
setOpen(false);
}, [setOpen]);
const onConfirm = React.useCallback((params) => {
setOpen(false);
setDates(params.dates);
console.log('[on-change-multi]', params);
}, []);
return (
<>
<Button onPress={() => setOpen(true)} uppercase={false} mode="outlined">
Pick multiple dates
</Button>
<DatePickerModal
locale="en"
mode="multiple"
visible={open}
onDismiss={onDismiss}
dates={dates}
onConfirm={onConfirm}
// moreLabel="More"
// validRange={{
// startDate: new Date(2021, 1, 2), // optional
// endDate: new Date(), // optional
// disabledDates: [new Date()] // optional
// }}
// saveLabel="Save" // optional
// uppercase={false} // optional, default is true
// label="Select period" // optional
// startLabel="From" // optional
// endLabel="To" // optional
// animationType="slide" // optional, default is slide on ios/android and none on web
/>
</>
);
}
export default function ReadMeExampleDatePickerInput() {
const [inputDate, setInputDate] = React.useState<Date | undefined>(undefined)
return (
<>
<DatePickerInput
locale="en"
label="Birthdate"
value={inputDate}
onChange={(d) => setInputDate(d)}
inputMode="start"
// mode="outlined" (see react-native-paper docs)
// other react native TextInput props
/>
</>
)
}
import * as React from 'react'
import { Button } from 'react-native-paper'
import { TimePickerModal } from 'react-native-paper-dates'
export default function TimePickerPage() {
const [visible, setVisible] = React.useState(false)
const onDismiss = React.useCallback(() => {
setVisible(false)
}, [setVisible])
const onConfirm = React.useCallback(
({ hours, minutes }) => {
setVisible(false);
console.log({ hours, minutes });
},
[setVisible]
);
return (
<>
<TimePickerModal
visible={visible}
onDismiss={onDismiss}
onConfirm={onConfirm}
hours={12} // default: current hours
minutes={14} // default: current minutes
label="Select time" // optional, default 'Select time'
uppercase={false} // optional, default is true
cancelLabel="Cancel" // optional, default: 'Cancel'
confirmLabel="Ok" // optional, default: 'Ok'
animationType="fade" // optional, default is 'none'
locale="en" // optional, default is automically detected by your system
/>
<Button onPress={()=> setVisible(true)}>
Pick time
</Button>
</>
)
}
Things on our roadmap have labels with enhancement
.
https://github.com/web-ridge/react-native-paper-dates/issues
- Use 0.14+ version of React-Native-Web (Modal and better number input)
- Try to avoid putting the Picker Modals inside of a scrollView If that is not possible use the following props on the surrounding ScrollViews/Flatlists
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="handled"
contentInsetAdjustmentBehavior="always"
This is to prevent the need to press 2 times before save or close button in modal works (1 press for closing keyboard, 1 press for confirm/close) React Native Issue: #10138
We recommend Hermes with React Native >= 0.66 you won't need these polyfills at all!
You will need to add a polyfill for the Intl API on Android if:
- You have Hermes enabled and are below React Native 0.66
- You have Hermes disabled and you want to support locales outside of en-US and you don't have the org.webkit:android-jsc-intl:+ variant enabled in your app/build.gradle
Install polyfills with Yarn
yarn add react-native-localize @formatjs/intl-pluralrules @formatjs/intl-getcanonicallocales @formatjs/intl-listformat @formatjs/intl-displaynames @formatjs/intl-locale @formatjs/intl-datetimeformat @formatjs/intl-numberformat @formatjs/intl-relativetimeformat
or npm
npm install react-native-localize @formatjs/intl-pluralrules @formatjs/intl-getcanonicallocales @formatjs/intl-listformat @formatjs/intl-displaynames @formatjs/intl-locale @formatjs/intl-datetimeformat @formatjs/intl-numberformat @formatjs/intl-relativetimeformat --save
If using Expo, omit react-native-localize
and use expo install expo-localization
instead. Read more here.
In your app starting entrypoint e.g. ./index.js
or even better use a index.android.js
to prevent importing on iOS/web put the following code. (don't forget to import the languages you want to support, in the example only english language is supported)
// on top of your index.android.js file
const isAndroid = require('react-native').Platform.OS === 'android'; // this line is only needed if you don't use an .android.js file
const isHermesEnabled = !!global.HermesInternal; // this line is only needed if you don't use an .android.js file
// in your index.js file
if (isHermesEnabled || isAndroid) { // this line is only needed if you don't use an .android.js file
require('@formatjs/intl-getcanonicallocales/polyfill');
require('@formatjs/intl-locale/polyfill');
require('@formatjs/intl-pluralrules/polyfill');
require('@formatjs/intl-pluralrules/locale-data/en.js'); // USE YOUR OWN LANGUAGE OR MULTIPLE IMPORTS YOU WANT TO SUPPORT
require('@formatjs/intl-displaynames/polyfill');
require('@formatjs/intl-displaynames/locale-data/en.js'); // USE YOUR OWN LANGUAGE OR MULTIPLE IMPORTS YOU WANT TO SUPPORT
require('@formatjs/intl-listformat/polyfill');
require('@formatjs/intl-listformat/locale-data/en.js'); // USE YOUR OWN LANGUAGE OR MULTIPLE IMPORTS YOU WANT TO SUPPORT
require('@formatjs/intl-numberformat/polyfill');
require('@formatjs/intl-numberformat/locale-data/en.js'); // USE YOUR OWN LANGUAGE OR MULTIPLE IMPORTS YOU WANT TO SUPPORT
require('@formatjs/intl-relativetimeformat/polyfill');
require('@formatjs/intl-relativetimeformat/locale-data/en.js'); // USE YOUR OWN LANGUAGE OR MULTIPLE IMPORTS YOU WANT TO SUPPORT
require('@formatjs/intl-datetimeformat/polyfill');
require('@formatjs/intl-datetimeformat/locale-data/en.js'); // USE YOUR OWN LANGUAGE OR MULTIPLE IMPORTS YOU WANT TO SUPPORT
require('@formatjs/intl-datetimeformat/add-golden-tz.js');
// https://formatjs.io/docs/polyfills/intl-datetimeformat/#default-timezone
if ('__setDefaultTimeZone' in Intl.DateTimeFormat) {
// If you are using react-native-cli
let RNLocalize = require('react-native-localize');
Intl.DateTimeFormat.__setDefaultTimeZone(RNLocalize.getTimeZone());
// Are you using Expo, use this instead of previous 2 lines
// Intl.DateTimeFormat.__setDefaultTimeZone(
// require("expo-localization").timezone
// );
}
} // this line is only needed if you don't use an .android.js file
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
- Simple form library for React Native with great UX for developer and end-user react-native-use-form
- Smooth and fast cross platform Material Design Tabs for React Native Paper: react-native-paper-tabs
- Simple translations in React (Native): react-ridge-translations
- Simple global state management in React (Native): react-ridge-state
- 1 command utility for React Native (Web) project: create-react-native-web-application