Audience: Contributor
Copy-ready page tree recipes grounded in real children.json files. Nesting rules and insert/trim behavior live on Page structure; confirm types against the Catalog.
See also: Page structure · Catalog · Values and bindings
Most screens use header + content (+ optional footer) as page-level children:
[
{
"type": "ion-header",
"children": [
{
"type": "ion-toolbar",
"children": [
{
"type": "ion-title",
"children": [
{
"type": "text",
"props": [
{ "name": "html", "value": { "raw": "Home" } }
]
}
]
}
]
}
]
},
{
"type": "ion-content",
"children": []
}
]
Put body content under ion-content.
Palette default from ionic/button/children.json (text only — no fill, no actions):
{
"type": "ion-button",
"children": [
{
"type": "text",
"props": [
{ "name": "html", "value": { "raw": "Button Text..." } },
{ "name": "wrapper", "value": { "raw": "span" } }
]
}
]
}
Common extension (not from children.json) — add attrs/actions after insert:
{
"type": "ion-button",
"props": [
{ "name": "fill", "type": "attr", "value": { "raw": "solid" } }
],
"children": [
{
"type": "text",
"props": [
{ "name": "html", "value": { "raw": "Continue" } },
{ "name": "wrapper", "value": { "raw": "span" } }
]
}
],
"actions": [
{
"event": "click",
"pageId": 10,
"direction": "forward"
}
]
}
ion-button only allows a restricted child set (see catalog).
Prefer copying lokakrt/form/children.json wholesale, then rename field values. Shape from that file:
lkt-form hostion-item → ion-input with field + validatorslkt-form-error with matching fieldion-button with { "event": "click", "submitForm": true }Field excerpt (from the real starter):
{
"type": "ion-item",
"children": [
{
"type": "ion-input",
"props": [
{ "name": "field", "type": "attr", "value": { "raw": "email" } },
{ "name": "label", "type": "attr", "value": { "raw": "Email" } },
{ "name": "type", "type": "attr", "value": { "raw": "email" } }
],
"validators": [
{ "type": "required" },
{ "type": "email" }
]
}
]
}
submitSuccess / submitFailure belong on the lkt-form host, not the submit button — see Actions and validators.
lkt-for-each iterates a data source. The prop name is source (editor group may be dataSource). Default raw value in schema is "greek". Expressions bind through state.data.<sourceName>.* — there is no item root.
From lokakrt/for-each/children.json:
{
"type": "lkt-for-each",
"props": [
{
"name": "source",
"type": "none",
"value": { "raw": "greek" }
}
],
"children": [
{
"type": "ion-item",
"children": [
{
"type": "text",
"props": [
{
"name": "html",
"type": "none",
"value": { "expr": "state.data.greek.name" }
},
{ "name": "wrapper", "value": { "raw": "span" } }
]
}
]
}
]
}
Copy from that file (and a working page) rather than inventing expression roots. See Values and bindings.
From ionic/grid/children.json:
{
"type": "ion-grid",
"children": [
{
"type": "ion-row",
"children": [
{
"type": "ion-col",
"props": [
{ "name": "size", "type": "attr", "value": { "raw": "6" } }
],
"children": []
},
{
"type": "ion-col",
"props": [
{ "name": "size", "type": "attr", "value": { "raw": "6" } }
],
"children": []
}
]
}
]
}
Wrap existing body nodes by moving them under a col — do not duplicate. Confirm each level in the catalog.
Extract a block and insert lkt-block instances (Blocks and reuse). Keep masters out of page JSON.
type appears in the Catalog.text nodes.id / lkt_id / version on persisted nodes.lkt-block instances have blockId and no expanded children.raw or expr, not both.