Building a calendar component with Tailwind and date-fns
Tags: beginners, javascript, react, webdev
Published: 2023-04-01 14:13:22

A calendar component is one of the most common and useful component on the web. It can be used for displaying events, scheduling reminders and for many other different use cases.

Over a last few days, I have been exploring Tailwind CSS a lot and I wanted to try building something that is used more commonly across the web.

In this article, we are going to build a simple calendar component using **Tailwind** which is a popular utility-first CSS framework, **date-fns** which is a popular library for manipulating date and time using JavaScript.

The calendar component that we are going to build in this article will have following features:

1. Display current month by default
2. Get next month
3. Get previous month
4. Highlight current day

So, let's dive right in!

## Installation
I assume that you have already set up a basic React project. You can install Tailwind CSS by following different guides mentioned in [installation](https://tailwindcss.com/docs/installation/framework-guides) section of Tailwind website.

Now that we have set up a basic `React + Tailwind` project, let's go ahead and install `date-fns` library.

If you are using `npm` then run `npm install date-fns` command to install the package. If you are using `yarn` then run `yarn add date-fns` command.

Now we have everything installed that we need to build this component.

## Building the component

Let's start building this component.

First we will a write code to display the current month and year. We will also include buttons to toggle between previous and next month. Here is what we are going to build in first part.

![Calendar Header](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cqb77t6qx4uunp0fca9c.png)

I am using [heroicons](https://heroicons.com/) (also created by makers of Tailwind CSS) to create buttons for fetching next and previous month.

Here is the JSX that will render a calendar component controls UI.

```
<div className="flex items-center justify-between">
  <p className="font-semibold text-xl"> {format(firstDayOfMonth, "MMMM yyyy")} </p>
  <div className="flex items-center justify-evenly gap-6 sm:gap-12">
    <ChevronLeftIcon className="w-6 h-6 cursor-pointer" onClick={getPrevMonth} />
    <ChevronRightIcon className="w-6 h-6 cursor-pointer" onClick={getNextMonth} />
  </div>
</div>
```
Here are the functions and state that we'll need to set up for this part.

```
const today = startOfToday();
  
const [currMonth, setCurrMonth] = useState(() => format(today, "MMM-yyyy"));
let firstDayOfMonth = parse(currMonth, "MMM-yyyy", new Date());

const daysInMonth = eachDayOfInterval({
start: firstDayOfMonth,
end: endOfMonth(firstDayOfMonth),
});

const getPrevMonth = (event: React.MouseEvent<SVGSVGElement>) => {
event.preventDefault();
const firstDayOfPrevMonth = add(firstDayOfMonth, { months: -1 });
setCurrMonth(format(firstDayOfPrevMonth, "MMM-yyyy"));
};

const getNextMonth = (event: React.MouseEvent<SVGSVGElement>) => {
event.preventDefault();
const firstDayOfNextMonth = add(firstDayOfMonth, { months: 1 });
setCurrMonth(format(firstDayOfNextMonth, "MMM-yyyy"));
};
```
Let's understand the different functions from above code.

- `format` function is provided by `date-fns` library. It is basically used for formatting the given date. It returns formatted date string.
- `startOfToday` function is also provided by `date-fns` and it returns current date.
- `parse` function provided by `date-fns` is used for parsing the date from given date string.
- `eachDayOfInterval` function provided by `date-fns` returns an array of dates within specified date interval.
- `getPrevMonth` function is used for getting dates for a previous month. It uses `add` function from `date-fns` and we are setting `month` to `-1` to fetch first day of previous month. Once we get first day of previous month. We can re-calculate and get dates using `eachDayOfInterval` function.
- `getNextMonth` function is similar to `getPrevMonth` except we are setting `month` value to `1` to fetch the first day of next month.

If you are following till now, you should have noticed that we are storing all dates for a month in `daysInMonth` variable. Now, we will use this array of dates to render calendar component UI.

Before writing code for calendar component, let's first write a code to display calendar header where we will render names of days.

This is what we are going to render.

![Calendar Header](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q6x4r6mkfh8jngrd75gk.png)

Here's a JSX that renders above UI.

First, we will need an array containing all days.

```
const days = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
```

```
<div className="grid grid-cols-7 gap-6 sm:gap-12 place-items-center">
 {
   days.map((day, idx) => {
    return (
     <div key={idx} className="font-semibold">
         {capitalizeFirstLetter(day)}
     </div>
    );
})}
</div>
```
Now that we have rendered calendar header, we can go ahead and write a code to display all dates in a grid format. We already have `daysInMonth` that we need to display dates in a month.

```
<div className="grid grid-cols-7 gap-6 sm:gap-12 mt-8 place-items-center">
  {daysInMonth.map((day, idx) => {
    return (
      <div key={idx} className={colStartClasses[getDay(day)]}>
        <p
          className={`cursor-pointer flex items-center justify-center font-semibold h-8 w-8 rounded-full  hover:text-white ${
            isSameMonth(day, today) ? "text-gray-900" : "text-gray-400"
          } ${!isToday(day) && "hover:bg-blue-500"} ${
            isToday(day) && "bg-red-500 text-white"
          }`}
        >
          {format(day, "d")}
        </p>
      </div>
    );
  })}
</div>;

```
In above code,
- `isToday` function provided by `date-fns` is used to check if the given day is current day or not.
- `isSameMonth` function provided by `date-fns` is used to check if the given day is a part of same month or not.

```
 const colStartClasses = [
    "",
    "col-start-2",
    "col-start-3",
    "col-start-4",
    "col-start-5",
    "col-start-6",
    "col-start-7",
  ];
```

By default, the first date of the month will be rendered in first column and first row. The above code will ensure that each date is getting rendered in the correct day column. 

The above code will render following UI

![Calendar UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qqb9pjftzzzny2hca7wm.png)

As you can see, we are applying different classes based on conditionals to highlight current date and change text colors of other dates.

## TL;DR

In this way, we can create a simple calendar component using Tailwind CSS and date-fns. You can check the final version of this code on my Github [here](https://github.com/VivekAlhat/Tailwind-Calendar).

Thank you for reading the article!