All articles
Web DevelopmentEdgeSubmit

How to Handle Form Submissions in Next.js Without Writing Backend Code

January 28, 2026 7 min read

You've got a Next.js site with a contact form, a feedback form, or a lead capture. The conventional approach is to write an API route in app/api/submit/route.ts, set up a database, configure email delivery, and handle spam. For one form, that's a lot of plumbing.

Instead, you can skip all of that and point your form at a managed form submission API. The API handles storage, spam filtering, and email notifications — you just build the frontend.

The Traditional Next.js Form Approach

Here's what you'd typically build for a simple contact form in Next.js:

  • A React form component with state management
  • An API route (/api/submit) to receive the data
  • Validation logic (server-side)
  • A database or storage solution for submissions
  • Email sending via Resend, SendGrid, or Nodemailer
  • Spam protection (reCAPTCHA, honeypot fields)
  • Rate limiting to prevent abuse

That's 5–7 different concerns for a single form. For a small business site or portfolio, this is massive overkill. Even for larger projects, it's often better to delegate this to a service.

The Simpler Approach: Form Submission API

With a form submission API, your architecture looks like this:

React Form
Form API
Email + Dashboard

No API routes. No database. No email configuration. You build the form, the service handles everything else.

Implementation

The Form Component

Here's a complete contact form component for Next.js using a form submission API:

"use client";

import { useState, FormEvent } from "react";

export default function ContactForm() {
  const [status, setStatus] = useState<
    "idle" | "submitting" | "success" | "error"
  >("idle");

  async function handleSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus("submitting");

    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData);

    try {
      const res = await fetch(
        "https://edgesubmit.com/api/submit/YOUR_FORM_ID",
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(data),
        }
      );

      if (!res.ok) throw new Error("Failed");
      setStatus("success");
      e.currentTarget.reset();
    } catch {
      setStatus("error");
    }
  }

  return (
    <form onSubmit={handleSubmit} className="space-y-4 max-w-lg">
      <div>
        <label htmlFor="name" className="block text-sm font-medium">
          Name
        </label>
        <input
          type="text"
          id="name"
          name="name"
          required
          className="mt-1 w-full rounded-lg border px-3 py-2"
        />
      </div>
      <div>
        <label htmlFor="email" className="block text-sm font-medium">
          Email
        </label>
        <input
          type="email"
          id="email"
          name="email"
          required
          className="mt-1 w-full rounded-lg border px-3 py-2"
        />
      </div>
      <div>
        <label htmlFor="message" className="block text-sm font-medium">
          Message
        </label>
        <textarea
          id="message"
          name="message"
          rows={5}
          required
          className="mt-1 w-full rounded-lg border px-3 py-2"
        />
      </div>
      <button
        type="submit"
        disabled={status === "submitting"}
        className="px-6 py-2.5 bg-blue-600 text-white rounded-lg 
                   hover:bg-blue-700 disabled:opacity-50"
      >
        {status === "submitting" ? "Sending..." : "Send Message"}
      </button>

      {status === "success" && (
        <p className="text-green-600 text-sm">
          Message sent! We'll get back to you soon.
        </p>
      )}
      {status === "error" && (
        <p className="text-red-600 text-sm">
          Something went wrong. Please try again.
        </p>
      )}
    </form>
  );
}

Adding Client-Side Validation

The form API handles server-side validation, but you should still validate on the client for a better UX:

// Simple validation before submission
function validateForm(data: Record<string, FormDataEntryValue>) {
  const errors: string[] = [];

  if (!data.name || String(data.name).trim().length < 2) {
    errors.push("Name must be at least 2 characters");
  }
  if (!data.email || !String(data.email).includes("@")) {
    errors.push("Please enter a valid email address");
  }
  if (!data.message || String(data.message).trim().length < 10) {
    errors.push("Message must be at least 10 characters");
  }

  return errors;
}

What About Server Actions?

Next.js 14+ introduced Server Actions, which let you run server-side code from form submissions. You could use a Server Action to call the form API instead of doing it client-side. This has a security advantage — your form endpoint ID isn't exposed in the client bundle:

// app/actions.ts
"use server";

export async function submitContactForm(formData: FormData) {
  const data = {
    name: formData.get("name"),
    email: formData.get("email"),
    message: formData.get("message"),
  };

  const res = await fetch(
    "https://edgesubmit.com/api/submit/YOUR_FORM_ID",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    }
  );

  if (!res.ok) throw new Error("Submission failed");
  return { success: true };
}

When to Use an API vs. Build Your Own

ScenarioForm APICustom Backend
Simple contact formBest choiceOverkill
Lead capture + CRM integrationGood (with webhooks)Also good
Multi-step form with complex logicPossibleBetter fit
Forms with file uploadsCheck supportFull control
High-volume transactional formsCost concernBuild your own

EdgeSubmit — Forms Without the Backend

EdgeSubmit handles form submissions at the edge. Built on Cloudflare Workers for speed, it includes spam filtering, email notifications, and a dashboard to manage submissions. Works with any frontend framework — React, Next.js, Vue, Svelte, or plain HTML.

Try EdgeSubmit Free →

Need a custom form solution or full-stack Next.js application? Talk to us — we specialize in building custom web applications.

nextjsformsform apireactserverless