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.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>