Building forms with the Forms module: how a form is put together and placed on a page, validating and branching, presenting it, where submissions are stored, and what happens after one arrives, from jobs and records to CRMs and payments.
L1–L2
Unit 07 · Reference
Form input reference
ReferencePage 17 of 20Updated
Every element the form builder offers, one entry each: what the editor configures, the markup the platform renders, what is stored, what the server validates, and the accessibility notes.
Who this is for. The developer building, styling or submitting a form who needs the facts about one element type. After this page you can say, for any element, what the editor configures, what markup the platform prints, what is stored, and what the server checks.
Settings: label, name, placeholder, help, default value, required, regex and custom CSS classes, as described on the elements page.
Markup: a wrapper <div class="base-element-block input-element-block"> plus any custom classes. Inside it, the label, the field (with class="element-input <editor>", id="element-<form id>-<name>" and data-id set to the element's id), and after a failed submit <div class="input-element-validation-errors">. The help text becomes the field's title.
Template path: none. Every element's markup is produced by the platform's PHP, not a template, so there is no file to override. To change one type's markup, print your own markup for elements of that editor in the form layout, and getHtml() for the rest (Rendering and styling).
Stored as: always a list. A single answer is a list of one, trimmed, and an empty answer is an empty list.
Text
Input field
Editor
inputText
Configures
Minimum and maximum length
Renders
<label> and <input type="text"> with minlength, maxlength, pattern (the regex) and required
Stored as
The text, trimmed
Validates
Required; regex; length
Accessibility
Labelled. Nothing marks it invalid after an error; see accessible errors
Textarea
Editor
inputTextarea
Configures
Minimum and maximum length
Renders
<label> and <textarea> with maxlength and required. No minlength: the minimum is checked by the server only
Stored as
The text, trimmed; line breaks kept
Validates
Required; regex; length
Accessibility
Labelled. State a minimum length in the label, since the browser does not enforce it
Email
Editor
inputEmail
Configures
Unique: refuse an address already used in another completed submission of this form
Renders
<label> and <input type="email"> with pattern and required
Stored as
The text, trimmed
Validates
Required; regex; unique against completed submissions when set. Not the format: that is the browser's type="email" only
Accessibility
Labelled. The browser's own format message is in the visitor's language and is announced
Phone
Editor
inputTel
Configures
Nothing beyond the shared settings
Renders
<label> and <input type="tel"> with pattern and required. Mobile keyboards show digits
Stored as
The text, trimmed, in whatever format was typed
Validates
Required; regex. Nothing checks that it is a phone number unless a regex does
Accessibility
Labelled. Give the expected format in the label, not only in a placeholder
Hidden input
Editor
inputHidden
Configures
The default value, which is the value sent
Renders
<input type="hidden"> alone, with no wrapper or label
Stored as
The text, trimmed
Validates
Required; regex. The visitor can change the value; see hidden inputs
Accessibility
Not announced
Numbers
Numeric
Editor
inputNumber
Configures
Minimum, maximum, step
Renders
<label> and <input type="number"> with min, max, step and required
Stored as
A number. Spaces are removed and a comma becomes a decimal point, so 1 000 is 1000 and 1,5 is 1.5; 1.234,5 is refused
Validates
Is a number; minimum; maximum; step, counted from the minimum. Use whole-number steps: a decimal step such as 0.1 can see a valid answer as not divisible
Accessibility
Labelled
Slider
Editor
inputSlider
Configures
Minimum, maximum, step
Renders
<label> and <input type="range" class="form-range"> with min and max. A step set on the element is not printed on the input, so the browser moves in steps of 1 while the server checks the configured step: add the step attribute with a line of script, or use Numeric. No required, and no display of the chosen number
Stored as
A number
Validates
As Numeric
Accessibility
Keyboard-operable, but the visitor cannot see the value chosen. Show it with a few lines of script writing the value into an <output>, or use Numeric. A slider always has a value, so "required" means nothing for it
Range
Editor
inputRange
Configures
Minimum, maximum, step, applied to both numbers
Renders
One <label> and two <input type="number" name="<name>[]">, from and to
Stored as
Two numbers, sorted lower first, whichever order they were typed in. Through the API the pair can also be sent as 10:20; send exactly two values
Validates
As Numeric, on both
Accessibility
Both inputs get the sameid, so the label points at the first and the second has no label. Add an aria-label to each ("From", "To") with a line of script, or use two Numeric fields when that matters
Choices
Select
Editor
inputSelect
Configures
Options (each a value and a title); Allow multi select; minimum and maximum number of choices
Renders
<label> and <select>. A single select starts with an empty option whose text is the placeholder, or the title of an option with an empty value. A title written Group::Option puts the option in an <optgroup label="Group">. Multi select adds multiple and names the field <name>[]
Stored as
The chosen value, or for multi select the list of values without duplicates
Validates
Required. Not that the value is one of the options, and not the minimum or maximum number of choices
Accessibility
Labelled. Multi-selects are awkward on every platform; prefer a checkbox list for up to about ten options
Radio
Editor
inputRadio
Configures
Options
Renders
<div class="radio-list list-group"> holding the label as a <div class="element-label">, then one <label class="list-group-item"> around each <input type="radio">, with ids ending -radio-0, -radio-1…. An option with an empty value is rendered disabled. No required attribute
Stored as
The chosen value
Validates
Required, on the server only. Not that the value is one of the options
Accessibility
Each option is labelled, but the question is not tied to the group: a screen reader announces "Yes, radio button" without the question. Put a radio question in its own element group, so its legend carries the question, or add role="radiogroup" and aria-labelledby with script
Checkbox
Editor
inputCheckbox
Configures
Options; minimum and maximum number ticked
Renders
One option: a single <input type="checkbox"> with its label as a real <label>, and required when required. Two or more: <div class="checkbox-list list-group"> with the label as a <div> and one labelled box per option, named <name>[], with ids ending -checkbox-0…, and no required
Stored as
One option: its value when ticked, nothing when not. Several: the list of ticked values
Validates
Required (for a list: at least one ticked). Not the minimum or maximum number ticked
Accessibility
A single box is fully labelled, which suits consent. A list has the same problem as Radio; give it a group
Dates, files and the rest
Date/Time
Editor
inputDatetime
Configures
Mode (date, time, or date and time); earliest and latest
Renders
<label> and <input type="date">, type="time" or type="datetime-local", with min, max and required. No placeholder
Stored as
The browser's text: 2026-10-05, 14:30 or 2026-10-05T14:30. No time zone is recorded
Validates
Required; earliest and latest
Accessibility
The browser's own date control, which is accessible in current browsers
File
Editor
inputFile
Configures
Allow multiple file uploads
Renders
<label> and <input type="file"> with required, and multiple with the name <name>[] when allowed. No accept. The form layout must add enctype="multipart/form-data" when hasFileInput() is true
Stored as
Each file is saved to the Files manager under user-files/form-submissions/<form name>/<submission token>/, and the answer is a list of {type, id, name, link}, where type is image or file and link is the file's address, starting /files/
Validates
Required, which only a file sent as a multipart/form-data file part meets: a file sent as a data URL in JSON does not count (Submitting a form through the API). No type or size limit beyond the server's upload limit. Who can open the file afterwards: see File uploads
Accessibility
Labelled. A failed submit cannot refill it, so say "choose the file again" in the error summary
Captcha
Editor
inputCaptcha
Configures
Mode: "I am not a robot" checkbox, or an invisible badge. On the form: Require completed captcha
Renders
<div class="g-recaptcha"> with the site key, a small inline script and Google's recaptcha/api.js, both carrying the page nonce. The checkbox mode also renders a label and a helper checkbox that the widget ticks when solved. The invisible mode takes over the enclosing form's submit
Stored as
Nothing: the token is checked with Google and dropped
Validates
Always required, and verified. Without Require completed captcha, a submission without a solved captcha is still saved, as incomplete; with it, nothing is saved (422 from the API). Tick it on every public form (Captcha)
Accessibility
Google's widget, with Google's own audio challenge
Submit button
Editor
inputSubmitButton
Configures
The label is the button text
Renders
<button type="submit" name="<name>">, with no wrapper and no class
Stored as
Nothing, but it counts as an input when the platform decides whether a submission is complete
Validates
Nothing
Accessibility
A real button. Use it instead of the layout's button, not as well as it
Groups, static elements and payment
Element group
Editor
group
Configures
Group title; the elements inside it
Renders
<fieldset class="element-block element-group-block"> with the title as <legend class="element-label element-text">, then each child's own markup
Stored as
Nothing; its children are stored as themselves
Accessibility
The legend is announced with each field: the right container for radio questions, checkbox lists and addresses
Richtext
Editor
staticRichtext
Configures
The content, in the rich-text editor
Renders
<div class="element-block static-element-block"><div class="element-text">, then the content as stored
Stored as
Nothing
Accessibility
Ordinary content. Use real headings and links inside it
HTML snippet
Editor
staticCode
Configures
Raw HTML and script
Renders
The content exactly as entered, with no wrapper. Every <script> is given the page's nonce, so it runs. See the note on static elements
Covered on Taking payments. It needs the platform's /assets/forms/js-payment-element.js (js-payment-element.js reference) and is processed only once every other answer on the form is valid