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.