Airbnb clone, configure the DayPickerInput component

Join the 2022 Full-Stack Web Dev Bootcamp!


This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.

So now we are ready to configure the calendar.

Let’s start by setting the format for the dates.

I found some instructions in the date picker documentation, and I’m going to follow them.

First install date-fns. This is a library useful to handle dates.

npm install date-fns

Then we import the date-fns/format and date-fns/parse functions in the DateRangePicker.js component:

components/DateRangePicker.js

import dateFnsFormat from 'date-fns/format'
import dateFnsParse from 'date-fns/parse'

and we define the parseDate and formatDate functions:

const parseDate = (str, format, locale) => {
  const parsed = dateFnsParse(str, format, new Date(), { locale })
  return DateUtils.isDate(parsed) ? parsed : null
}

const formatDate = (date, format, locale) =>
  dateFnsFormat(date, format, { locale })

We’ll pass them to the DayPickerInput component.

We also define the format for the date. I like the format day monthname year, for example 20 Nov 2019. This is how we define it:

const format = 'dd MMM yyyy'

Cool! Now in the JSX we change <DayPickerInput /> to

<DayPickerInput
  formatDate={formatDate}
  format={format}
  parseDate={parseDate}
  placeholder={`${dateFnsFormat(new Date(), format)}`}
/>

in the 2 places where it appears.

This is the code so far:

import DayPickerInput from 'react-day-picker/DayPickerInput'
import { DateUtils } from 'react-day-picker'
import 'react-day-picker/lib/style.css'
import dateFnsFormat from 'date-fns/format'
import dateFnsParse from 'date-fns/parse'

const parseDate = (str, format, locale) => {
  const parsed = dateFnsParse(str, format, new Date(), { locale })
  return DateUtils.isDate(parsed) ? parsed : null
}

const formatDate = (date, format, locale) =>
  dateFnsFormat(date, format, { locale })

const format = 'dd MMM yyyy'

export default function DateRangePicker() {
  return (
    <div className="date-range-picker-container">
      <div>
        <label>From:</label>
        <DayPickerInput
          formatDate={formatDate}
          format={format}
          parseDate={parseDate}
          placeholder={`${dateFnsFormat(new Date(), format)}`}
        />
      </div>
      <div>
        <label>To:</label>
        <DayPickerInput
          formatDate={formatDate}
          format={format}
          parseDate={parseDate}
          placeholder={`${dateFnsFormat(new Date(), format)}`}
        />
      </div>
      <style jsx>
        {`
          .date-range-picker-container div {
            display: grid;
            grid-template-columns: 30% 70%;
            padding: 10px;
          }
          label {
            padding-top: 10px;
          }
        `}
      </style>
      <style jsx global>
        {`
          .DayPickerInput input {
            width: 120px;
            padding: 10px;
            font-size: 16px;
          }
        `}
      </style>
    </div>
  )
}

And the result:

See? The date is now displayed nicely in the date picker.

Now we need to disable the ability to select all dates in the past.

Why? Because we can’t book dates in the past. Only in the future. We can’t go back in time.

We can do so by configuring DayPickerInput. I found a guide on the official website https://react-day-picker.js.org/docs/matching-days in “Matching days with modifiers” but this was talking about the DayPicker component, which is a different one than the DayPickerInput we’re using.

DayPicker is the calendar that appears when we click the input element with the formatted date.

Turns out we can pass properties to DayPicker by using the dayPickerProps prop on DayPickerInput:

<DayPickerInput
  formatDate={formatDate}
  format={format}
  parseDate={parseDate}
  placeholder={`${dateFnsFormat(new Date(), format)}`}
  dayPickerProps={{
    modifiers: {
      disabled: {
        before: new Date()
      }
    }
  }}
/>

This part is a little confusing, but every time we use a library, we must first understand how it works from the docs, and adapt to it. It’s always work we need to do.

In this particular case, I found the docs about dayPickerProps in this page: https://react-day-picker.js.org/docs/input, in “Customizing the DayPicker”.

See, we can’t select dates in the past now:

Ok so now that we denied selecting dates in the past, let’s add 2 variables in the components/DateRangePicker.js component, and we will update them when the selected dates change.

First import useState from React:

import { useState } from 'react'

Then we add them right after we define the component:

components/DateRangePicker.js

///

export default function DateRangePicker() {
  const [startDate, setStartDate] = useState(new Date())
  const [endDate, setEndDate] = useState(new Date())

  return (
    //...the component JSX
  )
}

Now, inside the JSX we add a new prop to DayPickerInput to update those state variables when a new date has been selected:

<DayPickerInput
  formatDate={formatDate}
  format={format}
  parseDate={parseDate}
  placeholder={`${dateFnsFormat(new Date(), format)}`}
  dayPickerProps={{
    modifiers: {
      disabled: {
        before: new Date()
      }
    }
  }}
  onDayChange={(day) => {
    setStartDate(day)
  }}
/>

and here’s the end date

<DayPickerInput
  formatDate={formatDate}
  format={format}
  parseDate={parseDate}
  placeholder={`${dateFnsFormat(new Date(), format)}`}
  dayPickerProps={{
    modifiers: {
      disabled: {
        before: new Date()
      }
    }
  }}
  onDayChange={(day) => {
    setEndDate(day)
  }}
/>

See the code on GitHub

Next part: Sync the start and end dates

Want to become a better Web Developer? Join the 2022 Web Development Bootcamp!

⭐️⭐️⭐️ Join the 2022 Web Development Bootcamp ⭐️⭐️⭐️