Skip to main content
View as Markdown

Form Prefill

A form that already knows the answer should not ask for it. The campaign that sent the visitor is in the URL; the signed-in user's email is in the session. Asking anyway costs a field, and the answer you get back is worse than the one you already had.

prefill maps field names to where their initial value comes from:

forms:
  - id: 1
    name: lead-capture
    title: Request a demo
    submitTo: { table: leads }
    prefill:
      utm_source: $query.utm_source
      utm_campaign: $query.utm_campaign
      plan: Starter
    fields:
      - { kind: table-field, column: email, required: true }
      - { kind: standalone, name: plan, inputType: short-text }
      - { kind: standalone, name: utm_source, inputType: short-text, hidden: true }
      - { kind: standalone, name: utm_campaign, inputType: short-text, hidden: true }

Prefill Sources

Every value in the map is either a reference or a literal. References resolve server-side while the form is being rendered.

Source Resolves to When it cannot resolve
$query.<name> The named URL query-string parameter of the request that rendered the form. Entry dropped; the field renders empty.
$user.<prop> A property of the signed-in user — $user.email, $user.id. Entry dropped; the field renders empty.
String, number, boolean Itself, verbatim — a plain default the submitter can overwrite.

An unresolvable reference is removed from the map, never rendered. The submitter sees an empty input, and the literal $query.utm_campaign never leaks into the HTML.

Prefilled Hidden Fields

A prefill on a hidden: true field is the point of the feature for attribution work. The field renders as an <input type="hidden"> carrying the resolved value, so the campaign the visitor arrived on rides into the record without ever appearing on screen — and without a hand-written tracking script.

prefill:
  campaign: $query.ref
fields:
  - { kind: table-field, column: email, required: true }
  - { kind: table-field, column: campaign, hidden: true }

/forms/lead-capture?ref=partner-x now stores partner-x in the record's campaign column.

$user Needs a Session

$user.<prop> resolves only when the request carries an authenticated session, which in practice means the form requires one. On a public form the reference simply drops and the field renders empty — no error, no leaked session state.

That tolerance is specific to the prefill map. A per-field defaultValue: $user.email on a public form is a configuration mistake rather than a runtime shrug, and it is rejected when the config is decoded, naming the form and the offending field. Either require authentication, or move the reference into prefill.

Prefill Versus defaultValue

Both seed an initial value, and both accept $query / $user references. The choice is about where the wiring lives.

Use When
prefill Attribution and session seeding — the wiring is a concern of the form, and reads better in one block.
defaultValue A value that is part of the field's own definition, such as a fixed starting quantity.
  • Form FieldsdefaultValue, hidden, and the fields a prefill key names.
  • Form ControlsinlinePrefill, where $parent and $record resolve.
  • Access Control — the require level that makes $user resolvable.
  • Forms Overview — where prefill sits 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.

Built with Sovrium