Integrating Google's appointment scheduler into your NextJS application

This one is for the freelancers out there.

One of the most reoccurring features I hear from small businesses looking to grow their online presence is a quick and easy way to schedule appointments.

Many business owners already have a personal and business management system built on the free tools Google provides, such as Google Docs, Calendar, and Spreadsheets. However, they lack the capability of integrating it into their personal website. That's where we come in.

I have found using this approach to be easy for both the client and the developer. No need for a backend, no need for recurring payments, just a simple and seamless action directly to their appointment scheduler.

If you are a lover of NextJS, then I shall show you how to integrate this system into your next project.

The Google Side

1. Firstly, you need a Google account to use. Then navigate to Google Calendar and create a new appointment scheduler.

Appointment Scheduler

2. Configure the options, such as availability, required form fields, etc. Once you have completed the required fields and added a short description, save your planner.

Planning Options

3. Next, view the booking scheduler and click the share button.

Share Appointments

4. You will need two things here: One, the URL link for the calendar, and secondly, the inline styles that we will use for rendering the popup display.

Calendar Link

InLine iframe Code

Make sure to select the inline booking style.

The Next Side

pun intended

If you require authentication to access the availability to make a button, you will need the calendar.app.google link. Save this to a NEXT_PUBLIC key. Due to scope, we won't cover this in this post.

1. Firstly, create a trigger button to toggle the display state of the calendar.

  <button className={className} onClick={() => setShowModel(prev => !prev)}>
        Book an Appointment
        <span className="btn btn-icon bg-slate-600 hover:bg-slate-700 border-slate-600 hover:border-slate-700 text-white rounded-full ml-2">
          <FaPhone className="text-xl" />
        </span>
      </button>

Okay, so now we have a state button, we need to create a reference to the Modal that will hold the iframe snippet.

2. Create a dialog element and add the ref. Next insert the iframe code you copied earlier, removing the enclosing comments.

  const ref = useRef<HTMLDialogElement | null>(null);

  <dialog ref={ref} className="h-full w-full md:w-1/2 rounded-lg shadow-md">
        {/* USER IFRAME CODE HERE  */}
      </dialog>

3. Create a useEffect hook and add the show state as a dependency. Next, check if the ref.current exists and return if not, to avoid unnecessary errors. The ref.current will have methods.showModal() and a close() due to it being a dialog element.

  const [showModel, setShowModel] = useState(false);

  useEffect(() => {
    if (!ref.current) {
      return;
    }

    if (showModel) {
      ref.current.showModal();
    } else {
      ref.current.close();
    }
  }, [showModel]);

Check here for more Info - MDN Web Docs - HTML Dialog Element

Now we can handle the opening and closing states of the modal.

You can update the default styling of the iframe as per normal. I prefer to make the style 100% of the width and height of the container.

For a final touch to enhance UI.

4 On the dialog element, add an onclick function.

This function first checks if the ref.current exists; if it does not, it returns. This avoids any display errors.

Now get the dimensions of the modal size by using the inbuilt JS getBoundingClientRect method. Check whether the click location falls inside or outside of the modal. If it falls outside, simply call the close method and toggle the show state to false.

 <dialog
        ref={ref}
        onClick={(e) => {
          if (!ref.current) {
            return;
          }

          const dialogDimensions = ref.current.getBoundingClientRect();
          if (
            e.clientX < dialogDimensions.left ||
            e.clientX > dialogDimensions.right ||
            e.clientY < dialogDimensions.top ||
            e.clientY > dialogDimensions.bottom
          ) {
            ref.current.close();
            setShowModel(false);
          }
        }}
        className="h-full w-full md:w-1/2 rounded-lg shadow-md"
      >
        {/* USER IFRAME CODE HERE  */}
      </dialog>

And that's really all it is.

Test it out on your own NextJS applications. Google will now handle the UI and deliver availability based on the current settings and current bookings.

Another great thing is that everyone gets one appointment scheduler for free! A completely overpowered booking system for small businesses!

Peace!

Feeling Lazy? Heres my full code

import { FunctionComponent, useEffect, useRef, useState } from "react";
import { FaPhone } from "react-icons/fa";

interface BookCallBtnProps {
  className: string;
}

const BookCallBtn: FunctionComponent<BookCallBtnProps> = ({ className }) => {
  const ref = useRef<HTMLDialogElement | null>(null);
  const [showModel, setShowModel] = useState(false);

  useEffect(() => {
    if (!ref.current) {
      return;
    }

    if (showModel) {
      ref.current.showModal();
    } else {
      ref.current.close();
    }
  }, [showModel]);

  return (
    <>
      <button className={className} onClick={() => setShowModel(prev => !prev)}>
        Book an Appointment
        <span className="btn btn-icon bg-slate-600 hover:bg-slate-700 border-slate-600 hover:border-slate-700 text-white rounded-full ml-2">
          <FaPhone className="text-xl" />
        </span>
      </button>
      <dialog
        ref={ref}
        onClick={(e) => {
          if (!ref.current) {
            return;
          }

          const dialogDimensions = ref.current.getBoundingClientRect();
          if (
            e.clientX < dialogDimensions.left ||
            e.clientX > dialogDimensions.right ||
            e.clientY < dialogDimensions.top ||
            e.clientY > dialogDimensions.bottom
          ) {
            ref.current.close();
            setShowModel(false);
          }
        }}
        className="h-full w-full md:w-1/2 rounded-lg shadow-md"
      >
        {/* USER IFRAME CODE HERE  */}
      </dialog>
    </>
  );
};

export default BookCallBtn;

Avatar for DarrachBarneveld

Written by DarrachBarneveld

Addicted to building

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.