DEV Community

Cover image for Collect form submissions with NextJS & Google Sheets
Solomon Antoine
Solomon Antoine

Posted on

Collect form submissions with NextJS & Google Sheets

Collect form submissions with NextJS & Google Sheets

https://github.com/icodestuff-io/nextjs-google-sheets/raw/main/screenshots/nextjs-sheets.jpg

In this tutorial, learn how to build a form that save records into Google Sheets using NextJS & TailwindCSS.

Google Cloud

We want to login or register an account for Google Cloud, then create a new project like so:

Screen Shot 2022-02-01 at 20.59.42.png

Go to the APIs & Services page and click the ENABLE APIS AND SERVICES button then search for sheets

Screen Shot 2022-02-02 at 11.22.08.png

The click Enable

Screen Shot 2022-02-02 at 11.22.23.png

After you’ve enable the Google Sheets API, then click the Credentials on the left navigation. Then click your service account and add a new key using the JSON option like so:

Screen Shot 2022-02-02 at 12.48.08.png

After you download the json file, copy the private_key and client_email as they will later be using in the env file

Google Sheets

Next, we want to setup our Google Sheet and add the Name, Email, Phone & Message as columns like so:

Screen Shot 2022-02-01 at 21.12.50.png

Then make the Google Sheet public.

Setup our project

Setup NextJS

Next, we will want to create our NextJS project by using the following command:

$ npx create-next-app nextjs-sheets-form --typescript
Enter fullscreen mode Exit fullscreen mode

Setup TailwindCSS

Install TailwindCSS by running the following:

$ npm install -D tailwindcss postcss autoprefixer @tailwindcss/forms
$ npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

Next, go to your tailwind.config.js file and add the following:

module.exports = {
    content: [
        "./pages/**/*.{js,ts,jsx,tsx}",
        "./components/**/*.{js,ts,jsx,tsx}",
    ],
    theme: {
        extend: {}
    },
    plugins: [
        require('@tailwindcss/forms')
    ],
}
Enter fullscreen mode Exit fullscreen mode

Lastly update you styles/global.css file with the following:

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Update .env

Next, let’s setup our .env.local file then add the following values.

GOOGLE_CLIENT_EMAIL=
GOOGLE_PRIVATE_KEY=
GOOGLE_SHEET_ID=
Enter fullscreen mode Exit fullscreen mode

The GOOGLE_PRIVATE_KEY is the API key you created earlier while the GOOGLE_CLIENT_EMAIL is the primary email for your Google Cloud account and the GOOGLE_SHEET_ID is the Spreadsheet ID available in the URL.

Add Google API

Next we want to install the Google API package by using the following command:

$ npm i googleapis
Enter fullscreen mode Exit fullscreen mode

Code our project

Now that our project is setup with the necessary packages, we want to design our form using TailwindCSS

Update Index File

import type { NextPage } from 'next'
import {FormEvent, useState} from "react";

const Home: NextPage = () => {
    const [name, setName] = useState('');
    const [email, setEmail] = useState('');
    const [phone, setPhone] = useState('');
    const [message, setMessage] = useState('');

    const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
        e.preventDefault();

        let form = {
            name,
            email,
            phone,
            message
        }

        const rawResponse = await fetch('/api/submit', {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(form)
        });
        const content = await rawResponse.json();

        // print to screen
        alert(content.data.tableRange)

        // Reset the form fields
        setMessage('')
        setPhone('')
        setName('')
        setEmail('')
    }

    return (
        <main className="bg-gray-100 min-h-screen">
            <div className="max-w-5xl mx-auto py-16">
                <form className="py-4 space-y-4" onSubmit={handleSubmit}>
                    <div className="flex items-center justify-center">
                        <label htmlFor="name" className="sr-only">Name</label>
                        <input value={name} onChange={e => setName(e.target.value)} type="text" name="name" id="name" className="shadow-md focus:ring-indigo-500 focus:border-indigo-500 block w-64 sm:text-md border-gray-300 rounded-md" placeholder="Your Name" />
                    </div>
                    <div className="flex items-center justify-center">
                        <label htmlFor="email" className="sr-only">Email</label>
                        <input value={email} onChange={e => setEmail(e.target.value)} type="email" name="email" id="email" className="shadow-md focus:ring-indigo-500 focus:border-indigo-500 block w-64 sm:text-md border-gray-300 rounded-md" placeholder="Your Email" />
                    </div>
                    <div className="flex items-center justify-center">
                        <label htmlFor="phone" className="sr-only">Phone</label>
                        <input value={phone} onChange={e => setPhone(e.target.value)} type="tel" name="phone" id="phone" className="shadow-md focus:ring-indigo-500 focus:border-indigo-500 block w-64 sm:text-md border-gray-300 rounded-md" placeholder="Your Phone" />
                    </div>
                    <div className="flex items-center justify-center">
                        <label htmlFor="message" className="sr-only">Message</label>
                        <textarea value={message} onChange={e => setMessage(e.target.value)} id="message" className="shadow-md focus:ring-indigo-500 focus:border-indigo-500 block w-64 sm:text-md border-gray-300 rounded-md" placeholder="Your Message" />
                    </div>
                    <div className="flex items-center justify-center">
                        <button type="submit" className="flex items-center justify-center text-sm w-64 rounded-md shadow py-3 px-2 text-white bg-indigo-600">Save</button>
                    </div>
                </form>
            </div>
        </main>
    )
}

export default Home
Enter fullscreen mode Exit fullscreen mode

Setup API Endpoint

Next, we want to setup our API endpoint to update our Google Sheet. Create a file called api/submit.ts and add the following code:

import type { NextApiRequest, NextApiResponse } from 'next'
import {google} from "googleapis";

type SheetForm = {
    name: string
    email: string
    phone: string
    message: string
}

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
) {
    if (req.method !== 'POST') {
        return res.status(405).send({ message: 'Only POST requests allowed' })
    }

    const body = req.body as SheetForm

    try {
        const auth = new google.auth.GoogleAuth({
            credentials: {
                client_email: process.env.GOOGLE_CLIENT_EMAIL,
                private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n')
            },
            scopes: [
                'https://www.googleapis.com/auth/drive',
                'https://www.googleapis.com/auth/drive.file',
                'https://www.googleapis.com/auth/spreadsheets'
            ]
        })

        const sheets = google.sheets({
            auth,
            version: 'v4'
        });

        const response = await sheets.spreadsheets.values.append({
            spreadsheetId: process.env.GOOGLE_SHEET_ID,
            range: 'A1:D1',
            valueInputOption: 'USER_ENTERED',
            requestBody: {
                values: [
                    [body.name, body.email, body.phone, body.message]
                ]
            }
        });

        return res.status(201).json({
            data: response.data
        })
    }catch (e) {
        return res.status(e.code).send({message: e.message})
    }

}
Enter fullscreen mode Exit fullscreen mode

Recap

In conclusion, configuring NextJS + Google Sheets is a pretty simple task and is a quick way to collect information without having to manage a database. If you found this tutorial useful, considering subscribing to my YouTube channel where I record
programming content on the regular or follow me on Twitter.

Social

Twitter

Github

YouTube

Patreon

Top comments (1)

Collapse
 
chamalsena profile image
Chamal Senarathna

Thank you bro <3