form.json defines what someone fills out: fields, their types, validation rules, and
any computed values. Open it from Project Files → form.json.
The editor
- Tap the title in the app bar to rename the form (this is what shows as the screen title in Fill Out a Form).
- + Add Field opens a sheet to configure a new field: type, label, ID, required, an autocomplete key, and type-specific options (below).
- Tap a field's edit icon to reopen that sheet; delete to remove it.
- Drag to reorder fields - the order here is the order they render in the form.
- Save appears whenever there are unsaved changes.
The field ID auto-fills from the label (lowercased, spaces → underscores) the first time you type a label; edit the ID directly and it stops auto-following.
Field types
| Type | What it's for | Type-specific options in the editor |
|---|---|---|
| Text | Free text | Min/max length, regex pattern |
| Number | Numeric entry | Min/max value |
| Currency | Money entry | Min/max value |
| Date | A date | Date format hint |
| Dropdown | Pick one of a fixed list | Options list (add/remove chips) |
| Checkbox | On/off | - |
| File | A photo/file upload | Accept filter (e.g. image/*) |
| Signature | Captured signature | - |
| Repeating Group | A variable-length list of sub-fields (line items, options, etc.) | See below |
Required (a toggle) and Autocomplete Key (a text field - see Autocomplete below) are available on every applicable type, at the top of the sheet.
formatdoesn't do anything yet. The date field's "Date format" box and a computed field's format setting are stored but not applied anywhere - there's no built-in currency/date/phone formatter. Do display formatting where the value is actually shown, i.e. in the layout's text blocks (see Block Types Reference), not here.
Repeating groups
A repeating group is a list of rows, each with its own set of sub-fields - the right shape for "add as many line items/options/passengers as needed" instead of a fixed number of similarly-named fields. At fill-out time it renders as an "Add row" list (see Fill Out a Form); in the submitted data it's a list of objects, one per row, keyed by each sub-field's ID.
The editor can create a repeating-group field, but can't yet configure its
sub-fields - you'll need to edit form.json directly for that part (open it in the
Script Editor - it edits any plain-text file, not just scripts -
or any external editor). The shape:
{
"id": "options",
"type": "repeating-group",
"label": "Aftermarket Options",
"fields": [
{ "id": "description", "type": "text", "label": "Description" },
{ "id": "price", "type": "currency", "label": "Price", "min": 0 }
]
}
fields here is a normal array of field definitions, same shape as top-level fields
(minus nested repeating groups - one level is as deep as this goes).
Autocomplete
Text, Number, Currency, and Date fields can offer as-you-type suggestions, pulled from
that project's autocomplete.json - a plain list of suggested values the form's
author curates ahead of time in the Autocomplete Editor, not
something that grows from what people type during fill-out.
To wire it up:
- Open the Autocomplete Editor and add a key with its list
of suggested values - e.g. a
makekey with a list of car makes. - In this editor, set that field's Autocomplete Key to the same key (e.g.
make).
A key can be reused across multiple fields. A field with no Autocomplete Key set (or
one that doesn't match anything in autocomplete.json) just doesn't offer
suggestions - not an error.
Computed fields
A computed field derives its value from other fields with a formula, evaluated and
snapshotted at submit time - not something someone fills in directly. There's no
in-app UI for these yet; add them to form.json's computed array by hand:
"computed": [
{ "id": "price_with_tax", "label": "Price + Tax", "expression": "sale_price * 1.08" }
]
- The
expressionsyntax is deliberately simple - arithmetic and field access only (sale_price * 1.08,inputs['sale_price'] * 1.08, both work). It is not JavaScript: no.map()/.reduce()/method calls. See Scripting & Processing for exactly what this syntax supports. - Summing or transforming a repeating group's rows needs real JavaScript, which
this simple formula syntax can't do - put that logic in a layout block's template or
in
process.jsinstead (see Block Types Reference and Scripting & Processing). - Once computed, a value is available in
validate.js,process.js, and the layout under its ownid, same as a directly-submitted field. - Calculated once, not live: a computed value is evaluated at generation time and baked directly into that PDF - there's no separate submission record it's stored in. Editing the formula later doesn't retroactively change PDFs already generated.
Validation reference
| Key | Applies to | Meaning |
|---|---|---|
| required | any | Must be filled in |
| min / max | number, currency | Numeric bounds |
| min_length / max_length | text | Character-count bounds |
| regex | text | Must match this pattern |
| options | dropdown | The picklist |
| accept | file | MIME filter, e.g. "image/*" |
Anything beyond these declarative rules - comparing two fields against each other, for
example - belongs in validate.js. See Scripting &
Processing.