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 07 · The shorthand, and working with each input type

The shorthand and live preview

In the admin's live preview, the shorthand keeps every text area editable in place, while getAreaText() keeps only rich text editable. The wrapper that does it, a before and after, why it breaks attributes, and why the shorthand survives an editor change.

Who this is for. Developers whose authors edit in the admin's live preview, and reviewers checking templates. After this page you can say why a field stopped being editable in the preview, and keep both in-place editing and attributes working.

What live preview adds

Live preview shows the page as the author edits it, and lets them type straight into it. For that, the platform wraps each editable text value in a marker element that carries the path to its area. The wrapper is added only on the frontend, and only when the render asks for live-preview tags (insertLivePreviewTags), as the live preview's render does. Visitors never get it. The full markup, for a heading:

HTML
<span class="live-preview-container" contenteditable="true" data-path="…" id="live-preview-container-…" style="all:inherit">Our services</span>

data-path identifies the area; the id is live-preview-container- followed by the same path, base64-encoded; style="all:inherit" keeps the span from changing how the text looks.

Which reads are wrapped

ReadText area (text, textarea, select and the rest)Rich text area
The shorthand, {$component->heading}WrappedWrapped
getAreaHtml('heading')WrappedWrapped
getAreaText('heading')Not wrappedWrapped, without style="all:inherit"

A switch or a date read through the shorthand is never wrapped: the shorthand returns a boolean or a date object for those, not text.

Before and after

For a plain text heading, both versions print the same text to visitors. In live preview only the shorthand version can be edited in place:

Template code
{* before: getAreaText() on a text area has no wrapper,
   so live preview cannot edit the heading in place *}
<h2>{$component->getAreaText('heading')}</h2>

{* after: the shorthand is wrapped, so the author types straight into it *}
<h2>{$component->heading}</h2>
{# before: getAreaText() on a text area has no wrapper,
   so live preview cannot edit the heading in place #}
<h2>{{ component.getAreaText('heading') }}</h2>

{# after: the shorthand is wrapped, so the author types straight into it #}
<h2>{{ component.heading }}</h2>

Nothing looks wrong with the getter version, on the site or in the preview. The field is not editable in place, which is why teams rarely notice they have lost it. This is the strongest argument for the shorthand.

The cost: never inside an attribute

The wrapper is markup. In element content that is what you want; inside an HTML attribute it breaks the tag, and only in live preview, so visitors never see the fault and authors do. The same holds wherever the value is not printed as element content: in a script, in JSON, in a comparison or a calculation, the shorthand gives the wrapped text in live preview, not the stored value. Read plain text there with getAreaText():

Template code
{* breaks in live preview: the class attribute receives a span *}
<section class="banner {$component->width}">

{* correct: plain text in attributes, the shorthand in content *}
<section class="banner {$component->getAreaText('width')}">
    <a class="{$component->getAreaText('cssClasses')}" href="{$component->getAreaText('ctaUrl')}">{$component->linkName}</a>
</section>

{* comparisons too *}
{if $component->getAreaText('width') == 'full'}...{/if}
{# breaks in live preview: the class attribute receives a span #}
<section class="banner {{ component.width }}">

{# correct: plain text in attributes, the shorthand in content #}
<section class="banner {{ component.getAreaText('width') }}">
    <a class="{{ component.getAreaText('cssClasses') }}" href="{{ component.getAreaText('ctaUrl') }}">{{ component.linkName }}</a>
</section>

{# comparisons too #}
{% if component.getAreaText('width') == 'full' %}...{% endif %}

On the live site both versions of the section render identically; the difference only shows up for an author, while editing. The switch and date exceptions hold here too: {if $component->hideDate} is safe, because the value is a boolean.

The other advantage: an editor change needs no template change

The shorthand never assumes which text editor an area has. If an area is later switched from plain text to rich text in the admin, a template that prints it with the shorthand keeps working, and the field stays editable in live preview before and after:

Template code
{* area "summary": a Text area today, rich text tomorrow;
   the template does not change *}
<div class="summary">{$component->summary}</div>
{# area "summary": a Text area today, rich text tomorrow;
   the template does not change #}
<div class="summary">{{ component.summary }}</div>
HTML What visitors receive
<!-- summary as a Text area -->
<div class="summary">Three offices, one team.</div>

<!-- the same area after the switch to rich text -->
<div class="summary"><p>Three offices, <strong>one team</strong>.</p></div>
Wrap for the richest editor

Choose the element around a shorthand for the richest editor the area might get. A div can hold the paragraphs rich text produces; an h2 or a p cannot.

Go deeper

Was this page helpful?

Your answer goes to the docs team.