FormLoom

Next.js survey form

A multi-question survey with mixed input types.

Recommended fields

FieldTypeRequired
emailemailNo
roleselectNo
satisfactionselectNo
commentstextareaNo

The code

Define an inline `"use server"` action that forwards the FormData to your FormLoom endpoint. No client JS required — it works with JavaScript disabled and progressively enhances. For richer UIs, pair it with `useActionState` for pending/error states.

Server Action (recommended)
app/contact/page.tsx
// app/contact/page.tsx — Next.js 15 App Router, Server Action
export default function ContactPage() {
  async function submit(formData: FormData) {
    "use server";
    const res = await fetch("https://formloom.vercel.app/api/submit/YOUR_ACCESS_KEY", {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify(Object.fromEntries(formData)),
    });
    // FormLoom scores spam, stores the row, and emails you — no backend to wire.
    return res.json();
  }

  return (
    <form action={submit}>
      <label>
        Email (optional)
        <input type="email" name="email" />
      </label>
      <label>
        Your role
        <select name="role">
          <option value="Developer">Developer</option>
          <option value="Designer">Designer</option>
          <option value="Founder">Founder</option>
          <option value="Manager">Manager</option>
          <option value="Other">Other</option>
        </select>
      </label>
      <label>
        How satisfied are you?
        <select name="satisfaction">
          <option value="Very">Very</option>
          <option value="Somewhat">Somewhat</option>
          <option value="Neutral">Neutral</option>
          <option value="Not really">Not really</option>
          <option value="Not at all">Not at all</option>
        </select>
      </label>
      <label>
        Anything else?
        <textarea name="comments"></textarea>
      </label>
      <!-- honeypot: bots fill this, humans don't see it -->
      <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off" />
      <button type="submit">Send</button>
    </form>
  );
}
React component (client)
ContactForm.tsx
// ContactForm.tsx — React (works in Vite, CRA, Next 'use client')
"use client";
import { useState } from "react";

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

  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const form = e.currentTarget;
    const res = await fetch("https://formloom.vercel.app/api/submit/YOUR_ACCESS_KEY", {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify(Object.fromEntries(new FormData(form))),
    });
    setStatus((await res.json()).success ? "ok" : "error");
    if (status !== "error") form.reset();
  }

  if (status === "ok") return <p>Thanks — we got your message.</p>;
  return (
    <form onSubmit={onSubmit}>
      <label>
        Email (optional)
        <input type="email" name="email" />
      </label>
      <label>
        Your role
        <select name="role">
          <option value="Developer">Developer</option>
          <option value="Designer">Designer</option>
          <option value="Founder">Founder</option>
          <option value="Manager">Manager</option>
          <option value="Other">Other</option>
        </select>
      </label>
      <label>
        How satisfied are you?
        <select name="satisfaction">
          <option value="Very">Very</option>
          <option value="Somewhat">Somewhat</option>
          <option value="Neutral">Neutral</option>
          <option value="Not really">Not really</option>
          <option value="Not at all">Not at all</option>
        </select>
      </label>
      <label>
        Anything else?
        <textarea name="comments"></textarea>
      </label>
      <!-- honeypot: bots fill this, humans don't see it -->
      <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off" />
      <button type="submit">Send</button>
    </form>
  );
}
@formloom/client (typed SDK)
submit.ts
// npm i @formloom/client
import { createForm } from "@formloom/client";

type Contact = {
  email?: string;
  role?: string;
  satisfaction?: string;
  comments?: string;
};

const form = createForm<Contact>("YOUR_ACCESS_KEY");

// End-to-end typed: TS errors if you send the wrong shape.
const { success, message } = await form.submit({
  email: form.email.value,
  role: form.role.value,
  satisfaction: form.satisfaction.value,
  comments: form.comments.value,
});

Tips

  • Keep surveys short — completion drops sharply past 5 questions.
  • Export to CSV/JSON for analysis in your tool of choice.

FAQ

Export all submissions to CSV or JSON from the dashboard and load them into your spreadsheet or analytics tool.
Next.js survey form — copy-paste code + live demo · FormLoom