Layouts, sections and partials
The pages of a site share a frame: header, menu, footer. To avoid repeating it in every view, the frame is described once in a layout. A page declares which layout it uses and fills its sections — named places for content.
The layout
Create the file app/Views/layouts/main.php by hand:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= esc($title ?? 'Blog') ?></title>
</head>
<body>
<main>
<?= $this->renderSection('content') ?>
</main>
</body>
</html>The renderSection('content') method marks the place where a page inserts its content. The section name can be anything.
A page based on the layout
Now the post list view contains only its own part:
<!-- app/Views/posts/index.php -->
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<h1>Posts</h1>
<p>The list of posts will be here.</p>
<?= $this->endSection() ?>extend() names the layout, and everything between section('content') and endSection() lands in place of renderSection('content'). The controller does not change: it still calls view('posts/index'). Open /posts and look at the page source — the heading is inside the main tag.
Several sections
A layout may contain several places. Add the line <?= $this->renderSection('scripts') ?> before the closing body tag, and a page can fill a second section:
<!-- app/Views/posts/index.php -->
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<h1>Posts</h1>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script src="<?= base_url('js/posts.js') ?>"></script>
<?= $this->endSection() ?>A section the page did not fill is rendered empty by the layout.
Partials
A repeating fragment — a menu, a footer — is moved to a separate file. Create app/Views/partials/nav.php:
<!-- app/Views/partials/nav.php -->
<nav>
<a href="/">Home</a>
<a href="/posts">Posts</a>
</nav>In the layout the include() method inserts it — add the line right after the opening body tag:
<body>
<?= $this->include('partials/nav') ?>The menu appears on every page that uses the layout.
A partial with its own data
To pass separate data to a fragment, for example one post inside a loop, call the view() function:
<div class="cards">
<?php foreach ($posts as $post): ?>
<?= view('posts/_card', ['post' => $post]) ?>
<?php endforeach ?>
</div>The file app/Views/posts/_card.php receives the $post variable. A leading underscore in the name is the accepted way to tell partials from whole pages.
Common mistake
In a view that extends a layout only the content inside sections is output. Markup written after extend() outside section() and endSection() never reaches the page. If a part of the page has disappeared, check that it sits inside a section and that the section name matches the name in renderSection().