How to create a contact form with Supabase & Next JS
An introduction to Supabase — Part 1
Originally posted in my portfolio notes in April, 2023.
Supabase is an open source Firebase alternative and helps you to build faster and focus on your products. As someone that likes to test out ideas once in a while, Firebase and Supabase have been interesting to work with.
In this tutorial, I will walk you through an introduction to Supabase, setting up a Database table, creating a simple contact form in Next JS, and putting it all together.
I assume you have basic knowledge of Next JS to follow along.
To follow along with the Next JS project, check it out on Github. The contact form demo is also live.
Supabase
Creating a Supabase account is pretty simple. Visit Supabase and click on the “Start your project” button.
First, create an organization
Next, create a project
On the next screen, your project API keys will be displayed. Copy the public key and keep it aside. We will use it later in this tutorial.
Copy the URL under the Project Configuration section too. This will be your base URL for the project.
Next, we will create a table for the contact form.
Create a Database table
In the section below, click on Database
A blank page will be displayed. Click on the button to create a new table and follow the steps.
Add as many columns as you’d like. For the sake of this tutorial, we will create a table named “contacts” with the following columns.
Note that the column types are important and the RLS policies section is checked. This controls access to our table.
Next, we write a simple policy for our table. Without a row level policy, we will not be able to read, update or delete rows on our table.
On our blank table, click on “No active RLS policies” on the extreme right of the table.
Create a new policy and select “For full customization”.
For the sake of this tutorial, we will enable access to all operations on our table. To read more about Row level security, visit this page in the documentation.
Follow the steps below.
We are all set to create our contact form now.
Contact form
To follow along with the Next JS project, check it out on [Github](https://github.com/DeolaJ/supabase-tutorials).
Create a simple Next JS project:
npx create-next-app contact-form
npm install formik yup @supabase/supabase-js
We install a few dependencies to help create the contact form and to interact with our supabase table.
To start, we create the supabase client in our next JS application using the supabase-js library. I added the Supabase base URL and our project’s public key to the .env
file to be used here. There is an example .env
file in the github project.
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
export default supabase;
Next, we create a simple contact form with the following schema
...
const ContactSchema = Yup.object().shape({
name: Yup.string().max(140, "Too long!").required("Required."),
email: Yup.string().email("Invalid email.").max(140, "Too long!").required("Required."),
body: Yup.string().max(280, "Too long!").required("Required."),
});
...
Any kind of form works here. I chose formik
for convenience. Check the github project for a complete picture.
...
<Formik
enableReinitialize
initialValues={defaultContact}
validationSchema={ContactSchema}
onSubmit={handleSubmit}
>
<Form>
<Field name="name">
{({ field, meta }) => (
<div className="form-control">
<label mb="1.5" fontSize="sm" htmlFor="contact-name">
Name
</label>
<input {...field} placeholder="Enter name" id="contact-name" />
{meta.error && meta.touched && <p>{meta.error}</p>}
</div>
)}
</Field>
<Field name="email">
{({ field, meta }) => (
<div className="form-control">
<label mb="1.5" fontSize="sm" htmlFor="contact-email">
Email
</label>
<input {...field} placeholder="Enter email" id="contact-email" />
{meta.error && meta.touched && <p>{meta.error}</p>}
</div>
)}
</Field>
<Field name="body">
{({ field, meta }) => (
<div className="form-control">
<label mb="1.5" fontSize="sm" htmlFor="contact-body">
Body
</label>
<textarea
{...field}
id="contact-body"
placeholder="Describe what you are reaching out for."
/>
{meta.error && meta.touched && <p>{meta.error}</p>}
</div>
)}
</Field>
<div className="button-container">
<button type="submit" className="submit-button">
{isSubmittingForm ? "Submitting..." : "Submit"}
</button>
</div>
</Form>
</Formik>
...
Next, we create the handleSubmit
function to interact with our supabase table.
async function handleSubmit(values, { resetForm }) {
const { name, email, body } = values;
try {
const { error } = await supabase.from("contacts").insert({ name, email, body });
if (error) {
throw error;
}
setFeedback("Form submitted successfully");
resetForm();
} catch (error) {
console.log("Error occurred", { error });
setFeedback("An error occurred");
}
}
The supabase client is used to insert a new row into the exact table we created (contacts). If this works well, you should see the new row on your table.
Using React Query
Alternatively, you can use this with React Query.
First, create a hook
import { useMutation } from "@tanstack/react-query";
import supabase from "../lib/supabase";
const updateContacts = async (contact) => {
const { data, error } = await supabase.from("contacts").insert(contact);
if (error) {
throw error;
}
return data;
};
export default function useUpdateContacts() {
return useMutation((contact) => updateContacts(contact));
}
Then call the hook inside your contact form,
...
const { mutateAsync: submitFormAsync, isLoading: isSubmittingForm } = useUpdateContacts();
...
Then use this in your handleSubmit
function instead
async function handleSubmit(values, { resetForm }) {
const { name, email, body } = values;
try {
await submitFormAsync({ name, email, body });
setFeedback("Form submitted successfully");
resetForm();
} catch (error) {
console.log("Error occurred", { error });
setFeedback("An error occurred");
}
}
Conclusion
Note that for free accounts on Supabase, the project is paused after 7 days of inactivity.
I hope this tutorial helps to get started with supabase. This is the first article of my Supabase and Next JS series.
Follow along to Part 2, How to send emails using Edge functions on Supabase.