Forms
Same-origin form submits are intercepted. Use GET forms to navigate and POST to drive UI updates through patches.
GET forms
Fields become the query string, then
navigate(). Useful for search with URL state -
shareable, back and forward.
When <navigation-root> is present, that is a soft navigation. See
Soft Navigation.
search.tsx
export function SearchForm() { return ( <form method="GET" action="/search"> <input name="q" /> <button type="submit">Search</button> </form> ); }
results.tsx
import type { ReadArgs } from "dashi"; export function Search({ ctx, html }: ReadArgs) { const q = ctx.url.searchParams.get("q") ?? ""; return html(<p>{q}</p>); }
POST forms
The form can sit anywhere on the page. While the write is in flight aria-busy
is set and a second submit is dropped.
Patches
A 2xx patch list applies and the form resets. A 4xx patch list applies and the fields stay. See Write handler.
add_form.tsx
export function AddForm() { return ( <form method="POST" action="/todos"> <input name="title" /> <button type="submit">Add</button> </form> ); }
add.tsx
import { patch, type WriteArgs } from "dashi"; export async function addTodo({ ctx, patches }: WriteArgs) { const title = (await ctx.req.formData()).get("title"); if (typeof title !== "string") { return patches([ patch.replace("#error", <p>Title is required</p>), ], { status: 400 }); } return patches([ patch.append("#todos", <li>{title}</li>), ]); }
Document load
When the result cannot stay on the page, the browser loads the document.
Redirect
A same-origin redirect swaps in place when <navigation-root> is present.
Without the host, or when the body is not HTML, the browser does a real document
load. See Soft Navigation.
hardNavigation
hardNavigation on the <form> or the submitter skips intercept and does a
real document load. Same as on <a>, see
Soft Navigation.
navigate_away.tsx
export function NavigateForm() { return ( <form method="GET" action="/elsewhere" hardNavigation> <button type="submit">Go elsewhere</button> </form> ); }