Building a calendar component with Tailwind and date-fns

vivekalhat

Vivek Alhat

Posted on April 1, 2023

Building a calendar component with Tailwind and date-fns

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

I am using heroicons (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>
Enter fullscreen mode Exit fullscreen mode

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"));
};
Enter fullscreen mode Exit fullscreen mode

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

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"];
Enter fullscreen mode Exit fullscreen mode
<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>
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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",
  ];
Enter fullscreen mode Exit fullscreen mode

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

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.

Thank you for reading the article!

💖 💪 🙅 🚩
vivekalhat
Vivek Alhat

Posted on April 1, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related