The page format
A page is one JSON document with four parts: what it is about (data), what it holds (fields), how it is laid out (layout), and its name and title. Nothing in it is code, so a page can be stored, sent, generated or edited by a designer.
{
"fieldia": "0.1",
"id": "customer",
"title": "Customer",
"description": "Optional text under the title",
"data": { "kind": "record", "model": "partner" },
"fields": { "...": "..." },
"layout": { "type": "sheet", "id": "sheet", "children": [] }
}
data: a record or responses
{ "kind": "record", "model": "partner" }— the page edits one record of a model: it loads it, tracks what changed, and saves the changes.{ "kind": "responses" }— the page collects answers, like a survey or a sign-up: each submit stores one response, and the form starts again.
The model is only a name your data source understands; Fieldia passes it through.
fields: what the page holds
Each field is declared once, by name. The layout then points at it, so the same field can be described in one place and placed anywhere.
"fields": {
"email": {
"type": "char",
"label": "Email",
"help": "We send the joining details here.",
"required": true,
"pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
},
"country_id": { "type": "many2one", "label": "Country", "relation": "country" }
}
Every field has a type and a label, and may have help, required, readonly and a default. Each type adds its own options — options for a selection, relation for a link to another record, min and max for a number. Fields lists them all.
layout: how it is arranged
A page's layout is one of four kinds:
| Layout | For |
|---|---|
sections | A form in titled sections, each with 1 to 4 columns. Sign-ups, settings, simple records. |
sheet | A business record: a title (with an optional photo), a statusbar, buttons, stat buttons, a ribbon, alerts, then sections and tabs, and an optional side panel. |
tabs | Tabs at the top level, each holding sections and fields. |
wizard | Steps, one at a time, with a progress bar. A step whose condition is false is skipped — that is how a survey branches. |
Inside them, these nodes can be nested freely:
| Node | What it is |
|---|---|
field | A field, by name: { "type": "field", "id": "email", "field": "email" }. It can override the label, choose a widget (such as radio or tags), set a placeholder, span colspan columns, and carry conditions. |
section | A titled group with columns (1–4) and a description. |
tabs | Tabs, each a tab with a label and children. |
button | A button that names an action. Fieldia hands the press to your app with the record; your app decides what it does. It can ask to confirm first. |
text | A heading, a paragraph or a note. |
slot | A named place your app fills with its own content — an activity feed, a map, a chart. |
Every node has an id, unique in the page. Ids are what a designer, a test or your app use to find a node.
Conditions: invisible, readonly, required
A field node, a section, a tab, a step or a button can be hidden, locked or made required by a condition written in the page. A condition is true, false, or an expression over the record's values:
{ "type": "field", "id": "other_role", "field": "other_role",
"invisible": "role != 'other'",
"required": true }
{ "type": "field", "id": "discount", "field": "discount",
"readonly": "state in ('done', 'cancel') or not is_company" }
Expressions read like Python: == != < > <= >=, in and not in with a list, and or not, True False None, numbers and quoted strings. An empty value is false. Conditions are checked again on every change, and a hidden field's value is left out of what is saved or submitted.
A condition that names a field the page does not have is refused when the page is checked, not when someone fills it in.
Checking a page
import { validatePage } from '@fieldia/core';
const checked = validatePage(json);
if (!checked.ok) console.log(checked.issues); // [{ path: 'layout.children[0]...', message: '...' }]
validatePage checks the shape, that every id is unique, that every field a node or a condition names exists, and that every condition parses. The viewer runs it too, and refuses a page that fails with the same list of issues. For editors and other languages the format is also a JSON Schema: @fieldia/core/page.schema.json.