Guide 04 · Developer docs

Input types, layouts, components and images

The platform's central abstraction, in one line: an input type defines what an editor can enter, a layout area binds one input type to a named slot, a layout groups areas and points at a template, a record or component holds the content for each area, and the template reads it back. Read this book straight after the template languages.

L1–L3
Unit 01 · The model, end to end

Where content is stored, and what a change does to it

Content lives in a record's revisions, and each value is linked to its layout area by the area's id, not to the template. From that you can predict what a template rewrite, an area rename, a layout switch, a type change, hiding and deleting each do to an author's content.

Who this is for. Anyone about to change a layout that already has content: the content modeller planning a change and the developer deploying it. After this page you can say, before you make a change, whether authors will lose anything.

The storage shape

A record does not hold its content itself. Pages, blog posts, books, units and book pages, FAQ pages and questions, custom entity records, product and category descriptions, forms and global components are versioned: their content is stored in revisions, and one revision at a time is the active revision, the one visitors see. A template reaches a value along this chain:

  1. the record, such as the $page or $post the template receives;
  2. its active revision, which getActiveRevision() returns;
  3. an area row on that revision for each layout area that has a value, pointing at the layout area by its id;
  4. the content row that the area row points at, holding the value.

You do not walk the chain by hand. On a versioned record every area getter reads the active revision for you: $page->getAreaText('intro') is $page->getActiveRevision()->getAreaText('intro'), and the shorthand {$page->intro} (Twig {{ page.intro }}) is the shortest way to the same value. Every saved revision keeps its own area rows, so an earlier revision still has the content it was saved with. A component placed in an area is stored with the revision whose area holds it.

The content row is the same shape for every input type. It has a column for each kind of value, and the input type's category decides which one is filled:

CategoryWhere the value is in the content row
text (every editor)The text column. A select stores its option key, a multiselect a JSON list of keys, a switch 1 or 0, a date 2026-10-19
record, recordsAlso the text column: the id, or the ids separated by commas
image, fileA link to the image or file in the file manager
componentA component of its own, or a link to a global component
collectionAn ordered list of collection elements, each with a position, a published flag, and a component or a global component

The shorthand reads whichever column the category fills and returns the value in a usable form: text as text, a switch as true or false, an image, file or record as its object. The explicit getters name the column: getAreaText() reads the text column, getAreaImage() the image link, getAreaRecord() turns the stored id into a record, getAreaCollectionElements() returns the list. An empty value is not stored at all: when a record is saved, an area whose content is empty loses its area row.

What each change does to content

A template asks for content by the layout area's current name; the content is linked to the layout area's id. Every outcome below follows from those two facts.

ChangeWhat happens to existing content
Rewrite the template, or point the layout at another fileNothing. The template only reads; the new one reads the same names.
Rename a layout areaThe content stays, now under the new name. Update every template and integration that reads the area to the new name: until then, the shorthand {$page->oldName} raises "Property oldName does not exist" in Smarty and prints nothing in Twig; getAreaText('oldName') prints nothing; and an API write naming the old area is refused with a 422.
Switch a record to another layoutThe admin copies each value into the area of the same name on the new layout. Values with no same-named area on the new layout are left out of the new revision; earlier revisions still have them.
Change a layout area's input typeExisting content is read with the new type. Within the same category (text to select, say) it still shows, and is checked against the new rules the next time the record is saved: a select value must then be one of the keys. Across categories (text to image) the old value reads as empty, and is left out of the next revision saved.
Hide a layout areaThe area leaves the editing form. Templates keep reading the stored value until the record is next saved; that revision no longer has it. Hiding can be reverted with Show this area.
Delete a layout areaThe content of every record goes with it, and cannot be recovered. Only users of the Developer type are offered the delete button for a saved area.
Template code
{* after renaming the layout area 'intro' to 'summary' *}
{$page->summary}                 {* prints the content *}
{$page->intro}                   {* error: Property intro does not exist *}
{$page->getAreaText('intro')}    {* prints nothing, no error *}
{# after renaming the layout area 'intro' to 'summary' #}
{{ page.summary }}                 {# prints the content #}
{{ page.intro }}                   {# prints nothing, no error #}
{{ page.getAreaText('intro') }}    {# prints nothing, no error #}

Smarty's shorthand raises an error on the old name; the getter and Twig print nothing instead. Release a rename together with the template changes it needs.

The safe procedure, briefly

  • To rename a layout area, change the name, every template that reads it and every integration that writes it, and release them together. Search the repository for the old name in quotes and as a property (->intro, .intro).
  • To retire a layout area, hide it rather than delete it. Hiding is reversible; deleting is not.
  • To change a type across categories, add a new area with the new type, move the content across, switch the templates to the new name, then hide the old area.

The full migration, with the order of steps and what cannot be recovered, is on Renaming and retiring without losing content. Writing content into areas through the API, where the same names apply, is on Writing areas.

Go deeper

Was this page helpful?

Your answer goes to the docs team.