Back to Blog
EnglishApril 2, 2026by Nikhil Nagar

How to Add a Contact Form to React or Next.js (No Backend Needed)

reactnextjstutorialcontact form

You have built a React or Next.js app and now you need a contact form. The traditional approach involves creating an API route, configuring an email service like SendGrid or Nodemailer, handling validation on the server, and managing error states. That is a lot of work for a simple "send me a message" form.

With Rowen, you skip all of that. You point your form at a Rowen endpoint, and submissions are stored in your dashboard, emailed to you, and optionally synced to Google Sheets. This tutorial walks you through the complete setup.

Prerequisites

  • A React or Next.js project (works with both Pages Router and App Router)
  • A free Rowen account — sign up at rowen.in/signup

Step 1 — Get Your Rowen Endpoint

Log into your Rowen dashboard and click "+ New Form". Copy the endpoint URL:

https://rowen.in/api/f/YOUR_FORM_ID

Step 2 — Build the Form Component

Here is a complete, production-ready contact form component with loading state, error handling, and success feedback:

import { useState } from "react";

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

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus("loading");

    const formData = new FormData(e.target);

    try {
      const res = await fetch("https://rowen.in/api/f/YOUR_FORM_ID", {
        method: "POST",
        body: formData,
      });

      if (res.ok) {
        setStatus("success");
        e.target.reset();
      } else {
        setStatus("error");
      }
    } catch (err) {
      setStatus("error");
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="name" placeholder="Your name" required />
      <input type="email" name="email" placeholder="Your email" required />
      <textarea name="message" placeholder="Your message" rows={5} required />

      {/* Honeypot spam protection */}
      <input type="text" name="_gotcha" style={{ display: "none" }} />

      <button type="submit" disabled={status === "loading"}>
        {status === "loading" ? "Sending..." : "Send Message"}
      </button>

      {status === "success" && <p>Message sent successfully!</p>}
      {status === "error" && <p>Something went wrong. Please try again.</p>}
    </form>
  );
}

Step 3 — Use It in Your Page

Import the component wherever you need it:

import ContactForm from "@/components/ContactForm";

export default function ContactPage() {
  return (
    <main>
      <h1>Get in Touch</h1>
      <ContactForm />
    </main>
  );
}

Next.js App Router: Using Server Actions?

If you are using Next.js App Router and prefer server actions, you can still use Rowen. However, the simplest approach is to keep the form as a client component (add "use client" at the top) and use the fetch pattern shown above. There is no advantage to routing through a server action when Rowen's endpoint already handles everything server-side.

TypeScript Version

If your project uses TypeScript, here is the typed version of the submit handler:

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

  const formData = new FormData(e.currentTarget);

  try {
    const res = await fetch("https://rowen.in/api/f/YOUR_FORM_ID", {
      method: "POST",
      body: formData,
    });
    setStatus(res.ok ? "success" : "error");
    if (res.ok) e.currentTarget.reset();
  } catch {
    setStatus("error");
  }
}

Styling the Form

Rowen does not impose any styling. The form is just a standard HTML form, so you can style it with Tailwind, CSS Modules, styled-components, or plain CSS. Here is a quick Tailwind example:

<input
  type="text"
  name="name"
  className="w-full rounded border border-gray-300 px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
  placeholder="Your name"
  required
/>

What You Get Without Writing Backend Code

  • All submissions stored in your Rowen dashboard
  • Email notifications on every submission
  • Spam filtering via honeypot (no CAPTCHA needed)
  • Google Sheets sync (free)
  • Webhook forwarding to Slack, Discord, or any URL

Wrap Up

A contact form in React or Next.js does not need an API route, a database, or an email service. With Rowen, you write a single client component, point it at your endpoint, and you are done. Create your free account at rowen.in/signup and have your form running in under two minutes.

Ready to add forms to your website?

Get started with Rowen for free. No credit card required.

Create your free account