React Nice Dates

A responsive, touch-friendly, and modular date picker library.

Overview

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:

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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 }) => (
<input
className={'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:

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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 (
<DateRangePicker
startDate={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'>
<input
className={'input' + (focus === START_DATE ? ' -focused' : '')}
{...startDateInputProps}
placeholder='Start date'
/>
<span className='date-range_arrow' />
<input
className={'input' + (focus === END_DATE ? ' -focused' : '')}
{...endDateInputProps}
placeholder='End date'
/>
</div>
)}
</DateRangePicker>
)
}

Customizing how the calendar appears

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.

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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.

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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>
<DateRangePickerCalendar
startDate={startDate}
endDate={endDate}
focus={focus}
onStartDateChange={setStartDate}
onEndDateChange={setEndDate}
onFocusChange={handleFocusChange}
locale={enGB}
/>
</div>
)
}

Customizing days

Modifiers define what CSS classes are applied to each calendar day. All the components accept a modifiersprop—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.

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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 Saturdays
highlight: date => getDay(date) === 2 // Highlights Tuesdays
}
const modifiersClassNames = {
highlight: '-highlight'
}
export default function ModifiersExample() {
const [date, setDate] = useState()
return (
<DatePickerCalendar
date={date}
onDateChange={setDate}
locale={enGB}
modifiers={modifiers}
modifiersClassNames={modifiersClassNames}
/>
)
}
// In your CSS:
// .nice-dates-day.-highlight { color: orange; }

Implementing your own date-picking behavior

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.

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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 selection
export 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} />
)
}

Parsing text inputs

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

July
MonTueWedThuFriSatSun
282930Jul12345678910111213141516171819202122232425262728293031Aug12345678
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>
)
}

Time-picking

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.

February 2020
MonTueWedThuFriSatSun
2728293031Feb1234567891011121314151617181920212223242526272829Mar12345678
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.

February 2020
MonTueWedThuFriSatSun
2728293031Feb1234567891011121314151617181920212223242526272829Mar12345678
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>
)
}

Localization

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:

July
SunMonTueWedThuFriSat
27282930Jul12345678910111213141516171819202122232425262728293031Aug1234567

Spanish:

julio
lunmarmiéjueviesabdom
282930jul12345678910111213141516171819202122232425262728293031ago12345678
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>
)
}

Installation

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} />

Style customization

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...

API Reference

DatePicker props

children: 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 props
maximumDate: instanceOf(Date), // See Calendar props
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
weekdayFormat: string, // See Calendar props
touchDragEnabled: 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 keyboard
ref,
type, // Default: 'text'
value

DateRangePicker props

children: 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 props
maximumDate: instanceOf(Date), // See Calendar props
minimumLength: number, // See DateRangePickerCalendar props
maximumLength: number, // See DateRangePickerCalendar props
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
weekdayFormat: string, // See Calendar props
touchDragEnabled: 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 keyboard
ref,
type, // Default: 'text'
value

DatePickerCalendar props

locale: object.isRequired,
date: instanceOf(Date),
month: instanceOf(Date), // See Calendar props
onDateChange: func,
onMonthChange: func, // See Calendar props
minimumDate: instanceOf(Date), // See Calendar props
maximumDate: instanceOf(Date), // See Calendar props
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
weekdayFormat: string, // See Calendar props
touchDragEnabled: bool // See Calendar props

DateRangePickerCalendar props

locale: object.isRequired,
startDate: instanceOf(Date),
endDate: instanceOf(Date),
focus: oneOf([START_DATE, END_DATE]),
month: instanceOf(Date), // See Calendar props
onStartDateChange: func.isRequired,
onEndDateChange: func.isRequired,
onFocusChange: func.isRequired,
onMonthChange: func, // See Calendar props
minimumDate: instanceOf(Date), // See Calendar props
maximumDate: instanceOf(Date), // See Calendar props
minimumLength: number, // Minimum range selection length, defaults to 0
maximumLength: number, // Maximum range selection length, defaults to null
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
weekdayFormat: string, // See Calendar props
touchDragEnabled: bool // See Calendar props

Calendar props

locale: object.isRequired,
minimumDate: instanceOf(Date), // Days before minimumDate will be disabled
maximumDate: instanceOf(Date), // Days after maximumDate will be disabled
modifiers: objectOf(func),
modifiersClassNames: objectOf(string),
month: instanceOf(Date), // Optional: Turns current month into a controlled prop
onMonthChange: func, // Optional: Turns current month into a controlled prop
onDayHover: func,
onDayClick: func,
weekdayFormat: string, // Optional: allows weekday to be dynamically formatted (ex. "EEEEE")
touchDragEnabled: bool // Default: true

useDateInput

const {
onBlur
onChange
onFocus
placeholder // Default: format.toLowerCase(),
type // 'text'
value
} = useDateInput({
date, // Current date
format, // Default: locale.formatLong.date({ width: 'short' })
locale, // date-fns locale object
minimumDate, // Dates before minimumDate won’t be valid
maximumDate, // Dates after maximumDate won’t be valid
onDateChange, // Function to call when a valid date is typed
validate // Custom date validation function. Recieves a date and must return a boolean.
})