Layouts & Slots

For the common "wrap this page in a site chrome" pattern, Fast gives you a tiny layout helper set: layout(), section() / end_section(), and slot(). A page declares the layout it wants; its printed body becomes the layout's default content slot; the layout pulls named sections with slot(). The layout also inherits the page's variables, so you rarely re-pass anything.

<!-- views/pages/home.php -->
<?php layout('layouts.app'); ?>
<?php section('title', 'Home'); ?>

<h1>Welcome, <?= e($user->name) ?></h1>   <!-- this body becomes the 'content' slot -->
<!-- views/layouts/app.php -->
<!doctype html>
<title><?= slot('title') ?></title>
<body>
	<?= slot() ?>                          <!-- the child's body -->
	<footer><?= e($user->name) ?></footer> <!-- $user inherited from the child -->
</body>
get('/', fn (): string => render('pages.home', ['user' => $user]));

section() has two forms: section('title', 'Home') sets a section directly, while a bare section('nav')end_section() captures everything printed between them. Layouts can themselves declare a layout(), so you can nest chrome (an admin layout inside the base layout, for example).

slot() takes an optional default for when a section was never set:

<title><?= slot('title', 'Untitled') ?></title>

Two things to keep in mind:

  • slot() returns already-rendered HTML and is emitted unescaped. Never wrap it

in e(): escape values inside the child or section, where the raw data lives.

  • An explicit section('content', ...) wins over the printed body. If a template

both sets a content section and prints markup, the printed body is discarded; pick one.

Layouts cover the "one value per slot" case. When many partials each need to contribute to the same spot, think scripts or stylesheets, you want a stack, which we cover next in Stacks.