Add form notifications to a Next.js site
This guide shows you how to add a contact form to a Next.js App Router project that sends submissions to your email via Form Alert.
Prerequisites
- A Form Alert access key — get one free
- A Next.js project using the App Router (Next.js 13+)
Create the form component
Since the form uses browser APIs, it needs to be a Client Component. Create components/contact-form.tsx.
components/contact-form.tsx
"use client";
import { useState } from "react";
export default function ContactForm() {
const [status, setStatus] = useState("idle");
async function handleSubmit(
e: React.FormEvent<HTMLFormElement>
) {
e.preventDefault();
setStatus("sending");
const formData = new FormData(e.currentTarget);
const data = Object.fromEntries(formData);
try {
const res = await fetch(
"https://api.form-alert.com/v1/submit",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
);
if (res.ok) {
setStatus("sent");
e.currentTarget.reset();
} else {
setStatus("error");
}
} catch {
setStatus("error");
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="hidden"
name="access_key"
value="YOUR_ACCESS_KEY"
/>
<label htmlFor="name">Name</label>
<input type="text" name="name" id="name" required />
<label htmlFor="email">Email</label>
<input type="email" name="email" id="email" required />
<label htmlFor="message">Message</label>
<textarea name="message" id="message" required />
<button
type="submit"
disabled={status === "sending"}
>
{status === "sending"
? "Sending..."
: "Send message"}
</button>
{status === "sent" && <p>Message sent!</p>}
{status === "error" && (
<p>Something went wrong. Try again.</p>
)}
</form>
);
}Add to a page
Import the component in any page. The page itself stays a Server Component — only the form is client-side.
app/contact/page.tsx
import ContactForm from "@/components/contact-form";
export default function ContactPage() {
return (
<main>
<h1>Contact us</h1>
<ContactForm />
</main>
);
}Keep your access key server-side (optional)
If you don't want to expose your access key in client-side code, you can proxy the request through a Next.js Route Handler.
app/api/contact/route.ts
// app/api/contact/route.ts
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const body = await request.json();
const res = await fetch(
"https://api.form-alert.com/v1/submit",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
...body,
access_key: process.env.FORM_ALERT_KEY,
}),
}
);
if (!res.ok) {
return NextResponse.json(
{ error: "Failed to send" },
{ status: 500 }
);
}
return NextResponse.json({ ok: true });
}Then update the form component to POST to /api/contact instead of the Form Alert API
directly.
Next steps
- API documentation for advanced options
- How Form Alert works
- Browse all integration guides
