Layouts let you define a single base template — the surrounding HTML structure, navigation, footer, asset links — and then plug page-specific content into named sections. The layout decides where each section appears using @yield; the page decides what each section contains using @section.
Webrium View's layout system is built around three pieces:
| Directive | Where it goes | Purpose |
|---|---|---|
@section('name') ... @endsection |
Child view | Declares a named block of content |
@yield('name', $default = '') |
Layout | Outputs the content of a named section |
Engine::renderLayout() |
PHP code | Renders a child view inside a layout |
A layout file:
{{-- views/layouts/main.php --}}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>@{{ $title }}</title>
</head>
<body>
<header>
<h1>My Site</h1>
</header>
<main>
@yield('content')
</main>
<footer>
@yield('footer', '© ' . date('Y'))
</footer>
</body>
</html>A child view that fills in the content section:
{{-- views/pages/home.php --}}
@section('content')
<h2>Welcome, @{{ $userName }}</h2>
<p>This is the home page.</p>
@endsectionRendering them together:
echo Engine::renderLayout(
'layouts/main',
'pages/home',
[
'title' => 'Home',
'userName' => 'Reza',
]
);The data array is passed to both the layout and the child view, so $title (used in the layout) and $userName (used in the child) are both available in their respective files.
Behind the scenes, Engine::renderLayout($layout, $view, $data):
- Clears any leftover section state.
- Renders the child view first. As it runs, every
@section ... @endsectionblock captures its contents into a named buffer instead of writing to the output. - If the child view didn't explicitly declare a
contentsection, its entire rendered output is used as thecontentsection automatically. - Renders the layout. Each
@yield('name')outputs the matching section. - Clears the section state again, so it doesn't leak into other renders.
The implicit content rule in step 3 is convenient for simple pages — you can write the child view with no @section directives at all, and its full output becomes the body:
{{-- views/pages/about.php — no @section needed --}}
<h2>About</h2>
<p>We make things.</p>echo Engine::renderLayout('layouts/main', 'pages/about', [
'title' => 'About',
]);If a section isn't defined by the child view, @yield outputs its second argument as a fallback:
<footer>
@yield('footer', '© ' . date('Y') . ' My Site')
</footer>The default can be any string — pass '' (or just omit it) when you want the section to silently disappear if it isn't filled in.
A child view can declare as many sections as it needs:
{{-- views/pages/article.php --}}
@section('title') @{{ $article->title }} @endsection
@section('content')
<article>
<h1>@{{ $article->title }}</h1>
@raw($article->body)
</article>
@endsection
@section('sidebar')
<aside>
<h3>Related</h3>
<ul>
<li w-for="$related as $r">
<a href="/articles/@{{ $r->slug }}">@{{ $r->title }}</a>
</li>
</ul>
</aside>
@endsection{{-- layouts/main.php --}}
<title>@yield('title', 'My Site')</title>
…
<div class="grid">
<main>@yield('content')</main>
<div class="sidebar">@yield('sidebar')</div>
</div>For convenience, the same call can be made with the global helper function:
echo layout('layouts/main', 'pages/home', [
'title' => 'Home',
'userName' => 'Reza',
]);layout() is a thin wrapper around Engine::renderLayout() — exactly equivalent, just shorter. It's loaded automatically with the webrium/view package.
For advanced cases — composing layouts dynamically, mixing programmatic rendering with templates — the Webrium\View\View class exposes the underlying section primitives:
use Webrium\View\View;
View::startSection('title');
echo 'Custom title';
View::endSection();
echo View::yieldSection('title', 'Default');
View::clearSections();| Method | Description |
|---|---|
View::startSection(string $name) |
Begin capturing output into a named buffer |
View::endSection() |
Close the current section and store its contents |
View::yieldSection(string $name, string $default = '') |
Return the captured content, or the default if the section is empty |
View::clearSections() |
Reset all sections and the open-section stack |
View::make(string $view, array $data = []) |
Alias for Engine::render() |
View::renderWithLayout(string $view, string $layout, array $data = []) |
The underlying implementation of renderLayout() (note the parameter order is view, layout — opposite of renderLayout) |
You'll rarely need any of these directly; they exist so that @section / @endsection / @yield have something to compile down to.
A few situations raise ViewException:
- Ending a section when none is open (
@endsectionwithout a matching@section) - Using an empty section name (
@section('')) - Calling
View::startSection()orView::yieldSection()with whitespace-only names
These usually indicate a typo or a missing @endsection rather than a runtime problem.
Webrium View does not have a built-in "extends an extends" mechanism — each render either uses a single layout or no layout at all. If you need shared structure across multiple layouts (e.g. an app layout and an admin layout that share a common shell), the simplest pattern is to extract the shared parts into components and include them from each layout:
{{-- layouts/admin.php --}}
@component('partials/head', ['title' => $title])
<body class="admin">
@component('partials/admin-nav')
<main>@yield('content')</main>
</body>
</html>See Components for the full story.