Form Success & Error Handling
The submission commits and the request returns. What happens on screen next is a product decision, not a technical one: a support ticket wants a reference number, a newsletter signup wants to stay put and accept another address, a checkout wants to move on.
onSuccess and onError make that decision declarative:
forms:
- id: 1
name: contact
title: Contact Sales
submitTo: { table: leads }
fields:
- { kind: table-field, column: email, required: true }
onSuccess:
type: successPage
title: Thank you
message: We received your message and will reply within one business day.
onError:
type: message
message: Something went wrong saving your request. Please try again.
onSuccess Types
onSuccess is a union discriminated by type. Each type carries its own properties.
type |
Properties | Behavior |
|---|---|---|
successPage |
title, message, buttonLabel, buttonHref, actions[], showSummary |
Replaces the form with a success screen. |
redirect |
url (required), delaySeconds |
Navigates away. delaySeconds defaults to 2; 0 navigates immediately. |
reset |
message, preserveFields[] |
Clears the form for another submission, keeping the listed fields' values. |
toast |
message (required), variant (success | info) |
Shows a transient notification and leaves the filled form in place. |
message |
message (required) |
Replaces the form with an inline message. |
Omit onSuccess entirely and the form falls back to a success toast reading "Submitted." — a sane default, but rarely the one you want in production.
reset is the type to reach for on a single-page form a submitter fills repeatedly. On a multi-step layout it clears the answers but does not return the submitter to the first step, so prefer successPage with a reset action there.
Success-Page Actions
A successPage can offer the submitter a next move. Each entry in actions[] renders as a button:
onSuccess:
type: successPage
title: Ticket received
message: Our team has been notified.
showSummary: true
actions:
- { label: Submit another, action: reset }
- { label: Back to help centre, action: navigate, url: /help }
| Property | Description | Default |
|---|---|---|
label |
Button text. Supports $t: keys. |
Required. |
action |
reset returns the empty form; navigate sends the submitter to url. |
Required. |
url |
Navigation target. Required for navigate, ignored by reset. |
— |
showSummary: true lists the submitted values below the message. It skips hidden fields and applies per-field read permissions, so a summary never shows a submitter something they were not allowed to read back.
Template Variables
Success copy and redirect targets can quote the submission that just happened. Three variables are substituted at submit time, in title, message and url alike:
| Variable | Resolves to |
|---|---|
$submission.id |
The ledger row id. Empty when submitTo.storeSubmission is false. |
$record.id |
The bound-table row id. Empty when the form has no submitTo.table. |
$record.<column> |
A column of the inserted row — but only one the submitter themselves supplied. |
onSuccess:
type: redirect
url: /thank-you?ref=$submission.id
delaySeconds: 0
Values substituted into a url are percent-encoded, so an email address survives the query string intact. The $record.<column> restriction is deliberate: server-computed and privileged columns are never exposed through a redirect URL, however tempting the shortcut.
An unresolved variable becomes empty, never the literal token. A $record.id on a form with no bound table substitutes to nothing, so /done?recId=$record.id navigates to /done?recId=. The redirect still fires and the submission is still recorded — you get a silently empty parameter rather than a broken URL or a leaked template string. Check the target page tolerates an empty value before templating one in.
onError
onError covers whole-submission failures — the write was rejected, the request did not complete.
| Property | Description | Default |
|---|---|---|
type |
toast, message (inline), or errorPage (full page). |
Required. |
message |
Body text. Supports $t: keys. |
Required. |
title |
Heading, used by errorPage. |
— |
variant |
error or warning. Only meaningful for type: toast. |
error |
Omitted, it falls back to a toast reading "Submission failed."
Per-field validation errors are a different channel. An invalid email or a missing required value comes back as a fieldErrors list and is rendered inline against the offending input — onError fires alongside it as the whole-form summary, not instead of it.
Related Pages
- Submissions — the dual write that has to commit before
onSuccessruns. - Form Fields — the per-field read permissions
showSummaryhonors. - Multi-Step Forms — what "after the last step" means for these types.
- Forms Overview — where
onSuccessandonErrorsit in the full form schema.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.