Glad Chinda FollowFull-stack web developer learning new hacks one day at a time. Web technology enthusiast. Hacking stuffs @theflutterwave.
How to build a custom date picker with React
16 min read4571
It is very common to see forms on the web that include one or more date fields. Whether it is a person’s date of birth that is required or a flight schedule date, you always want to be sure that the user supplies a valid date.
In HTML5, a new date input type was introduced to ensure better ways of getting valid date values in forms. The default behavior of the date input type is to present a date picker to the user. However, the appearance of this date picker is not consistent across browsers.
You can find out more about the date input type and browser support here.
The online code editor tailored for web applications
Before getting started, you need to ensure that you have Node already installed on your machine. It is recommended that you install the Yarn package manager on your machine, since it will be used instead of npm that ships with Node. You can follow this Yarn installation guide to install Yarn on your machine.
The boilerplate code for the React app will be created using the create-react-app package. You also need to ensure that it is installed globally on your machine. If you are using npm >= 5.2 then you don’t need to install create-react-app as a global dependency — you can use the npx command instead.
Create new Application
Start a new React application using the following command. You can name the application whatever you like.
npm >= 5.2
If you are using npm version 5.2 or higher, it ships with an additional npx binary. Using the npx binary, you don’t need to install create-react-app globally on your machine. You can start a new React application with this simple command:
npx create-react-app react-datepicker
The dependencies for this application are kept as lean as possible. Run the following command to install the required dependencies.
The bootstrap package has been installed as a dependency for the application to provide some default styling. To include the Bootstrap 4 styles, edit the src/index.js file and add the following line before every other import statement.
For this application, two major components are required.
The Calendar component, which renders the custom calendar with date selection functionality.
The Datepicker component, which renders a date input and presents the calendar for picking date.
Each of these components will be contained in its own directory with two files — index.js and styles.js. The index.js file exports the component while the styles.js file exports the styled components required by the component to add some styling.
Go ahead and run the following commands from your project root to create the component directories and files:
Since no external dependency will be required for handling dates in this application, there is need for date handling helper functions. Run the following commands to create a file for the calendar helper module.
Start the application by running the following command on your terminal with yarn:
The application is now started and development can begin. Notice that a browser tab has been opened for you with live reloading functionality to keep in sync with changes in the application as you develop.
Calendar helpers module
Basic constants and helpers
First off, define some calendar helpers and constants that will be needed to build the calendar. These helpers and constants will be defined in the calendar helper module you created earlier and will be exported as named exports.
Add the following content to the src/helpers/calendar.js file.
This code snippet contains comments to explain what each helper function is doing. However, there are a few things worth pointing out.
First, methods like getDay() and getMonth() in Date.prototype usually return a zero-based value. Hence, the first month of the year (January) is 0 where as December is 11, while the first day of the week (Sunday) is 0 where as Saturday is 7.
In the previous code snippet, you’ll see that 1 was always added to these zero-based values, so that Sunday becomes 1 for week days, and December becomes 12 for months.
Also, notice that CALENDAR_WEEKS is set to 6. Since a month typically spans through 4 weeks, this allows the calendar to accommodate at least the last week from the previous month, and the first week from the next month. You will see the effect of this constant soon, as it will be used in the calendar builder function.
Append the following content to the src/helpers/calendar.js file to add some additional helper functions to the calendar module.
Finally, here comes the default export of the calendar helper module — the calendar builder function itself. This function takes a month and year as arguments and returns an array of 42 elements, each element representing a calendar date in the format [YYYY, MM, DD].
Here is the calendar builder function. Append this code snippet to the src/helpers/calendar.js file.
Notice that the calendar dates returned in the array from the builder span from the dates in the last week of the previous month, through the dates in given month, to the dates in the first week of the next month.
Building the calendar component
Now you have the calendar helper module, it’s time to build the Calendar React component. Add the following code snippet to the src/components/Calendar/index.js file.
Notice in this code snippet that the default calendar export as well as other helper functions and constants have been imported from the calendar helper module. Also, all the exports from the calendar styles module have been imported with the Styled namespace.
Although, the styles have not been created at the moment, they will be created soon using the styled-components package.
The component state is partly resolved from props using the resolveStateFromProp() method which returns an object containing:
current — which is a Date object for the currently selected date or null.
month — which is the month of the currently selected date if it is set, otherwise it is the month of the current date (today).
year — which is the year of the currently selected date if it is set, otherwise it is the year of the current date (today).
The month and year state properties are required to properly render the calendar as shown in the getCalendarDates() method, which uses the calendar builder function to build the calendar for the month and year.
Finally, the state is augmented with the today property which is a Date object for the current date.
Rendering parts of the calendar component
From the previous Calendar component code snippet, the render() method made reference to some other methods for rendering the month and year, week days and calendar dates.
Add these methods to the Calendar component as shown in the following code snippet.
In the renderMonthAndYear() method, the month name is first resolved from the CALENDAR_MONTHS object. Then it is rendered alongside the year and two arrow controls on the left side and on the right side for navigating through months and years.
The arrow controls each has event handlers for the mousedown and mouseup events, which will be defined later — handlePrevious(), handleNext() and clearPressureTimer().
The rendered DOM from the renderMonthAndYear() method looks like the following screenshot (with some styling):
The renderDayLabel() method renders a label for a day of the week. It resolves the label from the WEEK_DAYS object. Notice that it takes two arguments — day and index, since it is used as a callback function to .map() as seen in the render() method.
After the mapping, here is what the rendered DOM looks like for the days of the week.
The renderCalendarDate() method is also used as a .map() callback function and renders a calendar date. The date it receives as its first argument is in the format [YYYY, MM, DD].
It checks if the date is same as today, same as currently selected date, and in the same month as the current state’s month and year. With these checks, it conditionally renders one of the variants of the calendar date cell — HiglightedCalendarDate, TodayCalendarDate or CalendarDate.
Also notice that an onClick handler is set for each rendered calendar date to jump to that particular date using the gotoDate() method that will be defined in the next section.
The event handlers
A couple of references have been made to some event handlers in previous sections. Go ahead and update the Calendar component to include the following code snippet for the event handlers.
The gotoDate() method is a higher-order function that takes a Date object as it’s argument and returns an event handler that can be triggered to update the currently selected date in the state. Notice that the resolveStateFromDate() method is used to resolve the month and year from the date and update the state.
If a callback function is passed to the onDateChanged prop of the Calendar component, then that function will be called with the updated date. This is very useful for cases where you want to propagate the date change to a parent component.
The handlePrevious() and handleNext() event handlers share a similar behavior. By default, they cycle through the months. However if the shift key is pressed, then they cycle through years instead. Finally, they handover control to the handlePressure() method.
The handlePressure() method simply uses timers to simulate pressure clicking for rapidly cycling through months or years, while the clearPressureTimer() method clears these timers.
Component lifecycle methods
The Calendar component is just some lifecycle methods away from being complete. Here are the lifecycle methods for the Calendar component.
In the componentDidMount() method, there is a day timer that is set to automatically update today state property to the next day when the current day is over.
Before the component is unmounted, all the timers are cleared as seen in the componentWillUnmount() method.
Styling the calendar
Now you have completed Calendar component, you will go ahead and create the styled components required for giving the calendar some styling.
Sick of debugging web apps? Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket pairs session replay with technical telemetry to quickly understand what went wrong.
Add the following code snippet to the src/components/Calendar/styles.js file.
And that’s all for the components and styles required to properly render the calendar. If you render the Calendar component in the app at this time, it should look like this screenshot.
Building the datepicker
To begin building the Datepicker component, add the following code snippet to the src/components/Datepicker/index.js file.
Here, the component state is initialized with two properties:
date — an ISO string representation for the current date on the date picker. The format is “YYYY-MM-DD”.
calendarOpen — a boolean flag that indicates if the date picker calendar is visible or not.
When the component mounts, the Date object is resolved from the value prop passed to the component and is updated on the state as seen in the componentDidMount() method.
The handleDateChange() method takes a Date object as its argument and updates the date in the state. If a callback function is passed to the onDateChanged prop of the Datepicker component, then that function will be called with the updated ISO date string.
Rendering the datepicker
At this point, it is worth mentioning that the Bootstrap Dropdown component will be used to simulate the dropdown effect for the custom date picker. This is the reason why the Reactstrap package was added as a dependency for this project.
As you will soon notice, the styled components that are rendered in the date picker are styled extensions of the dropdown components from Reactstrap.
Update the Datepicker component to include the render() method as shown in the following code snippet.
Here, the Styled.DatePickerFormGroup component is a Bootstrap .form-group that wraps the date picker label and input field. It is important to note that the input field is of type “text” and also marked as readonly so that it cannot be edited directly. Also notice that the default behavior for the change event on the input element has been prevented.
The Styled.DatePickerDropdown component and its descendants are styled extensions of the Dropdown component from the Reactstrap package. You can learn more about dropdowns in Reactstrap here.
Finally, the Calendar component is rendered in the dropdown menu passing the date from the state and the handleDateChange() method as callback function for the onDateChanged prop.
The final rendered DOM for the Datepicker component should look like the following screenshot (with some styling):
Styling the datepicker
Add the following code snippet to the src/components/Datepicker/styles.js file to create the styled components required for the date picker.
The app component
Finally, update the src/App.js file to look like the following code snippet.
If you followed this article and the code snippets through, you should have a working custom date picker rendered in your React application.
In this tutorial, you’ve been able to follow through a step-by-step guide on how you can build a custom React date picker component that can be used as a replacement for the native HTML5 date picker input element.
Although the custom date picker created in this tutorial works as expected, it does not completely satisfy all the requirements for a date picker element. Further improvements can be made such as: