React Datepicker
A powerful and flexible date picker component built on top of react-datepicker. Seamlessly integrated with RizzUI's design system, this component provides a consistent and accessible way to select dates, times, and date ranges in your application.
Features
- 📅 Multiple Selection Modes - Single date, date range, month, year, and time-only pickers
- 🎨 Theme Integration - Fully integrated with RizzUI theme colors and design tokens
- 🌓 Dark Mode Support - Automatic dark mode styling using RizzUI theme variables
- ♿ Accessible - Built with accessibility best practices and keyboard navigation
- 🎯 Type Safe - Full TypeScript support with comprehensive type definitions
- 📱 Responsive - Works seamlessly across all device sizes
- 🎛️ Customizable - Extensive customization options for dates, formats, and behavior
- 🔄 Form Integration - Works seamlessly with form libraries like React Hook Form
Installation
Before using the React Datepicker component, you'll need to install the required dependencies:
Step 1
Install the react-datepicker and @types/react-datepicker packages.
- npm
- yarn
- pnpm
- bun
npm install react-datepicker
npm install --save-dev @types/react-datepicker
yarn add react-datepicker
yarn add -D @types/react-datepicker
pnpm add react-datepicker
pnpm add -D @types/react-datepicker
bun add react-datepicker
bun add -d @types/react-datepicker
Step 2
Create a DatePicker component, components/date-picker.tsx
import React from 'react';
import { tv } from 'tailwind-variants';
import { Input, InputProps } from 'rizzui/input';
import { cn } from 'rizzui';
import DatePicker, { type DatePickerProps } from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { CalendarIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
const datePicker = tv({
slots: {
calendar:
'[&.react-datepicker]:!shadow-lg [&.react-datepicker]:!border-[var(--border-color)] [&.react-datepicker]:!rounded-[var(--border-radius)]',
monthContainer:
'[&.react-datepicker>div]:!pt-5 [&.react-datepicker>div]:!pb-3',
prevNextButton:
'[&.react-datepicker>button]:!items-baseline [&.react-datepicker>button]:!top-7 [&.react-datepicker>button]:!border [&.react-datepicker>button]:!border-solid [&.react-datepicker>button]:!border-[var(--border-color)] [&.react-datepicker>button]:!rounded-[var(--border-radius)] [&.react-datepicker>button]:!h-[22px] [&.react-datepicker>button]:!w-[22px] hover:[&.react-datepicker>button]:!border-[var(--text-primary)] [&.react-datepicker>button:hover>span::before]:!border-[var(--text-primary)]',
prevNextButtonChild:
'[&.react-datepicker>button>span]:!top-0 [&.react-datepicker>button>span]:!before:border-t-[1.5px] [&.react-datepicker>button>span]:!before:border-r-[1.5px] [&.react-datepicker>button>span]:!before:border-[var(--muted-foreground)] [&.react-datepicker>button>span]:!before:h-[7px] [&.react-datepicker>button>span]:!before:w-[7px]',
timeOnly:
'[&.react-datepicker--time-only>div]:!pr-0 [&.react-datepicker--time-only>div]:!w-28',
popper:
'[&>svg]:!fill-[var(--background)] dark:[&>svg]:!fill-[var(--muted)] [&>svg]:!stroke-[var(--border-color)] dark:[&>svg]:!stroke-[var(--muted)] dark:[&>svg]:!text-[var(--muted)]',
},
});
export type ReactDatePickerProps = DatePickerProps & {
inputProps?: InputProps;
};
const ReactDatePicker = ({
inputProps,
customInput,
onCalendarOpen,
onCalendarClose,
popperClassName,
calendarClassName,
...props
}: ReactDatePickerProps) => {
const [isCalenderOpen, setIsCalenderOpen] = React.useState(false);
const handleCalenderOpen = () => setIsCalenderOpen(true);
const handleCalenderClose = () => setIsCalenderOpen(false);
const {
calendar: calendarClass,
monthContainer: monthContainerClass,
prevNextButton: prevNextButtonClass,
prevNextButtonChild: prevNextButtonChildClass,
timeOnly: timeOnlyClass,
popper: popperClass,
} = datePicker();
return (
<DatePicker
customInput={
customInput || (
<Input
prefix={
<CalendarIcon className="w-5 h-5 text-[var(--muted-foreground)]" />
}
suffix={
<ChevronDownIcon
className={cn(
'h-4 w-4 text-[var(--muted-foreground)] transition',
isCalenderOpen && 'rotate-180'
)}
/>
}
{...inputProps}
/>
)
}
onCalendarOpen={onCalendarOpen || handleCalenderOpen}
onCalendarClose={onCalendarClose || handleCalenderClose}
calendarClassName={cn(
calendarClass(),
monthContainerClass(),
prevNextButtonClass(),
prevNextButtonChildClass(),
timeOnlyClass(),
calendarClassName
)}
popperClassName={cn(popperClass(), popperClassName)}
{...props}
/>
);
};
ReactDatePicker.displayName = 'ReactDatePicker';
export default ReactDatePicker;
Step 3
Copy and paste the following CSS code into your project CSS file./* React Datepicker Styling */
/* Uses RizzUI theme colors and design tokens */
/* Main calendar container */
.react-datepicker-popper .react-datepicker {
@apply bg-[var(--background)] text-[var(--text-secondary)] dark:border-[var(--muted)] dark:bg-[var(--muted)];
}
/* Month container */
.react-datepicker .react-datepicker__month-container {
@apply px-3;
}
/* Time container */
.react-datepicker .react-datepicker__time-container {
@apply w-auto border-l-0 pr-3.5;
}
.react-datepicker-popper
.react-datepicker__time-container
.react-datepicker__time {
@apply bg-[var(--muted)] dark:bg-[var(--muted)];
}
.react-datepicker__time-container
.react-datepicker__time
.react-datepicker__time-box
ul.react-datepicker__time-list
li.react-datepicker__time-list-item:hover {
@apply dark:bg-[var(--muted)];
}
/* Header */
.react-datepicker .react-datepicker__header {
@apply border-b-transparent bg-[var(--background)] dark:border-transparent dark:bg-[var(--muted)];
}
/* Current month name in header */
.react-datepicker .react-datepicker-year-header {
@apply dark:text-[var(--text-secondary)];
}
.react-datepicker .react-datepicker__current-month {
@apply mb-3 text-base font-medium text-[var(--text-secondary)] dark:text-[var(--text-secondary)];
}
/* Sun-Sat day names in header */
.react-datepicker .react-datepicker__day-names div {
@apply m-1.5 text-sm text-[var(--text-secondary)];
}
/* Previous month button */
.react-datepicker .react-datepicker__navigation--previous {
@apply ml-6 rtl:mr-6;
}
/* Next month button */
.react-datepicker .react-datepicker__navigation--next {
@apply mr-6 rtl:ml-6;
}
/* Icon in previous month button */
.react-datepicker .react-datepicker__navigation-icon--previous {
@apply right-0.5;
}
/* Icon in next month button */
.react-datepicker .react-datepicker__navigation-icon--next {
@apply left-0.5;
}
/* Each day */
.react-datepicker .react-datepicker__day {
@apply m-1.5 text-sm leading-7 text-[var(--text-secondary)];
@apply hover:rounded-[var(--border-radius)] hover:bg-[var(--muted)] hover:text-[var(--text-primary)];
}
/* Outside month */
.react-datepicker .react-datepicker__day--outside-month {
@apply text-[var(--muted-foreground)];
}
/* Keyboard selected */
.react-datepicker .react-datepicker__day--keyboard-selected {
@apply bg-[var(--muted)];
}
/* Today */
.react-datepicker .react-datepicker__day--today {
@apply rounded-[var(--border-radius)] border border-[var(--muted)] bg-[var(--muted)] leading-[26px] text-[var(--text-primary)];
@apply hover:border-[var(--border-color)] hover:bg-[var(--primary)] hover:text-[var(--primary-foreground)];
}
/* While selecting */
.react-datepicker div.react-datepicker__day--in-selecting-range {
@apply rounded-[var(--border-radius)] bg-[var(--muted)] text-[var(--text-primary)];
}
.react-datepicker div.react-datepicker__year-text--in-selecting-range {
@apply bg-[var(--muted)] text-[var(--text-primary)];
}
.react-datepicker
div.react-datepicker__year-text--in-selecting-range.react-datepicker__year-text--disabled {
@apply bg-transparent text-[var(--muted-foreground)];
}
/* In range */
.react-datepicker .react-datepicker__day--in-range {
@apply rounded-[var(--border-radius)] bg-[var(--muted)] text-[var(--text-primary)];
}
.react-datepicker .react-datepicker__year-text--in-range {
@apply bg-[var(--muted)] text-[var(--text-primary)];
}
/* Selected */
.react-datepicker .react-datepicker__day--range-start,
.react-datepicker .react-datepicker__day--range-end,
.react-datepicker .react-datepicker__day--selected {
@apply rounded-[var(--border-radius)] border-none bg-[var(--primary)] font-normal leading-7 text-[var(--primary-foreground)];
@apply hover:bg-[var(--primary-dark)] hover:text-[var(--primary-foreground)];
}
.react-datepicker .react-datepicker__year-text--range-end {
@apply border-none bg-[var(--primary)] font-normal text-[var(--primary-foreground)] hover:bg-[var(--primary-dark)] hover:text-[var(--primary-foreground)];
}
/* Time list */
.react-datepicker .react-datepicker__time-list {
@apply !h-[247px];
}
/* Time item */
.react-datepicker .react-datepicker__time-list-item {
@apply my-2 rounded text-sm text-[var(--muted-foreground)];
@apply hover:bg-[var(--muted)] hover:text-[var(--text-primary)];
}
/* Selected time item */
.react-datepicker .react-datepicker__time-list-item--selected {
@apply !bg-[var(--muted)] !font-medium !text-[var(--text-primary)];
}
/* Time only box */
.react-datepicker-popper .react-datepicker-time__header {
@apply text-[var(--text-secondary)];
}
.react-datepicker-popper
.react-datepicker__time-container
.react-datepicker__time
.react-datepicker__time-box {
width: 90px;
}
.react-datepicker--time-only
.react-datepicker__time-container
.react-datepicker__time
.react-datepicker__time-box {
@apply w-auto;
}
/* Time only item */
.react-datepicker--time-only .react-datepicker__time-list-item {
@apply rounded-none;
}
/* Month picker text */
.react-datepicker .react-datepicker__month-text {
@apply w-0 px-5 py-1.5;
}
/* Keyboard selected month in month picker */
.react-datepicker .react-datepicker__month-text--keyboard-selected {
@apply bg-[var(--muted)] text-[var(--text-primary)];
}
/* Month in range */
.react-datepicker .react-datepicker__month--in-range {
@apply bg-[var(--muted)] text-[var(--text-secondary)];
}
/* Hover on selected months in range */
.react-datepicker
.react-datepicker__month-text.react-datepicker__month--in-range:hover {
@apply bg-[var(--muted)] text-[var(--text-primary)];
}
/* Selected month in range */
.react-datepicker .react-datepicker__month--range-start,
.react-datepicker .react-datepicker__month--range-end,
.react-datepicker .react-datepicker__month--selected {
@apply bg-[var(--primary)] font-normal text-[var(--primary-foreground)];
}
/* Hover on selected range start and end month */
.react-datepicker
.react-datepicker__month-text.react-datepicker__month--selected:hover,
.react-datepicker
.react-datepicker__month-text.react-datepicker__month--range-start:hover,
.react-datepicker
.react-datepicker__month-text.react-datepicker__month--range-end:hover {
@apply bg-[var(--primary-dark)] text-[var(--primary-foreground)];
}
/* Year wrapper in year picker */
.react-datepicker .react-datepicker__year-wrapper {
@apply inline-block max-w-[220px];
}
/* Year text in year picker - ensure width is auto */
.react-datepicker__year .react-datepicker__year-text {
@apply !w-auto;
}
/* Year text in year picker */
.react-datepicker .react-datepicker__year-text {
@apply w-auto px-5 py-1.5 text-[var(--text-secondary)] dark:hover:text-[var(--foreground)];
}
.react-datepicker .react-datepicker__year-text--range-end {
@apply text-[var(--primary-foreground)];
}
/* Keyboard selected year in year picker */
.react-datepicker .react-datepicker__year-text--keyboard-selected {
@apply bg-[var(--muted)] text-[var(--text-primary)];
@apply hover:bg-[var(--muted)] hover:text-[var(--text-primary)];
}
/* Selected year & month in year picker */
.react-datepicker
.react-datepicker__year-text.react-datepicker__year-text--selected,
.react-datepicker
.react-datepicker__month-text.react-datepicker__month-text--selected {
@apply bg-[var(--primary)] text-[var(--primary-foreground)];
@apply hover:bg-[var(--primary-dark)] hover:text-[var(--primary-foreground)];
}
/* Year and month dropdown arrow */
.react-datepicker .react-datepicker__year-read-view--down-arrow,
.react-datepicker .react-datepicker__month-read-view--down-arrow {
@apply top-[5px] h-[7px] w-[7px] border-r-[length:var(--border-width)] border-t-[length:var(--border-width)] border-[var(--muted)];
}
/* Sub-header containing year and month dropdown */
.react-datepicker
.react-datepicker__current-month--hasYearDropdown.react-datepicker__current-month--hasMonthDropdown
~ .react-datepicker__header__dropdown {
@apply my-2 grid grid-cols-2 divide-x divide-[var(--muted)] text-sm;
}
/* Month and year dropdown button in sub-header */
.react-datepicker .react-datepicker__month-dropdown-container--scroll,
.react-datepicker .react-datepicker__year-dropdown-container--scroll {
@apply inline-flex justify-center;
}
/* Month and year dropdown */
.react-datepicker .react-datepicker__year-dropdown,
.react-datepicker .react-datepicker__month-dropdown {
@apply top-auto w-2/5 border border-[var(--border-color)] bg-[var(--muted)] shadow-md;
}
/* Year dropdown */
.react-datepicker .react-datepicker__year-dropdown {
@apply h-80;
}
/* Month dropdown */
.react-datepicker .react-datepicker__month-dropdown {
@apply py-3;
}
/* Month and year option */
.react-datepicker .react-datepicker__month-option,
.react-datepicker .react-datepicker__year-option {
@apply my-1 py-1 text-sm text-[var(--text-secondary)];
@apply hover:bg-[var(--muted)] hover:text-[var(--text-primary)];
}
/* First and last type of month and year option */
.react-datepicker .react-datepicker__month-option:first-of-type,
.react-datepicker .react-datepicker__month-option:last-of-type,
.react-datepicker .react-datepicker__year-option:first-of-type,
.react-datepicker .react-datepicker__year-option:last-of-type {
@apply rounded-none;
}
/* Selected month and year in dropdown */
.react-datepicker .react-datepicker__month-option--selected_month,
.react-datepicker .react-datepicker__year-option--selected_year {
@apply bg-[var(--muted)] text-[var(--text-primary)];
@apply [&>span]:hidden;
}
.react-datepicker .react-datepicker__day:hover,
.react-datepicker .react-datepicker__month-text:hover,
.react-datepicker .react-datepicker__quarter-text:hover,
.react-datepicker .react-datepicker__year-text:hover {
@apply bg-[var(--muted)] text-[var(--text-primary)];
}
/* Today hover - override general day hover */
.react-datepicker .react-datepicker__day--today:hover {
@apply !bg-[var(--primary)] !text-[var(--primary-foreground)];
}
/* Disabled item */
.react-datepicker .react-datepicker__day--disabled {
@apply cursor-not-allowed text-[var(--muted-foreground)] hover:bg-transparent hover:text-[var(--muted-foreground)] dark:hover:text-[var(--muted-foreground)];
}
.react-datepicker .react-datepicker__year-text--disabled {
@apply cursor-not-allowed text-[var(--muted-foreground)] hover:bg-transparent dark:hover:text-[var(--muted-foreground)];
}
Usage
Default Date Picker
The default date picker component with a clean, modern interface:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
placeholderText="Select Date"
/>
);
}
Month Picker
Select a month and year using the month picker:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<div className="h-72 w-96">
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
dateFormat="MMMM yyyy"
placeholderText="Select Month"
showMonthYearPicker
/>
</div>
);
}
Year Picker
Select a year using the year picker:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<div className="h-72">
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
dateFormat="yyyy"
placeholderText="Select Year"
showYearPicker
/>
</div>
);
}
Month Dropdown
Select a month from a dropdown menu:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<div className="h-96">
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
placeholderText="Select Month from Dropdown"
showMonthDropdown
/>
</div>
);
}
Year Dropdown
Select a year from a scrollable dropdown:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<div className="h-96">
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
placeholderText="Select Year from Dropdown"
showYearDropdown
scrollableYearDropdown
/>
</div>
);
}
Calendar With Time
Select both date and time in a single picker:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<div className="h-96">
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
dateFormat="d MMMM yyyy, h:mm aa"
placeholderText="Select Date & Time"
showTimeSelect
/>
</div>
);
}
Time Only Picker
Select only the time without a date:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<div className="h-96">
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
dateFormat="h:mm aa"
placeholderText="Select Time"
showTimeSelect
showTimeSelectOnly
/>
</div>
);
}
Range Date Picker
Select a date range with clearable functionality:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [dateRange, setDateRange] =
React.useState<[Date | null, Date | null]>();
return (
<div className="h-96">
<ReactDatePicker
startDate={dateRange?.[0]!}
endDate={dateRange?.[1]!}
onChange={(dates) => setDateRange(dates)}
monthsShown={2}
placeholderText="Select Date in a Range"
selectsRange
inputProps={{
clearable: true,
onClear: () => setDateRange([null, null]),
}}
/>
</div>
);
}
Advanced Usage
Custom Date Format
Customize the date format to match your requirements:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
dateFormat="dd/MM/yyyy"
placeholderText="DD/MM/YYYY"
/>
);
}
Min and Max Dates
Restrict date selection to a specific range:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
const minDate = new Date();
const maxDate = new Date();
maxDate.setMonth(maxDate.getMonth() + 3);
return (
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
minDate={minDate}
maxDate={maxDate}
placeholderText="Select a date"
/>
);
}
Filter Dates
Filter out specific dates from selection:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
const isWeekday = (date: Date) => {
const day = date.getDay();
return day !== 0 && day !== 6;
};
return (
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
filterDate={isWeekday}
placeholderText="Select a weekday"
/>
);
}
Custom Input Component
Use a custom input component:
import React from 'react';
import ReactDatePicker from '@components/date-picker';
import { Button } from 'rizzui/button';
export default function App() {
const [startDate, setStartDate] = React.useState<Date | null>();
return (
<ReactDatePicker
selected={startDate}
onChange={(date: Date) => setStartDate(date)}
customInput={<Button>Select Date</Button>}
/>
);
}
Best Practices
- Use controlled components - Manage date state with React state for better control
- Provide clear placeholders - Use descriptive placeholder text to guide users
- Set appropriate date ranges - Use
minDateandmaxDateto prevent invalid selections - Handle time zones - Be aware of timezone differences when working with dates
- Accessibility - The component includes built-in ARIA attributes for screen readers
- Form integration - Use with form libraries like React Hook Form for validation
- Performance - For large date ranges, consider using
filterDateto optimize rendering - Mobile considerations - Test on mobile devices as date pickers behave differently on touch devices
API Reference
The ReactDatePicker component extends all props from react-datepicker. Refer to their documentation for a complete list of available props and options.
Common Props
| Prop | Type | Description |
|---|---|---|
| selected | Date | null | Currently selected date (controlled) |
| onChange | (date: Date | null) => void | Callback when date changes |
| placeholderText | string | Placeholder text for the input |
| dateFormat | string | Format string for the date display |
| minDate | Date | Minimum selectable date |
| maxDate | Date | Maximum selectable date |
| filterDate | (date: Date) => boolean | Function to filter selectable dates |
| showTimeSelect | boolean | Show time selection |
| showTimeSelectOnly | boolean | Show only time selection |
| selectsRange | boolean | Enable range selection mode |
| startDate | Date | null | Start date for range selection |
| endDate | Date | null | End date for range selection |
| inputProps | InputProps | Props to pass to the Input component |
| customInput | ReactElement | Custom input component |
| calendarClassName | string | Additional CSS classes for calendar |
| popperClassName | string | Additional CSS classes for popper |
Note: For a complete list of props and advanced features, please refer to the react-datepicker documentation.