Next.js Quick Start

Integrate RizzForms with a Next.js (App Router) project. Choose between a simple HTML form or a server-side API route depending on your needs.

Overview

RizzForms works with any Next.js deployment: Vercel, self-hosted, or static export. The HTML form approach requires zero server-side code. The API route approach gives you more control over validation and error handling.

A: HTML Form (Simplest)

Drop a standard HTML form into any React component. This works with static export and requires no server-side code. HTML form POST is cross-origin safe by default.

// app/contact/page.tsx
export default function ContactPage() {
  return (
    <form
      action={`https://forms.rizzness.com/f/${process.env.NEXT_PUBLIC_RIZZFORMS_TOKEN}`}
      method="POST"
    >
      {/* Honeypot: hidden from users, catches bots */}
      <input
        type="text"
        name="_hp"
        style={{ display: "none" }}
        tabIndex={-1}
        autoComplete="off"
      />

      <label htmlFor="name">Name</label>
      <input type="text" id="name" name="name" required />

      <label htmlFor="email">Email</label>
      <input type="email" id="email" name="email" required />

      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" />

      <button type="submit">Send</button>
    </form>
  );
}

B: Server Action / API Route

For more control, create an API route that submits to RizzForms server-side. This keeps your endpoint token out of client-side code and lets you add custom validation before submission.

// app/api/contact/route.ts
import { NextResponse } from "next/server";

export async function POST(request: Request) {
  const body = await request.json();

  const response = await fetch(
    `https://forms.rizzness.com/json/${process.env.RIZZFORMS_TOKEN}`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        name: body.name,
        email: body.email,
        message: body.message,
        _hp: "", // honeypot must be empty
      }),
    }
  );

  const data = await response.json();

  if (!response.ok) {
    return NextResponse.json(
      { error: "Submission failed" },
      { status: response.status }
    );
  }

  return NextResponse.json({ ok: true, data });
}

Then call this route from your client component:

// app/contact/ContactForm.tsx
"use client";

import { useState } from "react";

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

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

    const form = new FormData(e.currentTarget);
    const res = await fetch("/api/contact", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        name: form.get("name"),
        email: form.get("email"),
        message: form.get("message"),
      }),
    });

    setStatus(res.ok ? "sent" : "error");
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" name="name" required />

      <label htmlFor="email">Email</label>
      <input type="email" id="email" name="email" required />

      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" />

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

      {status === "sent" && <p>Thank you! We'll be in touch.</p>}
      {status === "error" && <p>Something went wrong. Please try again.</p>}
    </form>
  );
}

Environment Variables

Add your endpoint token to .env.local:

# For HTML form approach (exposed to browser)
NEXT_PUBLIC_RIZZFORMS_TOKEN=your_endpoint_token

# For API route approach (server-side only)
RIZZFORMS_TOKEN=your_endpoint_token

Use NEXT_PUBLIC_ prefix only if you need the token in client-side code. The API route approach keeps the token server-side.

CORS Notes

HTML form POST (method="POST" with default encoding) works cross-origin without any CORS configuration. This is a browser standard, not a RizzForms feature.

JSON fetch from the browser to /json/:token may require CORS headers. To avoid CORS issues, use the server-side API route approach (option B above) so the fetch happens on your server, not in the browser.