A responsive, touch-friendly, and modular date picker library.
Sponsored by Bloggi.
React Nice Dates is composed of a set of components and utilities with different levels of abstraction that you can use to build your own date pickers.
At the top level, you have DatePicker
and DateRangePicker
. These are slightly-opinionated components that should cover the most common use-cases. They allow displaying date inputs with a calendar popover below. They make no assumptions about how your inputs should look. Instead, they provide all the necessary props so you can render them yourself.
Here’s the most basic example using the DatePicker
component:
import React, { useState } from 'react'import { enGB } from 'date-fns/locale'import { DatePicker } from 'react-nice-dates'import 'react-nice-dates/build/style.css'function DatePickerExample() {const [date, setDate] = useState()return (<DatePicker date={date} onDateChange={setDate} locale={enGB}>{({ inputProps, focused }) => (<inputclassName={'input' + (focused ? ' -focused' : '')}{...inputProps}/>)}</DatePicker>)}
Hint: If you are using a touch device, you can also change the current month by dragging the calendar grid up or down.
Here’s a more complete example, this time using the DateRangePicker
component:
import React, { useState } from 'react'import { enGB } from 'date-fns/locale'import { DateRangePicker, START_DATE, END_DATE } from 'react-nice-dates'import 'react-nice-dates/build/style.css'function DateRangePickerExample() {const [startDate, setStartDate] = useState()const [endDate, setEndDate] = useState()return (<DateRangePickerstartDate={startDate}endDate={endDate}onStartDateChange={setStartDate}onEndDateChange={setEndDate}minimumDate={new Date()}minimumLength={1}format='dd MMM yyyy'locale={enGB}>{({ startDateInputProps, endDateInputProps, focus }) => (<div className='date-range'><inputclassName={'input' + (focus === START_DATE ? ' -focused' : '')}{...startDateInputProps}placeholder='Start date'/><span className='date-range_arrow' /><inputclassName={'input' + (focus === END_DATE ? ' -focused' : '')}{...endDateInputProps}placeholder='End date'/></div>)}</DateRangePicker>)}
Next, you have DatePickerCalendar
and DateRangePickerCalendar
, the calendar-only date picker components (used by DatePicker
and DateRangePicker
). Use these if you want to display the calendars inline or implement your own popover component, for example.
DatePickerCalendar
example:
Selected date: none.
import React, { useState } from 'react'import { format } from 'date-fns'import { enGB } from 'date-fns/locale'import { DatePickerCalendar } from 'react-nice-dates'import 'react-nice-dates/build/style.css'function DatePickerCalendarExample() {const [date, setDate] = useState()return (<div><p>Selected date: {date ? format(date, 'dd MMM yyyy', { locale: enGB }) : 'none'}.</p><DatePickerCalendar date={date} onDateChange={setDate} locale={enGB} /></div>)}
DateRangePickerCalendar
example:
Selected start date: none.
Selected end date: none.
Currently selecting: startDate.
import React, { useState } from 'react'import { format } from 'date-fns'import { enGB } from 'date-fns/locale'import { DateRangePickerCalendar, START_DATE } from 'react-nice-dates'import 'react-nice-dates/build/style.css'export default function DateRangePickerCalendarExample() {const [startDate, setStartDate] = useState()const [endDate, setEndDate] = useState()const [focus, setFocus] = useState(START_DATE)const handleFocusChange = newFocus => {setFocus(newFocus || START_DATE)}return (<div><p>Selected start date: {startDate ? format(startDate, 'dd MMM yyyy', { locale: enGB }) : 'none'}.</p><p>Selected end date: {endDate ? format(endDate, 'dd MMM yyyy', { locale: enGB }) : 'none'}.</p><p>Currently selecting: {focus}.</p><DateRangePickerCalendarstartDate={startDate}endDate={endDate}focus={focus}onStartDateChange={setStartDate}onEndDateChange={setEndDate}onFocusChange={handleFocusChange}locale={enGB}/></div>)}
Modifiers define what CSS classes are applied to each calendar day. All the components accept a modifiers
prop—an object where each key corresponds to the modifier name, and each value corresponds to a function that receives a date
parameter and must return a boolean
determining whether that modifier class should apply to that particular day.
The default modifiers are disabled
, selected
, and today
. You can also create your own modifiers by passing a modifiersClassNames
which will be matched to the modifiers
object.
import React, { useState } from 'react'import { getDay } from 'date-fns'import { enGB } from 'date-fns/locale'import { DatePickerCalendar } from 'react-nice-dates'import 'react-nice-dates/build/style.css'const modifiers = {disabled: date => getDay(date) === 6, // Disables Saturdayshighlight: date => getDay(date) === 2 // Highlights Tuesdays}const modifiersClassNames = {highlight: '-highlight'}export default function ModifiersExample() {const [date, setDate] = useState()return (<DatePickerCalendardate={date}onDateChange={setDate}locale={enGB}modifiers={modifiers}modifiersClassNames={modifiersClassNames}/>)}// In your CSS:// .nice-dates-day.-highlight { color: orange; }
If you need to implement a date-picking behavior not covered by the previous components, you can use the Calendar
component directly (DatePickerCalendar
andDateRangePickerCalendar
are themselves wrappers around this component). It accepts callbacks for when a day is clicked or hovered, which you can then use to create modifiers to control which days are selected.
import React, { useState } from 'react'import { isSameDay } from 'date-fns'import { enGB } from 'date-fns/locale'import { Calendar } from 'react-nice-dates'import 'react-nice-dates/build/style.css'// Very rough implementation of multiple date selectionexport default function CalendarExample() {const [selectedDates, setSelectedDates] = useState([])const modifiers = {selected: date => selectedDates.some(selectedDate => isSameDay(selectedDate, date))}const handleDayClick = date => {setSelectedDates([...selectedDates, date])}return (<Calendar onDayClick={handleDayClick} modifiers={modifiers} locale={enGB} />)}
You can also use the useDateInput
hook if you want to have the same date-parsing functionality on text inputs in your custom implementation.
Here’s an example using it on a standalone input:
The selected date is
import React, { useState } from 'react'import { format } from 'date-fns'import { enGB } from 'date-fns/locale'import { useDateInput } from 'react-nice-dates'export default function StandaloneInputExample() {const [date, setDate] = useState()const inputProps = useDateInput({date,format: 'yyyy-MM-dd',locale: enGB,onDateChange: setDate})const handleReset = () => {setDate(new Date())}return (<div><p>The selected date is {date && format(date, 'dd MMM yyyy', { locale: enGB })}</p><button onClick={handleReset}>Set today</button><input className='input' {...inputProps} /></div>)}
And here it is paired with DatePickerCalendar
:
The selected date is
import React, { useState } from 'react'import { format } from 'date-fns'import { enGB } from 'date-fns/locale'import { DatePickerCalendar, useDateInput } from 'react-nice-dates'import 'react-nice-dates/build/style.css'export default function DatePickerCalendarWithInputExample() {const [date, setDate] = useState()const inputProps = useDateInput({date,format: 'yyyy-MM-dd',locale: enGB,onDateChange: setDate})return (<div><p>The selected date is {date && format(date, 'dd MMM yyyy', { locale: enGB })}</p><input className='input' {...inputProps} /><DatePickerCalendar date={date} onDateChange={setDate} locale={enGB} /></div>)}
While there’s no custom time-picking user interface, you can use an input format that includes the time, and the selected time will get carried over when the selected date changes.
import React, { useState } from 'react'import { enGB } from 'date-fns/locale'import { DatePicker } from 'react-nice-dates'import 'react-nice-dates/build/style.css'function DatePickerWithTimeExample() {const [date, setDate] = useState(new Date(2020, 1, 24, 18, 15))return (<DatePicker date={date} onDateChange={setDate} locale={enGB} format='dd/MM/yyyy HH:mm'>{({ inputProps, focused }) => <input className={'input' + (focused ? ' -focused' : '')} {...inputProps} />}</DatePicker>)}
You can also use separate input for the time of the date, using the useDateInput
hook.
import React, { useState } from 'react'import { enGB } from 'date-fns/locale'import { DatePicker } from 'react-nice-dates'import 'react-nice-dates/build/style.css'function DatePickerWithTimeInputExample() {const [date, setDate] = useState(new Date(2020, 1, 24, 18, 15))const timeInputProps = useDateInput({date,format: 'HH:mm',locale: enGB,onDateChange: setDate})return (<div style={{ display: 'flex' }}><DatePicker date={date} onDateChange={setDate} locale={enGB} format='dd/MM/yyyy'>{({ inputProps, focused }) => (<input className={'input' + (focused ? ' -focused' : '')} style={{ width: 150 }} {...inputProps} />)}</DatePicker><input className='input' style={{ marginLeft: 16, width: 80 }} {...timeInputProps} /></div>)}
As you might have noticed, React Nice Dates relies of the awesome date-fns library as a peer dependency. All components require a locale
prop, which must be a date-fns locale object of your desired language.
US English:
Spanish:
import React, { useState } from 'react'import { enUS, es } from 'date-fns/locale'import { DatePicker } from 'react-nice-dates'import 'react-nice-dates/build/style.css'export default function LocalesExample() {const [date, setDate] = useState()return (<div><p>US English:</p><DatePicker date={date} onDateChange={setDate} locale={enUS}>{({ inputProps, focused }) => (<input className={'input' + (focused ? ' -focused' : '')} {...inputProps} />)}</DatePicker><p>Spanish:</p><DatePicker date={date} onDateChange={setDate} locale={es} format='dd/MM/yyyy'>{({ inputProps, focused }) => (<input className={'input' + (focused ? ' -focused' : '')} {...inputProps} placeholder='DD/MM/YYYY' />)}</DatePicker></div>)}
1. Add the react-nice-dates
and date-fns
packages to your dependencies.
With NPM:
npm install react-nice-dates date-fns --save
Or with Yarn:
yarn add react-nice-dates date-fns
2. Import the desired components, a date-fns locale object of your language, and the CSS:
import { enGB } from 'date-fns/locale'import { DatePickerCalendar } from 'react-nice-dates'import 'react-nice-dates/build/style.css'//...<DatePickerCalendar locale={enGB} />
You can use and override the compiled CSS on your project:
.nice-dates-navigation, .nice-dates-day {color: #111;}.nice-dates-popover {box-shadow: none;border: 1px solid #ddd;border-radius: 2px;max-width: 480px;transition: none;}
Or, if you’re using SASS, import the original SASS file for easier customization:
// Existing variables and their defaults$nice-dates-color-gray-dark: #333;$nice-dates-color-gray: #999;$nice-dates-color-gray-light: #ddd;$nice-dates-color-accent: $nice-dates-color-gray-dark;$nice-dates-font-size-small: 12px;$nice-dates-font-size-base: 14px;$nice-dates-font-size-big: 16px;@import 'react-nice-dates/src/style.scss';// Other overrides...
DatePicker
propschildren: func.isRequired, // ({ inputProps, focused }) => {}locale: object.isRequired,date: instanceOf(Date),onDateChange: func,format: string, // Default: locale.formatLong.date({ width: 'short' })minimumDate: instanceOf(Date), // See Calendar propsmaximumDate: instanceOf(Date), // See Calendar propsmodifiers: objectOf(func),modifiersClassNames: objectOf(string),weekdayFormat: string, // See Calendar propstouchDragEnabled: bool // See Calendar props
inputProps
properties:
onBlur,onChange,onFocus,placeholder, // Default: format.toLowerCase()readOnly, // Default: true for touch devices to avoid triggering the on-screen keyboardref,type, // Default: 'text'value
DateRangePicker
propschildren: func.isRequired, // ({ startDateInputProps, endDateInputProps, focus }) => {}locale: object.isRequired,startDate: instanceOf(Date),endDate: instanceOf(Date),onStartDateChange: func,onEndDateChange: func,format: string, // Default: locale.formatLong.date({ width: 'short' })minimumDate: instanceOf(Date), // See Calendar propsmaximumDate: instanceOf(Date), // See Calendar propsminimumLength: number, // See DateRangePickerCalendar propsmaximumLength: number, // See DateRangePickerCalendar propsmodifiers: objectOf(func),modifiersClassNames: objectOf(string),weekdayFormat: string, // See Calendar propstouchDragEnabled: bool // See Calendar props
startDateInputProps
and endDateInputProps
properties:
onBlur,onChange,onFocus,placeholder, // Default: format.toLowerCase()readOnly, // Default: true for touch devices to avoid triggering the on-screen keyboardref,type, // Default: 'text'value
DatePickerCalendar
propslocale: object.isRequired,date: instanceOf(Date),month: instanceOf(Date), // See Calendar propsonDateChange: func,onMonthChange: func, // See Calendar propsminimumDate: instanceOf(Date), // See Calendar propsmaximumDate: instanceOf(Date), // See Calendar propsmodifiers: objectOf(func),modifiersClassNames: objectOf(string),weekdayFormat: string, // See Calendar propstouchDragEnabled: bool // See Calendar props
DateRangePickerCalendar
propslocale: object.isRequired,startDate: instanceOf(Date),endDate: instanceOf(Date),focus: oneOf([START_DATE, END_DATE]),month: instanceOf(Date), // See Calendar propsonStartDateChange: func.isRequired,onEndDateChange: func.isRequired,onFocusChange: func.isRequired,onMonthChange: func, // See Calendar propsminimumDate: instanceOf(Date), // See Calendar propsmaximumDate: instanceOf(Date), // See Calendar propsminimumLength: number, // Minimum range selection length, defaults to 0maximumLength: number, // Maximum range selection length, defaults to nullmodifiers: objectOf(func),modifiersClassNames: objectOf(string),weekdayFormat: string, // See Calendar propstouchDragEnabled: bool // See Calendar props
Calendar
propslocale: object.isRequired,minimumDate: instanceOf(Date), // Days before minimumDate will be disabledmaximumDate: instanceOf(Date), // Days after maximumDate will be disabledmodifiers: objectOf(func),modifiersClassNames: objectOf(string),month: instanceOf(Date), // Optional: Turns current month into a controlled proponMonthChange: func, // Optional: Turns current month into a controlled proponDayHover: func,onDayClick: func,weekdayFormat: string, // Optional: allows weekday to be dynamically formatted (ex. "EEEEE")touchDragEnabled: bool // Default: true
useDateInput
const {onBluronChangeonFocusplaceholder // Default: format.toLowerCase(),type // 'text'value} = useDateInput({date, // Current dateformat, // Default: locale.formatLong.date({ width: 'short' })locale, // date-fns locale objectminimumDate, // Dates before minimumDate won’t be validmaximumDate, // Dates after maximumDate won’t be validonDateChange, // Function to call when a valid date is typedvalidate // Custom date validation function. Recieves a date and must return a boolean.})