Images
Resizing, cropping, presets, srcset and base64 placeholders for image fields.
Declaring an image field
Declare a field of type image, then list it by name in the view:
// site/fields/hero/index.json
{ "label": "Hero", "type": "image", "options": {} }
// site/views/post/index.json
{ "fields": { "hero": {} } }
image decodes to a PageImage, a PageFile with transform and encoding helpers on top. SVGs are excluded even in an image field because the resize pipeline can't rasterize them; they decode to null. Use a file field for SVG.
Basic access
{$page->hero} original file's public URL
{$page->hero('w=400,h=300,crop')} transformed variant URL, shorthand for src()
Bare {$page->hero} calls url(), which exposes the original on first use and returns a cache-busted URL.
Transform options
src() takes a comma-separated option string, the same DSL used across PhlatPage:
| Option | Effect |
|---|---|
w=400 |
Max width in pixels |
h=300 |
Max height in pixels |
fit=crop |
Crop to exactly fill w x h. Default contain scales to fit, no crop |
grayscale |
Desaturate |
invert |
Invert colors |
blur=5 |
Gaussian blur radius |
brightness=10 |
Brightness adjustment |
contrast=20 |
Contrast adjustment |
gamma=1.5 |
Gamma correction |
pixelate=8 |
Pixelate block size |
sharpen=5 |
Sharpen amount |
posterize=16 |
Reduce to at most this many colors |
quality=60 |
JPEG quality (1-100), overriding the config default |
rotate=90 |
Rotate clockwise by this many degrees |
flip=v |
Mirror the image, h (default) or v |
orient |
Auto-rotate upright from EXIF data, before any resize |
trim=10 |
Remove border areas within this color-similarity tolerance |
colorize=20:0:0 |
Adjust red:green:blue channel intensity |
format=webp |
Encode as png, webp, etc. instead of JPEG |
{$page->hero->src('w=800,h=400,fit=crop')}
{$page->hero->src('w=600,grayscale,blur=3')}
{$page->hero->src('orient,w=600')}
{$page->hero->src('rotate=90,flip=v')}
Each unique option string is its own cached variant.
Presets
Presets declared in config.json give a transform a name:
{
"images": {
"presets": {
"thumbnail": "w=150,h=150,crop",
"card": "w=400,h=300,crop",
"hero": "w=1600,h=600,crop",
"avatar": "w=80,h=80,crop,grayscale"
}
}
}
A bare name with no = is a preset. Trailing key=value pairs override it, and a preset may reference other presets:
{$page->hero->src('card')}
{$page->hero->src('card,grayscale,quality=60')}
srcset()
Builds a srcset value from preset names (the width descriptor comes from the preset's w=) or from explicit option-to-descriptor pairs:
<img
src="{$page->hero->src('card')}"
srcset="{$page->hero->srcset(['thumbnail', 'card', 'hero'])}"
>
<img srcset="{$page->hero->srcset(['w=400' => '400w', 'w=800' => '800w'])}">
base64()
Encodes a variant as a data: URI, for blur-up placeholders inlined in the first response. Unlike src(), it generates the variant immediately.
<img
src="{$page->hero->base64('w=20,h=20,blur=5')}"
data-src="{$page->hero->src('w=800')}"
loading="lazy"
>
placeholder()
A global template function, not a field method. It builds a static SVG data URI with no source file, for a src fallback or a fixed-ratio box before any upload:
<img src="{placeholder(400, 300, '#e5e5e5')}" alt="">
Dimensions
width(), height(), ratio(), isLandscape(), isPortrait() and isSquare() read the original file. They are on the decoded PageImage, so call decode() first:
{var $hero = $page->hero->decode()}
{if $hero}
<div class="{if $hero->isPortrait()}max-w-sm{else}max-w-2xl{/if}">
<img src="{$hero->src('w=800')}" width="{$hero->width()}" height="{$hero->height()}">
</div>
{/if}
How variants are generated
src() and transform() only register a variant and return its URL. The variant is rendered when that URL is first requested (Page::route() checks the registry for a pending variant before running the page's router), and the web server serves the file statically after that.
If w/h don't shrink the source and no effects (including quality=) are requested, the original is mirrored as-is. Otherwise the variant is re-encoded as JPEG, unless format= asks for another, at quality= or the config default.
Config
{
"images": {
"driver": "gd",
"quality": 80
}
}
driver is the Intervention Image backend, gd or imagick (default imagick). quality is the JPEG quality (1-100) for re-encoded variants.
Example
Responsive card image with a blurred inline placeholder while the real variant loads
<figure class="relative aspect-[4/3] overflow-hidden rounded-box">
<img
src="{$page->hero->base64('w=24,h=18,blur=4')}"
data-src="{$page->hero->src('card')}"
srcset="{$page->hero->srcset(['card', 'hero'])}"
sizes="(min-width: 768px) 50vw, 100vw"
class="w-full h-full object-cover"
loading="lazy"
alt="{$page->title}"
>
</figure>