react-tailwindcss-datetimepicker
v3.0.7
Published
Feature-rich React date-time picker with range selection, customizable presets, keyboard navigation, TypeScript support, dark mode, and no date library dependency. Fully responsive. Built on top of **React 18** and [Vitejs](https://vitejs.dev/).
Downloads
1,522
Maintainers
Keywords
Readme
📦 React TailwindCSS Date and Time Picker
Feature-rich React date-time picker with range selection, customizable presets, keyboard navigation, TypeScript support, dark mode, and no date library dependency. Fully responsive. Built on top of React 18 and Vitejs.
- ✅ Selection of date ranges
- ✅ Selection of times within the chosen date range
- ✅ Customizable range presets for quick selection (e.g., Yesterday, last 30 days)
- ✅ Keyboard navigation for enhanced accessibility
- ✅ Full TypeScript support
- ✅ Built-in dark mode
- ✅ Fully responsive, optimized for mobile devices
- ✅ No dependency on external date libraries (compatible with vanilla JS, date-fns, moment, etc.)
Online Demo
Check out the online demo at codesandbox.io
This project is a fork of react-datetimepicker with significant alterations including:
- Complete revamp of CSS styles utilizing TailwindCSS.
- Transition to Vitejs for the build system.
- Conversion of all files to TypeScript for improved type safety and development efficiency.
Table of Contents
Installation
// Npm
npm i react-tailwindcss-datetimepicker
// Yarn
yarn add react-tailwindcss-datetimepicker
With TailwindCSS
If you're already including TailwindCSS in your project, just open up your tailwind.config.js
file and add the following line to your content
array so that tailwind could find CSS classes used in picker and add those to your project's global css file:
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./node_modules/react-tailwindcss-datetimepicker/dist/react-tailwindcss-datetimepicker.js',
// Add this line 👆
],
};
Without TailwindCSS
If you don't use TailwindCSS in your project you can simply import the shipped standalone CSS file needed for this component like so:
// src/main.tsx
import DateTimePicker from 'react-tailwindcss-datetimepicker';
import 'react-tailwindcss-datetimepicker/style.css';
Basic Usage
Function Components
import { useState } from "react";
import DateTimePicker from "react-tailwindcss-datetimepicker";
// If you are already using TailwindCSS, you can omit this.
// Check out section "Installing With TailwindCSS" in docs.
import "react-tailwindcss-datetimepicker/style.css";
const now = new Date();
const startOfToday = new Date();
startOfToday.setHours(0, 0, 0, 0);
const endOfToday = new Date(startOfToday);
endOfToday.setDate(endOfToday.getDate() + 1);
endOfToday.setSeconds(endOfToday.getSeconds() - 1);
function App() {
// Set the initial view of picker to last 2 days
const [selectedRange, setSelectedRange] = useState({
start: new Date(new Date().setDate(new Date().getDate() - 2)),
end: endOfToday,
});
function handleApply(startDate: Date, endDate: Date) {
setSelectedRange({ start: startDate, end: endDate });
}
return (
<DateTimePicker
ranges={{
Today: [new Date(startOfToday), new Date(endOfToday)],
"Last 30 Days": [
new Date(
now.getFullYear(),
now.getMonth() - 1,
now.getDate(),
0,
0,
0,
0
),
new Date(endOfToday),
],
}}
start={selectedRange.start}
end={selectedRange.end}
applyCallback={handleApply}
>
<button type="button">{`${selectedRange.start} - ${selectedRange.end}`}</button>
</DateTimePicker>
);
}
export default App;
Legacy Class Components
import React from 'react';
import DateTimePicker from 'react-tailwindcss-datetimepicker';
// If you are already using TailwindCSS, you can omit this.
// Check out section "Installing With TailwindCSS" in docs.
import 'react-tailwindcss-datetimepicker/style.css';
interface Props {}
interface State {
start: Date;
end: Date;
}
const now = new Date();
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
const endOfToday = new Date(startOfToday);
endOfToday.setDate(endOfToday.getDate() + 1);
endOfToday.setSeconds(endOfToday.getSeconds() - 1);
class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
// Set the initial view of picker to last 2 days
this.state = {
start: new Date(new Date().setDate(new Date().getDate() - 2)),
end: endOfToday,
};
}
applyCallback = (startDate: Date, endDate: Date) => {
this.setState({
start: startDate,
end: endDate,
});
};
render() {
return (
<DateTimePicker
ranges={{
Today: [new Date(startOfToday), new Date(endOfToday)],
'Last 30 Days': [
new Date(now.getFullYear(), now.getMonth() - 1, now.getDate(), 0, 0, 0, 0),
new Date(endOfToday),
],
}}
start={this.state.start}
end={this.state.end}
applyCallback={this.applyCallback}
displayMaxDate
>
<button type="button">{`${this.state.start} - ${this.state.end}`}</button>
</DateTimePicker>
);
}
}
export default App;
Component Props
| Option | Required | Type | Default | Description |
| ------------------------------------------- | ------------ | ---------- | ------------- | ------------------------------------------------------------------------------ |
| ranges
| Required | Object
| undefined
| A record of ranges defined as labels and a tuple of Date times. |
| start
| Required | Date
| undefined
| Initial start Date set in the picker |
| end
| Required | Date
| undefined
| Initial end Date set in the picker |
| applyCallback
| Required | Function
| undefined
| Function which is called when the apply button is clicked |
| locale
| optional | Object
| undefined
| locale format for translatable labels |
| rangeCallback
| optional | Function
| undefined
| Function which is called when one of the preset ranges is clicked |
| maxDate
| optional | Date
| undefined
| Maximum date that can be selected in calendar |
| autoApply
| optional | Boolean
| false
| Set dates as soon as they're clicked without pressing apply |
| descendingYears
| optional | Boolean
| false
| Set years be displayed in descending order |
| years
| optional | Array
| [1900, now]
| Limit the years shown in calendar |
| smartMode
| optional | Boolean
| false
| Switch the month on the right hand side (RHS) when two dates in the same month |
| pastSearchFriendly
| optional | Boolean
| false
| Optimize calendar for past searches |
| noMobileMode
| optional | Boolean
| false
| Picker will always be displayed in full screen mode |
| forceMobileMode
| optional | Boolean
| false
| Picker will always be displayed in condensed mode all the time |
| twelveHoursClock
| optional | Boolean
| false
| Display time values in a 12-hour format rather than a 24-hour format |
| standalone
| optional | Boolean
| false
| When set the picker will be open by default |
| leftMode
| optional | Boolean
| false
| Picker will open to the left |
| centerMode
| optional | Boolean
| false
| Picker will open in center |
| displayMaxDate
| optional | Boolean
| false
| Will display Max Date in picker footer |
| classNames
| optional | Object
| undefined
| Will override classNames for different parts of the picker |
| theme
| optional | string
| blue
| Predefined color themes for the calendar view |
ranges
(Required)
Record<string, [Date, Date]>
A record of ranges defined using a tuple of Date
instances.
Using vanilla Javascript:
const startOfToday = new Date(new Date().setHours(0, 0, 0, 0));
startOfToday.setHours(0, 0, 0, 0);
const endOfToday = new Date(startOfToday);
endOfToday.setDate(endOfToday.getDate() + 1);
endOfToday.setSeconds(endOfToday.getSeconds() - 1);
const ranges = {
Today: [startOfToday, startOfToday],
// 'Last 30 Days': [..., ...],
};
Or using date-fns
lib:
import { add, sub, startOfDay } from 'date-fns';
const now = new Date();
const startOfToday = startOfDay(now);
const endOfToday = add(sub(startOfToday, { seconds: 1 }), { days: 1 });
const ranges = {
Today: [startOfDay(startOfToday), endOfToday],
'Last 30 Days': [startOfDay(sub(startOfToday, { days: 30 })), endOfToday],
};
start
(Required)
Date
Initial start Date set in the picker
end
(Required)
Date
Initial end Date set in the picker
applyCallback
(Required) (start: Date, end: Date) => void
Function which is called when the apply button is clicked/pressed. Takes two params, start date and the end date.
locale
(optional)
Locale for translatable labels. Can also set Sunday to be first day or Monday.
Example:
const locale = {
format: 'dd-MM-yyyy HH:mm', // See: https://date-fns.org/v2.16.1/docs/format
sundayFirst: false,
days: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'So'],
months: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
fromDate: 'From Date',
toDate: 'To Date',
selectingFrom: 'Selecting From',
selectingTo: 'Selecting To',
maxDate: 'Max Date',
close: 'Close',
apply: 'Apply',
cancel: 'Cancel',
};
rangeCallback
(optional) (index: number, value: keyof PresetDateRanges) => void
Function which is called when one of the preset ranges is clicked/selected. Takes two params:
index
is the index of item which is selectedvalue
is the label of that item
maxDate
(optional) Date
Maximum date that can be selected in calendar.
autoApply
(optional)** boolean
defaults to false
When set there will only be one button in the bottom right to close the screen. With this set to true
upon changing anything in picker the callbackfunction
will be automatically called
descendingYears
(optional) boolean
defaults to false
To set years be displayed in descending order in picker instead of ascending.
years
(optional) [number, number]
defaults to [1900, new Date().getFullYear()]
Takes a tuple where the first value is the start year and the second values is the end year users can pick from.
Example:
years={[2000, 2025]}
smartMode
(optional) boolean
defaults to false
The date time picker will switch the month on the right hand side (RHS) when two dates in the same month are selected. Can be used in
conjunction with pastSearchFriendly
to switch the month on the left hand side (LHS) when the two dates are from the same month.
pastSearchFriendly
(optional) boolean
Note: Requires smartMode
to be enabled.
Changes the mode of the date time picker to be optimised for past searches. Where possible, the start and end time will be shown on the RHS when the month and year are equal. This allows for the previous month to be shown on the LHS to allow easier backwards searching.
This setting is false
by default meaning that the LHS is used when dates are selected in the same month & year
noMobileMode
(optional) boolean
defaults to false
When set the mobile breakpoint to be ignored. Picker will always be displayed in full screen mode.
forceMobileMode
(optional) boolean
defaults to false
When set the mobile breakpoint to be ignored. Picker will always be displayed in condensed mode all the time.
twelveHoursClock
(optional) boolean
defaults to false
When enabled, the picker will display time values in a 12-hour format rather than a 24-hour format.
standalone
(optional) boolean
defaults to false
When set the picker will be open by default.
leftMode
(optional) boolean
defaults to true
When set and changed the picker will open to the left (right to left) instead of the default which is to open to the right (left to right)
centerMode
(optional) boolean
defaults to false
To allow flexibility, center mode has been added where leftMode or default is not enough.
displayMaxDate
(optional) boolean
defaults to false
To allow flexibility, center mode has been added where leftMode or default is not enough.
classNames
(optional) object
Will add extra classNames to different parts of the picker. It's great for for tailoring the component to match your preferred look and feel. The object has the following keys:
rootContainer
rangesContainer
rangeButtonDefault
rangeButtonSelected
fromToRangeContainer
normalCell
normalCellHove
greyCel
invalidCel
startCel
endCel
withinRangeCel
startDot
endDot
footerContainer
applyButton
cancelButton
By providing CSS className
(s) for these keys, you can customize/override them.
Note: If you're already using TailwindCSS in your project, you can use the !
operand for overriding an already exisiting className. (Just like !important
in regular CSS) For example:
classNames={{
rootContainer: '!bg-red-700'
}}
The following illustration shows the different components of the picker which can be customized:
theme
There are 4 color themes available to choose from. (More to come later)
blue
(Default)orange
green
purple
Development
Runs the app in the development mode.
npm run dev
Open http://localhost:3000 to view it in the browser.
Hot module reloading is enabled in dev mode.
Production
npm run build
Builds the app for production to the /dist
folder using vite's library mode. Type declarations (index.d.ts
) are also created in the same directory.
Migration
from 2.x.x to
to 3.x.x
:
Moment
has been removed from the dependencies. Now you can use any date library (or even vanilla js) to construct your date objects. Seeranges
.
From 1.x.x to
to 2.x.x
:
local
prop has been renamed tolocale
and it's now an optional prop.style
prop has been removed in favor ofclassNames
.darkMode
prop has been removed. All UI elements of the picker now have dark styles defined for them. If you addclassName=dark
to your<body>
tag (or any other parent element of it), dark mode will be automatically turned on.
Roadmap
- [x] Support TypeScript
- [x] Ability to add custom CSS classes for different parts of the component
- [x] Migrate to date-fns
- [x] Adding predefined themes
- [ ] More demos showcasing different props
- [ ] Write tests