Askr
Routing & Data

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.

  1. Import the published @askrjs/askr/actions entrypoint.
  2. Keep page actions and forms configuration next to the component, route, or server composition root that owns it.
  3. 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.