Formulaire avec amélioration progressive

<Form name="X"> génère un formulaire HTML standard ciblant /_bext/action/X. Sans JS, il est soumis normalement et le serveur répond avec un 303. Avec FORM_CLIENT_RUNTIME sur la page, les soumissions passent par fetch avec x-bext-form: 1 — le serveur retourne le JSON de l'action, et un CustomEvent bext:result est déclenché sur le formulaire. Même formulaire, deux chemins.

Astuce
Le formulaire fonctionne sans JavaScript : sans le runtime, le navigateur soumet normalement et l'action renvoie une redirection 303. FORM_CLIENT_RUNTIME n'ajoute que ~400 o de JS vanilla et ne modifie jamais le balisage du formulaire — aucun attribut data-* supplémentaire n'est nécessaire.

Essayez

inactif

src/actions/echoFormDemo.tsTypeScript
// src/actions/echoFormDemo.ts — server action returning JSON
"use server";
export async function echoFormDemo(req: Request): Promise<Response> {
  const form = await req.formData();
  return Response.json({ greeting: "hello, " + form.get("name"), at: new Date().toISOString() });
}

// page.tsx — Form component + client runtime
import { Form, FORM_CLIENT_RUNTIME } from "@bext-stack/framework/form";

// Same form works with or without JS:
// - no JS → standard POST 303 redirect
// - JS on → fetch with x-bext-form:1, server returns JSON, bext:result fires
<Form name="echoFormDemo">
  <input name="name" required />
  <button type="submit">greet</button>
</Form>

// Include the runtime once per page (inlines ~400 B of vanilla JS)
<Raw html={FORM_CLIENT_RUNTIME} />

<script>
  document.addEventListener("bext:pending", e => { /* show spinner */ });
  document.addEventListener("bext:result", e => {
    // e.detail.result is the JSON the action returned
    document.getElementById("out").textContent = e.detail.result.greeting;
  });
</script>