Building a Date Range Picker with React and Day.js.
Tags: webdev, tutorial, react, javascript
Published: 2024-06-20 16:16:26

Welcome to the third and final part of this tutorial series on creating a custom calendar using React and Day.js. In this session, we will build upon the date picker we developed in part two. Our goal is to enhance it further by enabling users to select multiple dates and highlighting the selected range — essentially creating a range picker feature.


![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpun83bhl7xvn3xvs3z6.gif)


If you have not checked the first two parts, I encourage you to do so. 

The first part:

{% embed https://dev.to/oluwadahunsia/building-a-custom-calendar-with-react-and-dayjs-a-step-by-step-guide-2h1d %}

The second part:

{% embed https://dev.to/oluwadahunsia/building-a-simple-date-picker-with-react-and-dayjs-4oop %}



If you do wish to start from here, I'm providing all the necessary files you need to catch up. 



## Starter files.

```css
//style

.calendar__container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 25px;
  width: max-content;
  background: #ffffff;
  box-shadow: 5px 10px 10px #dedfe2;
}

.month-year__layout {
  display: flex;
  margin: 0 auto;
  width: 100%;
  flex-direction: row;
  align-items: center;
  justify-content: space-around;
}

.year__layout,
.month__layout {
  width: 150px;
  display: flex;
  padding: 10px;
  font-weight: 600;
  align-items: center;
  text-transform: capitalize;
  justify-content: space-between;
}

.back__arrow,
.forward__arrow {
  cursor: pointer;
  background: transparent;
  border: none;
}

.back__arrow:hover,
.forward__arrow:hover {
  scale: 1.1;
  transition: scale 0.3s;
}

.days {
  display: grid;
  grid-gap: 0;
  width: 100%;
  grid-template-columns: repeat(7, 1fr);
}

.day {
  flex: 1;
  font-size: 16px;
  padding: 5px 7px;
  text-align: center;
}

.calendar__content {
  position: relative;
  background-color: transparent;
}

.calendar__items-list {
  text-align: center;
  width: 100%;
  height: max-content;
  overflow: hidden;
  display: grid;
  grid-gap: 0;
  list-style-type: none;
  grid-template-columns: repeat(7, 1fr);
}

.calendar__items-list:focus {
  outline: none;
}

.calendar__day {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}

.calendar__item {
  position: relative;
  width: 50px;
  height: 50px;
  cursor: pointer;
  background: transparent;
  border-collapse: collapse;
  background-color: white;

  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  border: 1px solid transparent;
  z-index: 200;
}

button {
  margin: 0;
  display: inline;
  box-sizing: border-box;
}

.calendar__item:focus {
  outline: none;
}

.calendar__item.selected {
  font-weight: 700;
  border-radius: 50%;
  background: #1a73e8;
  color: white;
  outline: none;
  border: none;
}

.calendar__item.selectDay {
  position: relative;
  background: #1a73e8;
  color: white;
  border-radius: 50%;
  border: none;
  z-index: 200;
}

.calendar__item.gray,
.calendar__item.gray:hover {
  color: #c4cee5;
  display: flex;
  justify-content: center;
  align-items: center;
}

.input__container {
  display: flex;
  justify-content: space-around;
}

.input {
  height: 30px;
  border-radius: 8px;
  text-align: center;
  align-self: center;
  border: 1px solid #1a73e8;
}

.shadow {
  position: absolute;
  display: inline-block;
  z-index: 10;
  top: 0;
  background-color: #f4f6fa;
  height: 50px;
  width: 50px;
}

.shadow.right {
  left: 50%;
}

.shadow.left {
  right: 50%;
}

```



The calendar component

```typescript

//Calendar.tsx

import dayjs, { Dayjs } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat'; // add line
import backArrow from '../assets/images/back.svg';
import forwardArrow from '../assets/images/forward.svg';
import { useState } from 'react';
import { calendarObjectGenerator } from '../helper/calendarObjectGenerator';
import './style.css';

dayjs.extend(customParseFormat); // add line

const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'];

export const Calendar = () => {

  const [currentDate, setCurrentDate] = useState<Dayjs>(dayjs(Date.now()));
  const [inputValue, setInputValue] = useState<string>('');

  const daysListGenerator = calendarObjectGenerator(currentDate);

  const dateArrowHandler = (date: Dayjs) => {
    setCurrentDate(date);
  };

  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const date = event.target.value;

    setInputValue(date);

    // check if the entered date is valid.
    const isValidDate = dayjs(date, 'DD.MM.YYYY').isValid();

    if (!isValidDate || date.length < 10) return;
    
    //if you pass date without specifying the format ('DD.MM.YYYY'), 
    // you might get an error when you decide to edit a selected date in the input field.
    
    setCurrentDate(dayjs(date, 'DD.MM.YYYY'));
  };
  
  

  const handlePreviousMonthClick = (day: number) => {
  
    const dayInPreviousMonth = currentDate.subtract(1, 'month').date(day);
    
    setCurrentDate(dayInPreviousMonth);

    setInputValue(dayInPreviousMonth.format('DD.MM.YYYY')); // add line
  };
  
  
   const handleCurrentMonthClick = (day: number) => {
   
    const dayInCurrentMonth = currentDate.date(day);
    
    setCurrentDate(dayInCurrentMonth);
    
    setInputValue(dayInCurrentMonth.format('DD.MM.YYYY')); // add line
  };
  

  const handleNextMonthClick = (day: number) => {
  
    const dayInNextMonth = currentDate.add(1, 'month').date(day);
    
    setCurrentDate(dayInNextMonth);
    
    setInputValue(dayInNextMonth.format('DD.MM.YYYY'));  // add line
  };
  
  
  
  return (
    <div className='calendar__container'>
      <input
        className='input'
        value={inputValue}
        onChange={handleInputChange}
      />
      <div className='control__layer'>
        <div className='month-year__layout'>
          <div className='year__layout'>
            <button
              className='back__arrow'
              onClick={() => dateArrowHandler(currentDate.subtract(1, 'year'))}
            >
              <img src={backArrow} alt='back arrow' />
            </button>
            <div className='title'>{currentDate.year()}</div>
            <button
              className='forward__arrow'
              onClick={() => dateArrowHandler(currentDate.add(1, 'year'))}
            >
              <img src={forwardArrow} alt='forward arrow' />
            </button>
          </div>
          <div className='month__layout'>
            <button
              className='back__arrow'
              onClick={() => dateArrowHandler(currentDate.subtract(1, 'month'))}
            >
              <img src={backArrow} alt='back arrow' />
            </button>
            <div className='new-title'>
              {daysListGenerator.months[currentDate.month()]}
            </div>
            <button
              className='forward__arrow'
              onClick={() => dateArrowHandler(currentDate.add(1, 'month'))}
            >
              <img src={forwardArrow} alt='forward arrow' />
            </button>
          </div>
        </div>
        <div className='days'>
          {weekDays.map((el, index) => (
            <div key={`${el}-${index}`} className='day'>
              {el}
            </div>
          ))}
        </div>
        <div className='calendar__content'>
          <div className={'calendar__items-list'}>
            {daysListGenerator.prevMonthDays.map((el, index) => {
              return (
               <div 
                key={`${el}/${index}`}
                className='calendar__day'
                onClick={() => handlePreviousMonthClick(el)}
                > 
                <button
                  className='calendar__item gray'
									>
                  {el}
                </button>
               </div>
              );
            })}
            {daysListGenerator.days.map((el, index) => {
              return (
                <div 
                key={`${index}-/-${el}`} 
                className='calendar__day' 
                onClick={() => handleCurrentMonthClick(el)							 
               >
                  <button
                    className={`calendar__item 
                      ${+el === +daysListGenerator.day ? 'selected' : ''}`}
                  >
                    <div className='day__layout'>
                      <div className='text'>{el.toString()}</div>
                    </div>
                  </button>
                </div>
              );
            })}

            {daysListGenerator.remainingDays.map((el, idx) => {
              return (
                <div 
                 key={`${idx}----${el}`}
                className='calendar__day'
                onClick={() => handleNextMonthClick(el)}
                > 
                <button 
                className='calendar__item gray' 
                >
                  {el}
                </button>
               </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
};


```

```typescript

//back.svg
<?xml version="1.0" encoding="utf-8"?>
<svg width="20px" height="20px" viewBox="0 0 1000 1000" class="icon"  version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M768 903.232l-50.432 56.768L256 512l461.568-448 50.432 56.768L364.928 512z" fill="#000000" /></svg>

```


```typescript

//forward.svg
<?xml version="1.0" encoding="utf-8"?>
<svg width="20px" height="20px" viewBox="0 0 1000 1000" class="icon"  version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M256 120.768L306.432 64 768 512l-461.568 448L256 903.232 659.072 512z" fill="#000000" /></svg>

```


The helper function for generating necessary data.

```typescript

//calendarObjectGenerator.tsx

import dayjs, { Dayjs } from 'dayjs';

import LocaleData from 'dayjs/plugin/localeData';

dayjs.extend(LocaleData);

type GeneratedObjectType = {
  prevMonthDays: number[];
  days: number[];
  remainingDays: number[];
  day: number;
  months: string[];
};

export const calendarObjectGenerator = (
  currentDate: Dayjs
): GeneratedObjectType => {
  const numOfDaysInPrevMonth = currentDate.subtract(1, 'month').daysInMonth();
  const firstDayOfCurrentMonth = currentDate.startOf('month').day();
  return {
    days: Array.from(
      { length: currentDate.daysInMonth() },
      (_, index) => index + 1
    ),
    day: Number(currentDate.format('DD')),
    months: currentDate.localeData().months(),

    prevMonthDays: Array.from(
      { length: firstDayOfCurrentMonth },
      (_, index) => numOfDaysInPrevMonth - index
    ).reverse(),

    remainingDays: Array.from(
      { length: 6 - currentDate.endOf('month').day() },
      (_, index) => index + 1
    ),
  };
};


```

```typescript

//App.tsx

import { Calendar } from './Calendar/Calendar';

function App() {
  return (
    <>
      <Calendar />
    </>
  );
}

export default App;


```



Ensure that at this point, your app works as expected.


![a date picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m0uwxfddrjomsyife1to.gif)


Most of what we will be doing will be in our Calendar component, so
the other files will stay the same.


## Adding a Second Input field.

Let us add another input for the second day. We can also use a masked input instead of having two different inputs. 


Now we are going to be changing things up a bit in the Calendar component. 

Our **`inputValue`** state will take a new look, the functions where we use **`setInputValue`** namely **`handlePreviousMonthClick`**, **`handleCurrentMonthClick`** and **`handleNextMonthClick`** will change and we will make some changes to our **input elements** as well:

Make the necessary changes in the Calendar.tsx file as shown below.

```typescript

//Calendar.tsx


  //changes in state
  const [inputValue, setInputValue] = useState({
    firstInput: '',
    secondInput: '',
  });
  
  // destructure inputValue
  const {firstInput, secondInput} = inputValue;
  
  
  //changes in function
    const handlePreviousMonthClick = (day: number) => {
    const dayInPreviousMonth = currentDate.subtract(1, 'month').date(day);
    setCurrentDate(dayInPreviousMonth);

    // setInputValue(dayInPreviousMonth.format('DD.MM.YYYY')); remove this line
  };

  const handleCurrentMonthClick = (day: number) => {
    const dayInCurrentMonth = currentDate.date(day);
    setCurrentDate(dayInCurrentMonth);
    // setInputValue(dayInCurrentMonth.format('DD.MM.YYYY')); remove this line
  };

  const handleNextMonthClick = (day: number) => {
    const dayInNextMonth = currentDate.add(1, 'month').date(day);
    setCurrentDate(dayInNextMonth);
    // setInputValue(dayInNextMonth.format('DD.MM.YYYY')); remove this line
  };
  
  
  
  // changes in input fields
       <div className='input__container'> //add this container
        <input
          name='firstInput'        //add this line
          className='input'
          value={firstInput}       //add this line
          onChange={handleInputChange}
        />
        <input                     //add the second input
          name='secondInput'
          className='input'
          value={secondInput} 
          onChange={handleInputChange}
        />
      </div>

```


## The RangePicker function.

Next, we want to allow users to select two dates and visually mark the selected dates as well as the days in between them. Let's focus on implementing this feature now.

We are about to create a little complex function in the `Calendar.tsx` file, so bear with me as I explain each line in detail. The primary goal of this function is to allow us to select our `firstInput` (or first day) and `secondInput` (or second day). We'll name this function `rangePicker`.


```typescript

// Calendar.tsx


...
  
  //check explanation
  const rangePicker = (day: Dayjs) => {
 
     const isTheSameYear =
      currentDate.year() === dayjs(firstInput, 'DD.MM.YYYY').get('year');
      
     const isFirstInputAfterSecondInput = dayjs(
      firstInput,
      'DD.MM.YYYY'
    ).isAfter(day);
    
    
    const isCurrentMonthLessThanFirstInputMonth =
      currentDate.month() < dayjs(firstInput, 'DD.MM.YYYY').get('month');
      
      
    const isCurrentYearLessThanFirstInputYear =
      currentDate.year() < dayjs(firstInput, 'DD.MM.YYYY').get('year');
      
      
      //we do not want to be able to select the same day
      
     const isTheSameDay =
      dayjs(firstInput, 'DD.MM.YYYY').format('DD.MM.YYYY') ===
      day.format('DD.MM.YYYY');
      
  
    if (!firstInput && !secondInput) {
    
    // if there is no firstInput and no secondInput, 
    // then the first clicked value should be the firstInput. 
    
      setInputValue({
        ...inputValue,
        firstInput: day.format('DD.MM.YYYY'),
      });
      
    } else if (firstInput && !secondInput) {
    //we do not want to be able to select the same day
     if (isTheSameDay) return;
    
    // if there is a firstInput value, and no secondInput,
    // check to see if the newly selected date is not before the firstInput date
    // if the newly selected date is earlier than the firstInput date then
    // swap the dates
    // if not, set the secondInput to the selected date.
    
        if (
       isFirstInputAfterSecondInput ||
        (isTheSameYear && isCurrentMonthLessThanFirstInputMonth) ||
        isCurrentYearLessThanFirstInputYear
      ) {
        setInputValue({
          ...inputValue,
          secondInput: firstInput,
          firstInput: day.format('DD.MM.YYYY'),
        });

        return;
      }
      

      setInputValue({
        ...inputValue,
        secondInput: day.format('DD.MM.YYYY'),
      });
      
    } else if (firstInput && secondInput) {
    
    //if the user clicks again when there are both inputs,
    // clear the secondInput and set the firstInput to the selected value. 
      setInputValue({
        firstInput: day.format('DD.MM.YYYY'),
        secondInput: '',
      });
    }
  };
  
  
  ...
  
 

```

Do not worry if your calendar does not look exactly like the images below yet, we are going to get there. 

The `rangePicker` function checks if there is a `firstInput` and `secondInput`.  

- If there is no `firstInput` and no `secondInput`, then we can assume that the first clicked value should be the first input. Therefore, we set the first clicked value to `firstInput`.

- If there is a `firstInput` value and no `secondInput` value then we can assume that the user wants to select a `secondInput` value. The selected value is then set to `secondInput`. But wait, there is a caveat.

    - We do not want the second day to be the same as the first day, hence the check for **isTheSameDay.** As you can see, we are not allowed to select the same day.


![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjncmir9z11gunmcdo5k.gif)


   - We also need to check if the second selected date is after (or greater than) the first date. If the `firstInput` value is after ( or greater than) the second selected date, then we can just swap the dates out. As shown below.
    
    You can see that when 30th June was selected as the `firstInput` and 3rd June as the second input, the dates swapped automatically.



![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqw9z3qumzwz0ovzzdke.gif)


- Lastly if there are both `firstInput` and `secondInput`, then when the user clicks on another day we just need to clear the `secondInput` and set the `firstInput` to the clicked date.


For simplicity, we will only be using this function in the current month. We can always customize it to meet our specifications. 

So call the range picker function inside the `handleCurrentMonthClick` function like so:


```typescript
  const handleCurrentMonthClick = (day: number) => {
    const dayInCurrentMonth = currentDate.date(day);
    setCurrentDate(dayInCurrentMonth);

    rangePicker(dayInCurrentMonth); // add line 
  };
```

Now we should be able to select two dates one for the `firstInput` and the other for the `secondInput`. 

That is not all though, we need to be able to highlight both of the selected days and the days in-between them to get something like this:


![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npsbxfa7kz52ux89ev2u.png)


A quick reminder: If you ever get lost, do not worry, I will provide the entire files at the end. 


## Highlighting Days of the Current Month

Let’s start by highlighting the selected days. Make the necessary adjustments in your `Calendar.tsx` file. 


```typescript

//Calendar.tsx

//add function
  const highlightDays = (el: number) => {
    if (!secondInput) return;

    return (
      currentDate?.set('date', el).isAfter(dayjs(firstInput, 'DD.MM.YYYY')) &&
      currentDate?.set('date', el).isBefore(dayjs(secondInput, 'DD.MM.YYYY'))
    );
  };

...

return (

...

         {daysListGenerator.days.map((el, index) => {
           //add lines
           const formattedCurrentMonthDates = currentDate
                .set('date', el)
                ?.format('DD.MM.YYYY');

              const isDayEqualFirstInput =
                firstInput === formattedCurrentMonthDates;

              const isDayEqualSecondInput =
                secondInput === formattedCurrentMonthDates;

              const applyGrayBackground = !(
                isDayEqualFirstInput || isDayEqualSecondInput
              );

              return (
                <div
                  key={`${index}-/-${el}`}
                  className='calendar__day'
                  onClick={() => handleCurrentMonthClick(el)}
                >
                  <button
                
                    className={`calendar__item   //add lines
                    ${
                        +el === +daysListGenerator.day &&
                       !(firstInput || secondInput)
                          ? 'selected'
                          : isDayEqualFirstInput || 
                           isDayEqualSecondInput
                          ? 'selectDay'
                          : ''
                      }`}
                      
                  style={{                       //add lines 
                      backgroundColor: `${
                        applyGrayBackground && highlightDays(el)
                          ? '#F4F6FA'
                          : ''
                      }`,
                    }}
                      
                      
...
```

While mapping through the days array, we will create some variables to help us pick some important moments. 

**`formattedCurrentMonthDates`** ensures that we convert the numbers in the array to dates of the format ‘DD.MM.YYYY’

**`isDayEqualFirstInput` and `isDayEqualSecondInput`** are checking the day that is equal to the firstInput or the secondInput value, since we need to know these days and style them accordingly. If the day is equal to either `firstInput` or `secondInput`, we can then add the **`selectDay`** class for styling. 

**`applyGrayBackground`** checks if the value is not equal to the firstInput or secondInput. We are applying gray background to the days that are neither firstInput or secondInput but that are in-between them.

As you might have noticed, the **`highlightDays`** function takes in each of the integers we are mapping through, converts them to dates and checks if the date is between the `firstInput` and the `secondInput`.



## Highlighting Days of the Previous months.

Next is to ensure that the days of the previous months are also properly highlighted when they fall between selected days. Something like this:



![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8hzhqqfbrr0mqp8rcyjf.png)


To do this, let’s add some lines to the `prevMonthDays` map function.

```typescript

//Calendar.tsx
...

//add this Day.js plugin

import isBetween from 'dayjs/plugin/isBetween';

...

// add line
dayjs.extend(isBetween);

...

return (

...
 //add lines
            {daysListGenerator.prevMonthDays.map((el, index) => {
   
						     
                const formatPrevMonthsDates = currentDate
                .subtract(1, 'month')
                .set('date', el)
                ?.format('DD.MM.YYYY');
   
   //add lines              
                const isBetween = currentDate
                .subtract(1, 'month')
                .set('date', el)
                .isBetween(
                  dayjs(firstInput, 'DD.MM.YYYY'),
                  dayjs(secondInput, 'DD.MM.YYYY')
                );
   
   //add line

	const isFirstDay = firstInput === formatPrevMonthsDates;

              return (
                <div
                  className='calendar__day'
                  key={`${el}/${index}`}
                  onClick={() => handlePreviousMonthClick(el)}
                >
                  <button
                    className='calendar__item gray'
                    style={{                       //add lines
                      backgroundColor: `${
                        isBetween || (isFirstDay && secondInput)
                          ? '#F4F6FA'
                          : ''
                      }`,
                    }}
                  >
                    {el}
                  </button>
                </div>
              );
            })}
  ...

```


Again, we will convert the numbers in the **`prevMonthDays`** array into the date of the previous month. This is why we are calling the **`subtract(1, 'month')`** method on the currentDate. 

The next thing is to check if the date is between the `firstInput` and the `secondInput`. To do this, we need to include another DayJs  **`isBetween`** plugin.

Lastly we use **`isFirstDay`** to check if the day of the prevMonth is equal to the selected firstInput, we might need this for styling as well.  

If you have done everything, you should be able to see the `prevMonthDays` highlighted provided they fall between the `firstInput` and the `lastInput`.


## Highlighting The Remaining Days (days of the next month).

Lastly we should do the same thing for the `remainingDays`. We will highlight them when they fall between the `firstInput` and the `lastInput`. See, they are currently not highlighted so let’s fix that. 


![range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61p68err899iw9wzfh1m.png)


We are going to create another super contrived function to handle the possible cases with the goal of achieving this.


![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7ppe6kte7diw1p465f6.png)


Let us call the function `remainingDaysIsBetween`. Create this function inside your `Calendar.tsx` file.


```typescript
  
  ...
      const remainingDaysIsBetween = () => {
                const firstDay = dayjs(firstInput, 'DD.MM.YYYY');
                const secondDay = dayjs(secondInput, 'DD.MM.YYYY');

                const firstYear = firstDay?.year();
                const secondYear = secondDay?.year();

                if (
                  firstYear === secondYear &&
                  currentDate.year() === firstYear
                ) {
                  return (
                    firstDay &&
                    firstDay?.month() <= currentDate.month() &&
                    secondDay &&
                    secondDay?.month() > currentDate.month()
                  );
                } else if (
                  secondYear &&
                  firstYear &&
                  secondYear > firstYear &&
                  currentDate.year() <= secondYear
                ) {
                  if (
                    currentDate.year() === firstYear &&
                    currentDate.month() < firstDay?.month()
                  ) {
                    return;
                  }
                  if (currentDate.year() < secondYear) {
                    return (
                      (firstDay &&
                        firstDay?.year() === currentDate.year() &&
                        firstDay?.month() >= currentDate.month()) ||
                      (secondDay &&
                        currentDate.year() <= secondDay?.year() &&
                        currentDate.year() >= firstDay?.year())
                    );
                  } else {
                    return (
                      (firstDay &&
                        firstDay?.year() === currentDate.year() &&
                        firstDay?.month() <= currentDate.month()) ||
                      (secondDay &&
                        currentDate.year() <= secondDay?.year() &&
                        secondDay?.month() > currentDate.month())
                    );
                  }
                }
              };
     ...
```


Now there is a lot going on here because I was trying to catch the obvious edge cases. You can play around with it and optimize the function. 

The function `remainingDaysIsBetween` checks whether the current date falls within a specific range defined by two input dates (`firstInput` and `secondInput`). These dates are expected to be in the format `DD.MM.YYYY`. Let's break down the conditions step by step:

```typescript
const firstDay = dayjs(firstInput, 'DD.MM.YYYY');
const secondDay = dayjs(secondInput, 'DD.MM.YYYY');

const firstYear = firstDay?.year();
const secondYear = secondDay?.year();
```

`firstInput` and `secondInput` are parsed into Day.js objects (`firstDay` and `secondDay`).

We then extract the years of the parsed objects into `firstYear`  and  `secondYear` .

It is very important to remeber that  `currentDate`  is a Day.js object representing the last selected date and its value changes as you move back and forth between months and years with the control arrows. Let’s look further into the conditions in the  `remainingDaysIsBetween` function.


## Condition 1

We check that both the firstInput and secondInput dates are in the Same Year and that the current year is the same as the first year: 

```typescript
if (firstYear === secondYear && currentDate.year() === firstYear) {
  return (
    firstDay &&
    firstDay?.month() <= currentDate.month() &&
    secondDay &&
    secondDay?.month() > currentDate.month()
  );
}
```


If the condition is true, then we ensure that we are only highlighting the remaining days for that particular month where the `firstInput`’s month is less than or equal to the `currentDate`’s month and the `secondInput`’s month is greater than the `currentDate`’s month. 

For instance when the firstInput is `18.06.2024` and the secondInput is `16.07.2024`. When move between months with our month control arrows, when we are in June, 

**`firstDay?.month() <= currentDate.month()`** returns true because the `firstDay.month` is June and the **`currentDate.month()`** is also June (remember that currentDate changes as you move around) also, the second check **`secondDay?.month() > currentDate.month()`**  returns true because **`secondDay.month()`** is July and remember that while we are viewing June, **`currentDate.month()`** is still June. Because both inequalities return true when we are viewing June, the remaining days in June are highlighted with a gray color.

However when we move forward to July, the second check **`secondDay?.month() > currentDate.month()`** returns false since  **`secondDay?.month()`** is July and `currentDate.month()`** is also July. Hence the remaining days in July are not highlighted


![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wu1thhjgaayn6igm748.png)


![Range picker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/30ifc6r8uah5j61gvyvp.png)

## Condition 2

We check for when both dates (`firstInput` and `secondInput`) are in different years and the current date is less than or equal to the secondInput year.


```typescript
else if (secondYear && firstYear && secondYear > firstYear && currentDate.year() <= secondYear) {
  // Sub-condition 2.1: Current Year Matches First Year but Before First Month
  if (currentDate.year() === firstYear && currentDate.month() < firstDay?.month()) {
    return;
  }
  // Sub-condition 2.2: Current Year is Before Second Year
  if (currentDate.year() < secondYear) {
    return (
      (firstDay &&
        firstDay?.year() === currentDate.year() &&
        firstDay?.month() >= currentDate.month()) ||
      (secondDay &&
        currentDate.year() <= secondDay?.year() &&
        currentDate.year() >= firstDay?.year())
    );
  } else {
    // Sub-condition 2.3: Current Year Matches Second Year
    return (
      (firstDay &&
        firstDay?.year() === currentDate.year() &&
        firstDay?.month() <= currentDate.month()) ||
      (secondDay &&
        currentDate.year() <= secondDay?.year() &&
        secondDay?.month() > currentDate.month())
    );
  }
}

```

## Conclusion

Following the first condition, we can play around with other conditions to meet our specifications. If you have done everything as explained in this article, you should now have a fully functional Calendar with a range picker. There are still multiple ways to improve on this code.

If you have followed along till this point, you can as well have the whole changes we have made to the `Calendar.tsx` file, so here you have it:

```typescript
//Calendar.tsx

import dayjs, { Dayjs } from 'dayjs';
import backArrow from '../assets/images/back.svg';
import forwardArrow from '../assets/images/forward.svg';
import './style.css';
import { useState } from 'react';
import { calendarObjectGenerator } from '../helper/calendarObjectGenerator';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import isBetween from 'dayjs/plugin/isBetween';

dayjs.extend(customParseFormat);
dayjs.extend(isBetween);

const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'];

export const Calendar = () => {
  const [currentDate, setCurrentDate] = useState<Dayjs>(dayjs(Date.now()));

  const [inputValue, setInputValue] = useState({
    firstInput: '',
    secondInput: '',
  });

  const { firstInput, secondInput } = inputValue;

  const daysListGenerator = calendarObjectGenerator(currentDate);

  const dateArrowHandler = (date: Dayjs) => {
    setCurrentDate(date);
  };

  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const date = event.target.value;

    setInputValue({ ...inputValue, [event.target.name]: event.target.value });

    // check if the entered date is valid.
    const isValidDate = dayjs(date, 'DD.MM.YYYY').isValid();

    if (!isValidDate || date.length < 10) return;

    setCurrentDate(dayjs(date, 'DD.MM.YYYY'));
  };

  const rangePicker = (day: Dayjs) => {
    const isTheSameYear =
      currentDate.year() === dayjs(firstInput, 'DD.MM.YYYY').get('year');
    const isFirstInputAfterSecondInput = dayjs(
      firstInput,
      'DD.MM.YYYY'
    ).isAfter(day);

    const isCurrentMonthLessThanFirstInputMonth =
      currentDate.month() < dayjs(firstInput, 'DD.MM.YYYY').get('month');
    const isCurrentYearLessThanFirstInputYear =
      currentDate.year() < dayjs(firstInput, 'DD.MM.YYYY').get('year');

    const isTheSameDay =
      dayjs(firstInput, 'DD.MM.YYYY').format('DD.MM.YYYY') ===
      day.format('DD.MM.YYYY');

    if (!firstInput && !secondInput) {
      setInputValue({
        ...inputValue,
        firstInput: day.format('DD.MM.YYYY'),
      });
    } else if (firstInput && !secondInput) {
      if (isTheSameDay) return;

      if (
        isFirstInputAfterSecondInput ||
        (isTheSameYear && isCurrentMonthLessThanFirstInputMonth) ||
        isCurrentYearLessThanFirstInputYear
      ) {
        setInputValue({
          ...inputValue,
          secondInput: firstInput,
          firstInput: day.format('DD.MM.YYYY'),
        });

        return;
      }

      setInputValue({
        ...inputValue,
        secondInput: day.format('DD.MM.YYYY'),
      });
    } else if (firstInput && secondInput) {
      setInputValue({
        firstInput: day.format('DD.MM.YYYY'),
        secondInput: '',
      });
    }
  };

  const handlePreviousMonthClick = (day: number) => {
    const dayInPreviousMonth = currentDate.subtract(1, 'month').date(day);
    setCurrentDate(dayInPreviousMonth);
  };

  const handleCurrentMonthClick = (day: number) => {
    const dayInCurrentMonth = currentDate.date(day);
    setCurrentDate(dayInCurrentMonth);

    rangePicker(dayInCurrentMonth);
  };

  const handleNextMonthClick = (day: number) => {
    const dayInNextMonth = currentDate.add(1, 'month').date(day);

    setCurrentDate(dayInNextMonth);
  };

  const highlightDays = (el: number) => {
    if (!secondInput) return;

    return (
      currentDate?.set('date', el).isAfter(dayjs(firstInput, 'DD.MM.YYYY')) &&
      currentDate?.set('date', el).isBefore(dayjs(secondInput, 'DD.MM.YYYY'))
    );
  };

 const remainingDaysIsBetween = () => {
     const firstDay = dayjs(firstInput, 'DD.MM.YYYY');
      const secondDay = dayjs(secondInput, 'DD.MM.YYYY');

      const firstYear = firstDay?.year();
      const secondYear = secondDay?.year();

       if (
          firstYear === secondYear &&
           currentDate.year() === firstYear
          ) {
          
           return (
            firstDay &&
               firstDay?.month() <= currentDate.month() &&
                secondDay &&
                 secondDay?.month() > currentDate.month()
                  );
                } else if (
                  secondYear &&
                  firstYear &&
                  secondYear > firstYear &&
                  currentDate.year() <= secondYear
                ) {
                  if (
                    currentDate.year() === firstYear &&
                    currentDate.month() < firstDay?.month()
                  ) {
                    return;
                  }
                  if (currentDate.year() < secondYear) {
                    return (
                      (firstDay &&
                        firstDay?.year() === currentDate.year() &&
                        firstDay?.month() >= currentDate.month()) ||
                      (secondDay &&
                        currentDate.year() <= secondDay?.year() &&
                        currentDate.year() >= firstDay?.year())
                    );
                  } else {
                    return (
                      (firstDay &&
                        firstDay?.year() === currentDate.year() &&
                        firstDay?.month() <= currentDate.month()) ||
                      (secondDay &&
                  currentDate.year() <= secondDay?.year() &&
                    secondDay?.month() > currentDate.month())
                  );
          }
       }
   };


  return (
    <div className='calendar__container'>
      <div className='input__container'>
        <input
          name='firstInput'
          className='input'
          value={firstInput}
          onChange={handleInputChange}
        />
        <input
          name='secondInput'
          className='input'
          value={secondInput}
          onChange={handleInputChange}
        />
      </div>

      <div className='control__layer'>
        <div className='month-year__layout'>
          <div className='year__layout'>
            <button
              className='back__arrow'
              onClick={() => dateArrowHandler(currentDate.subtract(1, 'year'))}
            >
              <img src={backArrow} alt='back arrow' />
            </button>
            <div className='title'>{currentDate.year()}</div>
            <button
              className='forward__arrow'
              onClick={() => dateArrowHandler(currentDate.add(1, 'year'))}
            >
              <img src={forwardArrow} alt='forward arrow' />
            </button>
          </div>
          <div className='month__layout'>
            <button
              className='back__arrow'
              onClick={() => dateArrowHandler(currentDate.subtract(1, 'month'))}
            >
              <img src={backArrow} alt='back arrow' />
            </button>
            <div className='new-title'>
              {daysListGenerator.months[currentDate.month()]}
            </div>
            <button
              className='forward__arrow'
              onClick={() => dateArrowHandler(currentDate.add(1, 'month'))}
            >
              <img src={forwardArrow} alt='forward arrow' />
            </button>
          </div>
        </div>
        <div className='days'>
          {weekDays.map((el, index) => (
            <div key={`${el}-${index}`} className='day'>
              {el}
            </div>
          ))}
        </div>
        <div className='calendar__content'>
          <div className={'calendar__items-list'}>
            {daysListGenerator.prevMonthDays.map((el, index) => {
              const formatPrevMonthsDates = currentDate
                .subtract(1, 'month')
                .set('date', el)
                ?.format('DD.MM.YYYY');

              const isBetween = currentDate
                .subtract(1, 'month')
                .set('date', el)
                .isBetween(
                  dayjs(firstInput, 'DD.MM.YYYY'),
                  dayjs(secondInput, 'DD.MM.YYYY')
                );

              const isFirstDay = firstInput === formatPrevMonthsDates;

              return (
                <div
                  className='calendar__day'
                  key={`${el}/${index}`}
                  onClick={() => handlePreviousMonthClick(el)}
                >
                  <button
                    className='calendar__item gray'
                    style={{
                      backgroundColor: `${
                        isBetween || (isFirstDay && secondInput)
                          ? '#F4F6FA'
                          : ''
                      }`,
                    }}
                  >
                    {el}
                  </button>
                </div>
              );
            })}
            {daysListGenerator.days.map((el, index) => {
              const formattedCurrentMonthDates = currentDate
                .set('date', el)
                ?.format('DD.MM.YYYY');

              const isDayEqualFirstInput =
                firstInput === formattedCurrentMonthDates;

              const isDayEqualSecondInput =
                secondInput === formattedCurrentMonthDates;

              const applyGrayBackground = !(
                isDayEqualFirstInput || isDayEqualSecondInput
              );

              return (
                <div
                  key={`${index}-/-${el}`}
                  className='calendar__day'
                  onClick={() => handleCurrentMonthClick(el)}
                >
                  <button
                    className={`calendar__item 
                      ${
                        +el === +daysListGenerator.day &&
                        !(firstInput || secondInput)
                          ? 'selected'
                          : isDayEqualFirstInput || isDayEqualSecondInput
                          ? 'selectDay'
                          : ''
                      }`}
                    style={{
                      backgroundColor: `${
                        applyGrayBackground && highlightDays(el)
                          ? '#F4F6FA'
                          : ''
                      }`,
                    }}
                  >
                    <div className='day__layout'>
                      <div className='text'>{el.toString()}</div>
                    </div>
                  </button>
                  {firstInput && secondInput && isDayEqualFirstInput && (
                    <span className='shadow right'></span>
                  )}
                  {firstInput && secondInput && isDayEqualSecondInput && (
                    <span className='shadow left'></span>
                  )}
                </div>
              );
            })}

            {daysListGenerator.remainingDays.map((el, idx) => {
              return (
                <div
                  key={`${idx}----${el}`}
                  className='calendar__day'
                  onClick={() => handleNextMonthClick(el)}
                >
                  <button
                    className='calendar__item gray'
                    style={{
                      background: `${
                        remainingDaysIsBetween() ? '#F4F6FA' : ''
                      }`,
                    }}
                  >
                    {el}
                  </button>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
};


```

Thank you for following this till the end of this tutorial. 

If you so desire, you can play around with what we have done so far, and find several areas to improve.