Forms

Forms are built in Cockpit and rendered by the storefront — the delivery route that serves a published form's FormKit schema, the block that renders it, and how a submission is recorded.

Forms in a storefront — contact, quote request, sample order, callback, registration of interest — are built in Cockpit and rendered by the theme. A customer's team creates the form and its fields; your theme renders whatever they built.

That means a new form is not a deploy. It's a form the customer creates and an editor places on a page.

How it works

text
Cockpit                 →  a form, with a slug and a field definition
    ↓
GET /v1/forms           →  the published form (status: live)
    ↓
GET /api/forms/{slug}   →  your BFF, localized for the buyer
    ↓
the `form` block        →  FormKit renders the definition verbatim
    ↓
POST /api/forms/{slug}/submit  →  POST /v1/forms/submissions

The definition is a FormKit schema — the same machinery the checkout uses. That's why an editor-built form gets real validation, conditional fields, and accessible markup without anyone writing a component for it.

FormKit, not another form library. If you're adding a custom input type, it's a FormKit input.

The delivery route

RouteWhat it does
GET /api/forms/{slug}Returns the published form for that slug — its name, its FormKit definition, and its settings.
POST /api/forms/{slug}/submitRecords a submission.

Both are on your own origin; the gateway call happens inside them. See The BFF pattern.

Three behaviours of the delivery route are worth knowing:

Only live forms are served. A draft form is a 404. There is no way for an unpublished form to appear on a storefront, which is the property you want — but it also means "the form 404s" usually means "it hasn't been published", not "the slug is wrong".

The definition is localized before it reaches the browser. Labels, validation messages, and the submit label are resolved to the buyer's locale server-side, so one form definition serves every language rather than one per language.

Settings come with it. The submit button label and the success message are the form's own, set by whoever built it — don't hard-code either in your theme.

The block

The form block renders a form on any page. It's in the standalone content group of the block catalog, so an editor can drop it into a content section like any other block.

Its distinguishing feature is how the form is chosen. Rather than typing a slug, the editor picks from the tenant's live forms in a picker: the theme registers a complex option type through a Blökkli module, and the option renders as a list of real forms rather than a free-text field.

That's the pattern to copy whenever an editor has to reference a platform record. A free-text id field guarantees typos; a picker cannot produce an invalid reference.

The block renders the same way on a live page, on the editor canvas, and in preview — so what the editor sees while placing the form is the form.

Submissions

A submission is recorded against the form on the platform. Submissions are then managed where the form was built, and are reachable from the CLI for scripting and export:

Shell
revenexx forms submissions-list --form-id <FORM_ID>

The full command set — create, list, get, update, delete, prune — is in the forms command reference.

Two things to be deliberate about:

  • A form is a data-collection surface, so treat its payload as personal data. Don't log submission bodies from your server routes; see Logs.
  • Don't use a form as an integration transport. If a quote request has to reach a CRM, build that in Integration Studio off the submission, rather than posting to a third party from your theme's submit route.

Adding your own fields

Because the definition is a FormKit schema, extending what a form can collect is a FormKit question, not a platform one. Register a custom input in the theme's FormKit configuration and it becomes available to schemas rendered by the theme.

The base layer already does this — the barcode-scanner input, for instance, is a custom FormKit input with a camera overlay, which is what makes "scan a SKU into a quick-order form" a field rather than a feature.

Next steps

Was this page helpful?