Formulaire avec action serveur

PRISM exporte un loader (exécuté sur GET) et une action (exécutée sur POST). Après une action réussie, PRISM relance le loader pour que la page s'affiche avec des données fraîches — à la façon de Remix.

Astuce
L'action peut retourner soit un objet plain (qui repart en rendu via actionData), soit une Response 303 pour rediriger. Préférez la redirection pour les mutations réussies : cela rend l'URL rafraîchissable et empêche le double-envoi sur F5.

Résultat en direct

compteur : 0

src/app/examples/server-action-form/page.tsxTSX
// src/app/examples/server-action-form/page.tsx
const __state = (globalThis as any).__demoCounter ??= { count: 0, lastNote: null };

export async function loader() {
  return { count: __state.count, lastNote: __state.lastNote };
}

export async function action({ request }: ActionArgs) {
  const form = await request.formData();
  const op = String(form.get("op") ?? "");
  const note = String(form.get("note") ?? "").slice(0, 60);
  if (op === "inc") __state.count++;
  if (op === "dec") __state.count--;
  if (op === "reset") { __state.count = 0; __state.lastNote = null; }
  if (note) __state.lastNote = note;
  return { ok: true, op };
}

export default function Page({ data, actionData }) {
  return (
    <form method="post">
      <p>count: {data.count}</p>
      {data.lastNote ? <p>last note: {data.lastNote}</p> : null}
      <button type="submit" name="op" value="inc">+1</button>
      <button type="submit" name="op" value="dec">−1</button>
      <button type="submit" name="op" value="reset">reset</button>
      <input name="note" placeholder="optional note…" />
      {actionData?.ok ? <p>action ran: {actionData.op} ✓</p> : null}
    </form>
  );
}