Introduction

I'm currently building a WordPress website for a company that rents out equipment. They want to allow their customers to rent equipment from their website, rather than having to call or stop in at the shop.

A couple years ago I built a website for a company that has studios that photographers can book for photo sessions. To add the booking functionality to this website I used the WooCommerce Bookings And Appointments plugin by PluginHive. I was planning on using this plugin again for the equipment rental company but the issue I was facing is that they want their website to work directly with their CRM, and this is pretty difficult to accomplish using a third-party plugin.

So, in order to add the booking functionality that integrates directly with the CRM I built a custom datepicker plugin using Svelte.

Screenshot of datepicker on dutch rentalz website

Building the datepicker

This post walks through a basic datepicker built with Svelte. The plugin I built for the equipment rental company is quite a bit more complex but this is a good starting point.

Define the basic calendar grid

We start by defining a general grid for a calendar month and initializing the grid with let rows = initRows(). This initRows function just describes how many days are in a week, weeks in a month, etc. We'll fill this out with data for the selected month and year in a minute.

javascript

/**
 * Create the calendar grid
 * @returns {Array} rows
 */
function initRows() {
  return [
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
  ];
}

let rows = initRows();

Fill out the calendar grid for the selected month and year

Next we fill out the calendar grid with the correct dates using the components onMount lifecycle function.

javascript

/**
 * Return an array of days
 * @returns {Array} days
 */
export function arrDays() {
  return ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
}

/**
 * Return a calendar month
 */
export function updateRows(month, year) {
  const rows = initRows();
  // pad the month with a zero if it's less than 10
  month = month.toString().padStart(2, "0");

  const firstDayOfCurrentMonth = format(
    startOfMonth(new Date(`${year}-${month}-01T12:00:00`)),
    "EEEEEE"
  );

  const lastDayOfCurrentMonth = +format(
    endOfMonth(new Date(`${year}-${month}-01T12:00:00`)),
    "d"
  );

  let row = 0;
  let col = 0;
  let start = false;
  let i = 0;

  // loop through each row (week)
  for (row = 0; row < 6; row++) {
    // loop through each day of the week and assign the date
    arrDays().forEach((daystr) => {
      if (i > lastDayOfCurrentMonth) {
        return;
      }

      if (!start && daystr === firstDayOfCurrentMonth) {
        i++;
        start = true;
      }

      rows[row][col] = i;
      col++;

      if (start) {
        i++;
      }
    })
    
    col = 0;
  }

  return rows;
}

/**
 * When the component mounts
 * update the rows array with data for current month and year
 */
onMount(() => {
  rows = updateRows(selectedMonth, selectedYear);
})

Updating the calendar grid

Now whenever we change the month and/or year we also need to update the calendar grid.

javascript

/**
 * Navigate months
 */
 function previousMonth() {
  selectedMonth--;

  if (selectedMonth <= 0) {
    selectedMonth = 12;
    selectedYear--;
  }

  // update the calendar grid for the newly selected month and year
  rows = updateRows(selectedMonth, selectedYear);
}

function nextMonth() {
  selectedMonth++;

  if (selectedMonth > 12) {
    selectedMonth = 1;
    selectedYear++;
  }

  // update the calendar grid for the newly selected month and year
  rows = updateRows(selectedMonth, selectedYear);
}

Displaying the calendar

Now that we have an array of days for the selected month and year we can display them in the calendar. This is

svelte

<table class="w-full">
  <thead>
    <tr>
      <!-- display the day headers -->
      {#each arrDays() as day}
        <th>
          <div class="flex justify-center w-full">
            <p
              class="text-base font-medium text-center text-gray-700 dark:text-gray-300"
            >
              {day}
            </p>
          </div>
        </th>
      {/each}
    </tr>
  </thead>

  <tbody>
    <!-- loop through each week -->
    {#each rows as col}
      <tr>
        <!-- loop through days of the week -->
        {#each col as d}
          <td>
            {#if d}
              <div class="flex justify-center w-full px-1 py-1 cursor-pointer">
                <button
                  on:click={() => {
                    selectDate(selectedYear, selectedMonth, d);
                  }}
                  class="text-center text-white transition-colors duration-200 bg-purple-700 border-none w-14 h-14 hover:bg-purple-600"
                  class:active-day-button={differenceInDays(
                      new Date(`${selectedFullDate}`),
                      new Date(`${selectedYear}-${padNumber(selectedMonth)}-${padNumber(d)}T00:00:00`)
                    ) === 0}
                >
                  {d}
                </button>
              </div>
            {/if}
          </td>
        {/each}
      </tr>
    {/each}
  </tbody>
</table>

View the project

For a full look at how this basic datepicker was created you can check out the GitHub repo. To see the datepicker in action check out the link below! Keep in mind this is only meant to be a starting point that you can build off so it is pretty basic, but it can be customized pretty extensively to meet your needs.

GitHub Repository

https://github.com/zpthree/svelte-datepicker

Live datepicker link

https://svelte-datepicker.netlify.app/