Forms
λ Router can intercept form submissions via the Navigation API. When a form with method="POST" submits to a URL that matches a route with a .formHandler(), the handler receives the FormData and the navigation event instead of rendering the route component.
Defining a Form Handler
Section titled “Defining a Form Handler”Use the .formHandler() method on the route builder:
import { createRouter } from "@studiolambda/router/react";
const router = createRouter(function (route) { route("/contact") .formHandler(async function (formData, event) { const name = formData.get("name"); const email = formData.get("email"); const message = formData.get("message");
await fetch("/api/contact", { method: "POST", body: JSON.stringify({ name, email, message }), headers: { "Content-Type": "application/json" }, }); }) .render(ContactPage);});Handler Signature
Section titled “Handler Signature”type FormHandler = (formData: FormData, event: NavigateEvent) => void | Promise<void>;| Parameter | Type | Description |
|---|---|---|
formData | FormData | The form data from the submission. |
event | NavigateEvent | The raw Navigation API event, providing access to destination URL, navigation type, etc. |
How It Works
Section titled “How It Works”When a <form method="POST"> submits and the Navigation API fires a navigate event, the Router checks:
- Does the destination URL match a registered route?
- Does that route have a
formHandler?
If both are true, the form handler is called directly instead of going through the normal navigation lifecycle (prefetch → render). The form handler receives the FormData extracted from the navigation event and can perform server submissions, mutations, or any async operation.
Example: Login Form
Section titled “Example: Login Form”function LoginPage() { return ( <form method="POST" action="/login"> <label> Email <input type="email" name="email" required /> </label> <label> Password <input type="password" name="password" required /> </label> <button type="submit">Sign in</button> </form> );}
const router = createRouter(function (route) { route("/login") .formHandler(async function (formData) { const email = formData.get("email") as string; const password = formData.get("password") as string;
const response = await fetch("/api/auth/login", { method: "POST", body: JSON.stringify({ email, password }), headers: { "Content-Type": "application/json" }, });
if (!response.ok) { throw new Error("Login failed"); }
// Navigate to dashboard after successful login. navigation.navigate("/dashboard"); }) .render(LoginPage);});Combining with Other Configuration
Section titled “Combining with Other Configuration”Form handlers compose with other route configuration:
route("/settings/profile") .middleware([AuthGuard]) .formHandler(async function (formData) { await updateProfile(Object.fromEntries(formData)); }) .render(ProfileSettings);The middleware applies when rendering the page (GET navigation), and the form handler applies when submitting the form (POST navigation). This keeps your read and write logic cleanly separated on the same route.