ID du formulaire

Next.js

Envoyez depuis un composant client de l'App Router Next.js, ou via une server action avec une clé secrète.

Il y a deux bonnes façons d'envoyer depuis Next.js. Envoyez depuis le navigateur dans un composant client : chaque visiteur garde sa propre IP pour les limites de débit, et cela fonctionne avec tous les contrôles anti-spam. Ou envoyez depuis une server action : le formulaire continue de fonctionner sans JavaScript.

ItemComposant clientServer action
Fonctionne sans JavaScriptNonOui
Limites de débit comptées parChaque visiteurL'IP de votre serveur
Vérification humaine IkiformUtilisez challenge du SDKIgnorée avec une clé secrète
Domaines autorisésComparés à l'origine du navigateurIgnorée avec une clé secrète
Nécessite une clé secrèteNonOui

Installez le SDK

Terminal
npm install @ikiform/sdk

Le SDK est facultatif. Tout ce qui suit fonctionne aussi avec fetch ; voir JavaScript et fetch.

Depuis un composant client

app/contact/contact-form.tsx
"use client";

import { submit } from "@ikiform/sdk";
import { useState } from "react";

type Status = "idle" | "sending" | "sent" | "error";

export function ContactForm() {
  const [status, setStatus] = useState<Status>("idle");
  const [errors, setErrors] = useState<Record<string, string[]>>({});
  const [message, setMessage] = useState("");

  async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setStatus("sending");
    const result = await submit("frm_xxxxxxxxxxxxxxxx", new FormData(event.currentTarget));
    if (result.ok) return setStatus("sent");
    setErrors(result.error.fields ?? {});
    setMessage(result.error.message);
    setStatus("error");
  }

  if (status === "sent") return <p>Thanks, we got it.</p>;

  return (
    <form onSubmit={onSubmit} noValidate>
      <label htmlFor="email">Email</label>
      <input id="email" name="email" type="email" required aria-invalid={Boolean(errors.email)} />
      {errors.email ? <p>{errors.email[0]}</p> : null}
      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" />
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      {status === "error" && !Object.keys(errors).length ? <p role="alert">{message}</p> : null}
      <button disabled={status === "sending"}>{status === "sending" ? "Sending" : "Send"}</button>
    </form>
  );
}
  • submit ne lève jamais d'exception. Il se résout en { ok: true, id } ou { ok: false, error, status, requestId }.
  • error.fields associe les ids de champs à des messages quand le formulaire a des champs définis.
  • Affichez le composant depuis n'importe quelle page en server component, par exemple app/contact/page.tsx.

Avec la vérification humaine Ikiform

Si le contrôle anti-spam du formulaire est Ikiform, obtenez un token juste avant chaque envoi. challenge monte une petite frame ikiform.com qui reste invisible sauf si Turnstile a besoin d'un clic.

contact-form.tsx
const data = new FormData(event.currentTarget);
const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx");
const result = await submit("frm_xxxxxxxxxxxxxxxx", data, { turnstileToken });

Importez-le avec import { challenge, submit } from "@ikiform/sdk";. Les tokens sont à usage unique : appelez-le une fois par envoi.

Depuis une server action

Créez une clé secrète dans le tableau de bord sous API keys avec la portée submissions:write, et stockez-la dans une variable d'environnement. Ne l'exposez jamais au navigateur : ne la préfixez pas par NEXT_PUBLIC_.

.env.local
IKIFORM_SECRET_KEY=ik_sk_your_secret_key
app/contact/actions.ts
"use server";

export interface ContactState {
  ok: boolean;
  message: string;
  fields?: Record<string, string[]>;
}

export async function sendContact(_previous: ContactState, formData: FormData): Promise<ContactState> {
  const body = new URLSearchParams();
  for (const [name, value] of formData) {
    if (typeof value === "string" && !name.startsWith("$ACTION")) body.append(name, value);
  }
  const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
    method: "POST",
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${process.env.IKIFORM_SECRET_KEY}`,
    },
    body,
  });
  const result = await response.json();
  if (result.ok) return { ok: true, message: "Thanks, we got it." };
  return { ok: false, message: result.error.message, fields: result.error.fields };
}
app/contact/contact-form.tsx
"use client";

import { useActionState } from "react";
import { type ContactState, sendContact } from "./actions";

const initial: ContactState = { ok: false, message: "" };

export function ContactForm() {
  const [state, action, pending] = useActionState(sendContact, initial);

  if (state.ok) return <p>{state.message}</p>;

  return (
    <form action={action}>
      <label htmlFor="email">Email</label>
      <input id="email" name="email" type="email" required />
      {state.fields?.email ? <p>{state.fields.email[0]}</p> : null}
      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" />
      {state.message && !state.fields ? <p role="alert">{state.message}</p> : null}
      <button disabled={pending}>{pending ? "Sending" : "Send"}</button>
    </form>
  );
}
Les envois depuis votre serveur viennent tous de l'adresse IP de votre serveur, donc les limites de débit par visiteur d'Ikiform comptent tous les visiteurs ensemble. Le préréglage Standard autorise 5 envois par minute par IP et par formulaire, ce qu'un site fréquenté peut atteindre. Envoyez depuis le navigateur quand c'est possible, ou choisissez la limite de débit Relaxed ou Custom pour les formulaires que vous faites passer par un serveur.

Le submit du SDK peut aussi servir dans une server action, mais il n'envoie pas d'en-tête Authorization : un formulaire avec des domaines autorisés ou la vérification humaine Ikiform refuserait ou signalerait l'envoi. Utilisez fetch avec la clé secrète comme ci-dessus.

Recevoir les webhooks dans un route handler

Pour traiter chaque soumission dans votre app Next.js, ajoutez un route handler. Voir Recevoir des webhooks.