Skip to main content
Layout inheritance lets you define an HTML skeleton once in a layout file, then have individual page templates fill in named sections. The layout handles chrome (navigation, headers, footers); child pages supply only their own content.

How it works

When a child template is rendered:
  1. The child executes first — sections are captured into named buffers, the layout name is stored.
  2. Any output outside #section / #endsection in the child is discarded.
  3. The layout executes using the same environment — each #yield draws from the captured sections.
  4. If the layout itself extends another layout, the process repeats upward until a root layout is reached.
Templates execute top-down; layouts are assembled inside-out. The innermost child runs first, the outermost layout last.

Directives

DirectiveUsed inPurpose
#yield('name')LayoutOutputs the named section. Nothing is emitted if the child didn’t define it.
#yield('name', 'default')LayoutOutputs a fallback string when the section is absent.
#extends('layout')Child pageDeclares which layout to inherit. Must be the first directive in the file.
#section('name')#endsectionChild pageDefines the content for a named yield slot.
#parentInside #sectionInjects the parent layout’s own version of that section before your content.
#extends must be the first directive in the file. Any content that appears outside #section blocks in a child template is silently discarded.

Creating a layout

A layout is an ordinary .lex file that uses #yield to mark where child content goes.
views/layouts/base.lex
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>#yield('title', 'My App')</title>
  <link rel="stylesheet" href="/app.css">
  #yield('head')
  #stack('styles')
</head>
<body>

  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>

  <main>
    #yield('content')
  </main>

  <footer>
    <p>&copy; {{ date('Y') }} My App</p>
  </footer>

  #stack('scripts')
</body>
</html>
#yield('title', 'My App') outputs My App if no child defines a title section. #stack('styles') and #stack('scripts') are covered in Stacks.

Extending the layout in a child page

In the child template, call #extends first, then define your sections in any order.
views/pages/home.lex
#extends('layouts.base')

#section('title')
  Home — My App
#endsection

#section('content')
  <h1>Welcome, {{ $user->name }}!</h1>
  <p>You have {{ $notifications }} unread messages.</p>
#endsection

#push('scripts')
  <script src="/home.js"></script>
#endpush
Dot notation maps to directory paths: layouts.base resolves to views/layouts/base.lex.

Full working example

1

Create the layout file

Define your HTML skeleton with #yield slots at views/layouts/app.lex:
views/layouts/app.lex
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>#yield('title', 'My App')</title>
  #stack('styles')
</head>
<body>
  <main>
    #yield('content')
  </main>
  #stack('scripts')
</body>
</html>
2

Create the child page

Extend the layout and fill in the sections at views/pages/dashboard.lex:
views/pages/dashboard.lex
#extends('layouts.app')

#section('title')
  Dashboard — My App
#endsection

#section('content')
  <h1>Dashboard</h1>
  <p>Welcome back, {{ $user->name }}.</p>
#endsection

#push('scripts')
  <script src="/dashboard.js"></script>
#endpush
3

Render the page

Pass the template name to render() — Lex assembles the layout automatically:
echo $lexer->render('pages.dashboard', ['user' => $currentUser]);
The rendered HTML will be the full layout with the dashboard content inserted.

The #parent directive

Use #parent inside a #section block to prepend the parent layout’s own version of that section before your additions. This lets you extend a section rather than replace it entirely.
views/layouts/base.lex
<nav>
  #yield('nav')
</nav>

Multi-level inheritance

A layout can itself extend another layout. The rendering chain walks upward until it reaches a root layout with no #extends.
views/layouts/base.lex
<!DOCTYPE html>
<html>
<body>
  #yield('body')
  #stack('scripts')
</body>
</html>
views/layouts/app.lex
#extends('layouts.base')

#section('body')
  <nav>...</nav>
  <main>#yield('content')</main>
#endsection
views/pages/about.lex
#extends('layouts.app')

#section('content')
  <h1>About</h1>
#endsection
The rendering chain for the above is:
views/pages/about.lex → views/layouts/app.lex → views/layouts/base.lex
If two layouts extend each other in a cycle (A → B → A), Lex detects the loop and throws TemplateRuntimeException immediately — no infinite recursion.

Default section content

Pass a second argument to #yield to display fallback text when no child defines the section:
#yield('sidebar', '<p>No sidebar configured.</p>')
The fallback value is output as a PHP string literal — it is not processed as a Lex template.

Build docs developers (and LLMs) love