Page Actions and Forms
Use page actions and forms with the current published Askr packages.
@askrjs/askr/actions0.0.59
How to use page actions and forms
Bind form input in the server action, return field-aware failures without navigation, and redirect only after a successful write.
- Import the published @askrjs/askr/actions entrypoint.
- Keep page actions and forms configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
<form method="post" action="/projects/new">
<Field><FieldLabel for="name">Name</FieldLabel><Input id="name" name="name" required /></Field>
<Button type="submit">Create project</Button>
</form>Action contract
defineAction({ id, input, invalidates }) declares a named action: id identifies it, input is an ObjectSchema<TInput> from @askrjs/schema that validates incoming values, and an optional invalidates list names the query prefixes a successful submission should clear. What comes back is an ActionDescriptor — a plain object describing the contract, not a function you call directly.
Form submission
ActionForm({ action, children, ...props }) renders what the type declarations call a native form bound to a declared action — it's not a synthetic event API layered over fetch, it's a real <form> wired to the ActionDescriptor you pass in. For submitting programmatically instead of through a rendered form, action(descriptor) returns a command handle with a submit(input) method and a state tuple carrying ActionStatus (pending, result, error).
Validation failures
When submitted input fails the action's input schema, the failure comes back as an ActionValidationError with kind: 'invalid', the action id, the raw values that were submitted, an issues array from the schema validator, and fieldErrors — a record mapping each field name to its own array of messages. That per-field shape is what you'd use to highlight individual inputs rather than showing one generic error for the whole form.
Redirects and revalidation
Invalidation after a successful action is driven by the invalidates list on the ActionDescriptor, which clears the matching query prefixes the same way a mutation's affects() option does. There's no separate redirect API in the actions module itself — navigating away after a submission is ordinary router navigation, and any queries feeding the destination route's loaders pick up fresh data through the router's own revalidation rather than anything action-specific.