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 08 · Images end to end

From an image area to an image

How a template gets the image out of an image area, why an empty area gives a blank image rather than nothing, the test that tells them apart, and the setting behind grey placeholder boxes that some visitors see and others do not.

Who this is for. Developers writing a template that renders an image area. After this page you can get the image, test whether the area is empty with a test that works, and say why a grey box shows for some visitors and not for others.

The shorthand returns the image object

An image area holds a link to an image in the file manager. Read it with the area shorthand, {$component->heroImage} in Smarty or {{ component.heroImage }} in Twig. For an image area the shorthand returns the image object, not text, so you can keep it in a variable and call its methods. On a page, a post or any other versioned record it reads the active revision, the one visitors see: {$page->heroImage} is the shortest way to $page->getActiveRevision()->getAreaImage('heroImage').

Printed on its own, the object renders an image tag: {$page->heroImage} prints the same markup as getHtml() with no attributes. getAreaImage('name') is the explicit form and returns the same object.

Template code
{$hero = $page->heroImage}
{$photo = $component->getAreaImage('photo')}

{$hero->getResized(1200, 500, 'fit')->getHtml(['alt' => $hero->getName()|escape])}
{% set hero = page.heroImage %}
{% set photo = component.getAreaImage('photo') %}

{{ hero.getResized(1200, 500, 'fit').getHtml({'alt': hero.name|e}) }}

Use getAreaImage() when the area's name collides with a getter of the record or component: the getter wins, so an area called name, url, id, slug or fullPath is not reachable through the shorthand. Use getAreaImageOrPlaceholder(), below, when you want a placeholder for an empty area.

For an area that does not exist on the layout at all, the Smarty shorthand raises an error, Property heroImage does not exist in …, while the Twig shorthand prints nothing. How the area shorthand resolves has the full rule.

An empty area gives a blank image, not null

On the frontend, an image area with no image does not give you null. It gives a blank image: an object that answers every method a real image does, harmlessly.

MethodOn a blank image
getName(), getPath(), getFormat(), getMimeType()An empty string
getWidth(), getHeight()0
getAspectRatio()1.0
getResized(), toFormat(), toOptimizedFormat()The same blank image
getHtml()An empty string
isBlank()true

So a chain such as getResized(800, 450)->toOptimizedFormat()->getHtml() never fails on an empty area: each step returns the same blank image and the last prints nothing. The page renders, with empty space where the image should be, and any wrapper markup around it still prints unless you test first.

Testing whether the area is empty

Because the blank image is an object, the usual tests for a missing value do not work in Smarty:

  • {if $image} is always true: every object counts as true.
  • {if !empty($image)} is always true as well: PHP's empty() is never true for an object. A template that guards an icon this way prints its wrapper whether or not an icon was chosen.

Ask the object instead. isBlank() is true on a blank image and false on a real one. In Twig, the platform's is empty test treats a blank image as empty, so is not empty is the test there.

Template code
{$background = $component->backgroundImage}
{if !$background->isBlank()}
  {$background->getResized(1706, 960, 'fit')->getHtml()}
{/if}
{% set background = component.backgroundImage %}
{% if background is not empty %}
  {{ background.getResized(1706, 960, 'fit').getHtml() }}
{% endif %}

One case gives null after all: a versioned record with no active revision. A page rendered directly always has one, but a listing that reads images from other records can meet one. There, test both: {if $img && !$img->isBlank()} in Smarty. Twig's is not empty already covers null. Testing whether an area is empty gives the same rule for every area type.

Placeholders: getAreaImageOrPlaceholder()

getAreaImageOrPlaceholder('name', width, height) returns the area's image when it has one. When the area is empty it returns, depending on a site setting, either a blank image or a generated placeholder image of exactly that width and height, so that an author previewing an unfinished page sees where the image will go.

Template code
{$component->getAreaImageOrPlaceholder('image', 414, 304)->getResized(414, 304, 'fit')->getHtml(['loading' => 'lazy'])}
{{ component.getAreaImageOrPlaceholder('image', 414, 304).getResized(414, 304, 'fit').getHtml({'loading': 'lazy'}) }}
The width and height size only the placeholder

The dimensions passed to getAreaImageOrPlaceholder() are used only to generate the placeholder. They do nothing to a real image in the area: without getResized() after it, a real upload is served at its original size, however large. Chain getResized() with the same dimensions, as above.

Who sees a placeholder

The setting Show image placeholders (key Files.images.usePlaceholder) decides what an empty area gives:

ValueAn empty area gives
For logged in users (loggedIn, the default)A placeholder for anyone who is signed in; a blank image for everyone else
Always (always)A placeholder for every visitor
Never (never)A blank image for everyone

The default is meant to show authors where an image is missing, without showing it to the public. "Signed in" means any signed-in session: a backend user previewing the site, and equally a site member who has logged in to a member area. On a site with member accounts, every signed-in member therefore sees the grey boxes, which is the usual answer to "why is a grey placeholder showing on production". Set the setting to Never on such a site, or render those images with the shorthand or getAreaImage() and an isBlank() test.

Where placeholders come from

A placeholder is a real image file. The first time a template asks for a new size, the platform fetches a placeholder of that size from an external placeholder service, placehold.co, stores it in the file manager in the folder placeholders/ under a name such as 414x304.png, and reuses that file for every later request of the same size. Each distinct width and height pair adds one stored file, and the files are not removed automatically, so ask for a small, fixed set of sizes.

Go deeper

Was this page helpful?

Your answer goes to the docs team.