Deploy a SendGrid-Powered Contact Form with Vercel

Create a SendGrid-Powered Next.js and Node.js Contact Form and deploy it with Vercel.

SendGrid is a cloud-based SMTP provider that allows you to send email without having to maintain email servers.

This guide walks you through creating a Next.js app with a contact form powered by SendGrid, and how to deploy it with Vercel.

Step 1: Creating a SendGrid API Key

To start, you need to have created a SendGrid account. Once logged in to SendGrid, follow the setup guide steps for integrating using the Web API, choosing the Node.js option.

You will be asked to supply a name for your API key and given the option to create it.

Once created, make a note of this API key as it will not be shown again, this will be used in the app you create.

Note: On occasion, it can take a few hours for the API key to become active.

Step 2: Creating Your Next.js App

Get started creating your Next.js app by making a project directory with the required structure and moving into it:

mkdir -p nextjs-sendgrid/pages/api && cd nextjs-sendgrid

Creating and entering into the /nextjs-sendgrid directory.

Next, initialize the project:

npm init -y

Initializing the project, this creates a package.json file.

Continue to install the SendGrid Node.js helper which allows you to send email using your API key, along with the required dependencies for Next.js including dotenv for using your API key during local development:

npm i @sendgrid/mail dotenv next react react-dom

Adding the @sendgrid/mail, dotenv, next, react and react-dom dependencies to the project.

Inside of the /pages directory, create an index.js file with the code below:

import React, { useState } from 'react'

export default () => {
  const [status, setStatus] = useState({
    submitted: false,
    submitting: false,
    info: { error: false, msg: null }
  })

  const [inputs, setInputs] = useState({
    email: '',
    message: ''
  })

  const handleResponse = (status, msg) => {
    if (status === 200) {
      setStatus({
        submitted: true,
        submitting: false,
        info: { error: false, msg: msg }
      })
      setInputs({
        email: '',
        message: ''
      })
    } else {
      setStatus({
        info: { error: true, msg: msg }
      })
    }
  }

  const handleOnChange = e => {
    e.persist()
    setInputs(prev => ({
      ...prev,
      [e.target.id]: e.target.value
    }))
    setStatus({
      submitted: false,
      submitting: false,
      info: { error: false, msg: null }
    })
  }

  const handleOnSubmit = async e => {
    e.preventDefault()
    setStatus(prevStatus => ({ ...prevStatus, submitting: true }))
    const res = await fetch('/api/send', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(inputs)
    })
    const text = await res.text()
    handleResponse(res.status, text)
  }

  return (
    <main>
      <form onSubmit={handleOnSubmit}>
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="email"
          onChange={handleOnChange}
          required
          value={inputs.email}
        />
        <label htmlFor="message">Message</label>
        <textarea
          id="message"
          onChange={handleOnChange}
          required
          value={inputs.message}
        />
        <button type="submit" disabled={status.submitting}>
          {!status.submitting
            ? !status.submitted
              ? 'Submit'
              : 'Submitted'
            : 'Submitting...'}
        </button>
      </form>
      {status.info.error && (
        <div className="error">Error: {status.info.msg}</div>
      )}
      {!status.info.error && status.info.msg && (
        <div className="success">{status.info.msg}</div>
      )}
    </main>
  )
}

Adding a /pages/index.js file to the project.

Add development and build scripts to the package.json file. These allow you to run your app with a development server and tell Vercel how to build your project for deployment:

{
  ...
  "scripts": {
    "dev": "next dev",
    "build": "next build"
  }
}

Adding dev and build scripts to the package.json file.

Note: If you wish to use the same styles as the example app, you can find them here.

Step 3: Writing the Serverless Function

Create the Node.js API endpoint that will be used to send the form data to the SendGrid API by adding a send.js file to the /pages/api directory with the following code:

const sgMail = require('@sendgrid/mail')

export default async function(req, res) {
  sgMail.setApiKey(process.env.SENDGRID_API_KEY)

  const { email, message } = req.body

  const content = {
    to: '[your-email-address]',
    from: email,
    subject: `New Message From - ${email}`,
    text: message,
    html: `<p>${message}</p>`
  }

  try {
    await sgMail.send(content)
    res.status(200).send('Message sent successfully.')
  } catch (error) {
    console.log('ERROR', error)
    res.status(400).send('Message not sent.')
  }
}

An example /pages/api/send.js file that sends form data to the SendGrid API.

Note: Make sure you change the to value to the required destination for emails sent.

Create a .env file, adding your SendGrid API key where prompted:

SENDGRID_API_KEY=[your-sendgrid-api-key]

An example .env file that stores environment variables for use during local development.

Add a next.config.js file that will provide your app with the defined environment variable:

require('dotenv').config()

module.exports = {
  env: {
    SENDGRID_API_KEY: process.env.SENDGRID_API_KEY
  }
}

An example next.config.js file that provides environment variables to the app.

Note: The app can now be run in development by using npm run dev.

Create a Secret to securely store the SendGrid API key, this will be used when deploying the app with Vercel.

vercel secrets add sendgrid_api_key [your-sendgrid-api-key]

Adding the access key as a Secret.

Lastly, to make the Secret available to the Serverless Function when deployed, create a vercel.json file:

{
  "build": {
    "env": {
      "SENDGRID_API_KEY": "@sendgrid_api_key"
    }
  }
}

An example vercel.json file that makes a Secret available to the app.

Step 4: Deploying the App with Vercel

There are two ways to deploy with Vercel. We recommend using a Vercel for Git Integration for ease-of-use. Alternatively, Vercel CLI can be used to generate a manual Preview Deployment.


To deploy your Next.js + Sendgrid app with a Vercel for Git Integration, make sure it has been pushed to a Git repository.

Import the project into Vercel using your Git Integration of choice:

After your project has been imported, all subsequent pushes to branches will generate Preview Deployments, and all changes made to the default branch (commonly "master") will result in a Production Deployment.



Written By
Written by mcsdevmcsdev