Template stacks
A block snippet knows best which styles and scripts it needs, but the <head> of your page has already been written when the block is rendered. Stacks solve this: push from anywhere, render anywhere, and Kirby puts everything in the right place at the end.
Push to a stack
Wrap any output in push() and endpush() to add it to a stack. The output doesn't show up where you push it, but wherever the stack is rendered:
<?php push('head') ?>
<link rel="stylesheet" href="<?= url('assets/css/gallery.css') ?>">
<?php endpush() ?>
<figure class="gallery">
<!-- … -->
</figure>
For short content, pass it as second argument instead:
<?php push('head', '<meta name="robots" content="noindex">') ?>
Render a stack
Call stack() with the name of the stack where its content should appear:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= $page->title()->esc() ?></title>
<?= css('assets/css/index.css') ?>
<?php stack('head') ?>
</head>
<body>
Order doesn't matter
In the example above, the header snippet renders the head stack before any block has been rendered. That's fine. While Kirby renders your template, stack() only leaves a placeholder. Once the whole template is rendered, Kirby replaces each placeholder with everything that has been pushed to that stack, no matter whether it was pushed before or after.
This means you can push from any template or snippet, including snippets that are rendered inside other snippets, like blocks or layouts.
Push only once
Blocks often appear more than once on a page. Without precautions, three gallery blocks would add the same stylesheet three times. Pass unique: true to add identical content only once:
<?php push('head', css('assets/css/gallery.css'), unique: true) ?>
This also works with output buffering:
<?php push('footer', unique: true) ?>
<script src="<?= url('assets/js/gallery.js') ?>" defer></script>
<?php endpush() ?>
Kirby compares the exact content, so make sure that every push of the same asset produces exactly the same output.
Options for stack()
stack(
string $name,
string $glue = PHP_EOL,
bool $return = false,
bool $clear = true
): string|null
| Option | Default | Description |
|---|---|---|
$glue |
PHP_EOL |
String between the pushed entries |
$return |
false |
Returns the result instead of echoing it |
$clear |
true |
Empties the stack after rendering it |
// join the entries without line breaks
<?php stack('head', glue: '') ?>
// return instead of echo
<?php $head = stack('head', return: true) ?>
By default, a stack is empty after it has been rendered. To render the same stack in more than one place, pass clear: false to every call but the last:
<?php stack('notices', clear: false) ?>
<!-- … -->
<?php stack('notices') ?>
Example: block assets
Here's how a site with a blocks field can load the CSS and JS of each block type only on pages that actually use it.
The header and footer render the stacks:
<head>
<meta charset="utf-8">
<title><?= $page->title()->esc() ?></title>
<?= css('assets/css/index.css') ?>
<?php stack('styles') ?>
</head>
<body>
<?php stack('scripts') ?>
</body>
</html>
Each block snippet pushes what it needs:
<?php push('styles', css('assets/css/blocks/slider.css'), unique: true) ?>
<?php push('scripts', js('assets/js/blocks/slider.js', ['defer' => true]), unique: true) ?>
<div class="slider">
<?php foreach ($block->images()->toFiles() as $image): ?>
<img src="<?= $image->url() ?>" alt="<?= $image->alt()->esc() ?>">
<?php endforeach ?>
</div>
The template doesn't need to know about any of it:
<?php snippet('header') ?>
<main>
<?= $page->text()->toBlocks() ?>
</main>
<?php snippet('footer') ?>
A page with three sliders gets slider.css and slider.js exactly once. A page without a slider doesn't load them at all.