Templates & Design
Forms that write straight into a collection
cms.form.builder() reads a collection and renders its fields, so a contact form, an application or a sign-up is a few lines of Twig. Every submission lands in the collection as an object you open in the admin like any other record.
The form builder takes a collection name and gives you back a form. {% set form = cms.form.builder('inquiries') %}, an addField() per field, then {{ form.build() }}. Each field's type comes from the collection's schema, so a date property renders a date picker and an image property renders an upload; you pass a type only to override one. For layout, capture fields with form.field() and hand them to form.layout2Columns() or cms.form.fieldset().
Validation is the schema's, not a second set of rules to keep in sync. Fields accept required, minlength and pattern, and a pattern library means you rarely write a regex: patterns.slug, patterns.currency, patterns.phone.usa, patterns.postCode.uk. The same names work in a schema property, where they expand to the real regex with anchors added, so the browser and the server agree.
What happens after the save is the actions system. newActions, editActions and deleteActions run in order: message, redirect, redirect-object — which substitutes {id} — refresh, back, mailer to send email through a configured mailer (Standard), and webhook to POST the form data as JSON to any URL (Pro). Extensions add their own types, like the bundled Pushover one, also Pro. Mark an optional step continue: true and a failure there is logged instead of stopping the redirect behind it.
On a public page, two settings do the guarding. addOnly: true makes the form create-only, so nobody edits someone else's record by adding ?id= to the URL, and the collection needs create in its publicOperations before anonymous visitors reach it. Image and file fields still work: uploads are held client-side until the parent save returns an ID, then sent against the new record.
Sign-ups get their own switch. register: true retargets the form at /admin/register/{collection}, which creates the user and signs them in in one request — or, when the collection requires email verification, sends a tokenized link and reveals whatever you marked data-verification-message. The collection must be listed in auth.publicRegistration, an allow-list that is empty by default.
A worked case: a contact form on a Site Builder page, built from an inquiries collection with addOnly and two new actions — a mailer that emails you the submission, and a message thanking the visitor. The enquiry is a record in the admin, not a line in a mailbox.
What you get
The schema is the form
addField('email') renders whatever that property is — date picker, upload, rich text. Pass a field type only when you want to override the schema's.
Patterns instead of regex
patterns.slug, patterns.currency, patterns.phone.usa, sixteen postal-code patterns. The same names work in the schema, where they expand anchored.
Actions run in order
message, redirect, redirect-object with {id}, refresh, back, mailer, webhook. Add continue: true so an optional step failing does not block the redirect after it.
Safe to expose
addOnly: true ignores any ID in the URL so a public form can only create. The collection also needs create in its publicOperations before anonymous visitors reach it.
Sign-ups in one option
register: true posts to the registration endpoint, which creates the member and logs them in — or sends a verification email when the collection requires one.
In practice
One snippet
{% set form = cms.form.builder('inquiries', {
addOnly: true,
newActions: [
{action: 'mailer', mailerId: 'inquiry-notice', continue: true},
{action: 'message', text: 'Thanks — we\'ll be in touch.'}
]
}) %}
{% do form.addField('name') %}
{% do form.addField('email') %}
{% do form.addField('message', {field: 'textarea'}) %}
{{ form.build() }}
A public enquiry form. addOnly keeps it create-only, the mailer action emails the submission, and continue: true means a mail failure still lets the thank-you show.
FAQ
Common questions
Is there spam protection or a CAPTCHA?
Total CMS does not ship a CAPTCHA. What it gives you is addOnly, which stops a public form editing existing records, and publicOperations, which decides whether anonymous visitors can create in that collection at all. The docs are explicit that a CAPTCHA or a rate limit is the operator's job, and that this still applies to registration forms even when email verification is turned on.
Where do submissions go?
Into the collection the form was built from, as ordinary objects. Open, edit, export or query them like any other record, and set that collection's defaults once in its Form Settings — helpStyle, and the newActions, editActions and deleteActions every form on it inherits.
Can I style the form myself?
Yes. The markup is yours to style, and the form carries state classes through the save — unsaved, processing, success, error, and actions-completed once emails and webhooks have finished. Add the no-status-banner class to suppress the full-screen overlay and design your own feedback instead. Help text placement is a setting: label, tooltip or box, on hover or on focus.
How do I get notified when someone submits?
Add a mailer action with the ID of a configured mailer; the template can read every submitted field as data.fieldName and the signed-in user as user.fieldName. Mailer actions are included in Standard. A webhook action POSTs the form data as JSON to any URL and needs Pro, as do extension-provided actions such as Pushover push notifications.
Related features
- 36+ admin field types Rich text, dates, prices, colors, selects, lists, images, video, cards and decks — each with the right editor, the right storage shape and the right filters.
- Pages you can gate Put any page behind an access group, keep it as a draft until it is ready, or attach your own middleware — per page, from the admin.
- Automations Write a PHP handler, give it a cron schedule, a webhook, or a content event. Run history, guard rails and auto-disable are built in.
Want the details? Read the Front-end forms documentation →
Or keep browsing: every Total CMS feature →
Start Your Free 45-Day Trial