Template Inheritence
Most web applications have a common structure shared by many pages, such as a navigation bar, a header, a footer, and a main content area.
Repeating this structure in every template makes your code harder to maintain. If you need to change the navigation, for example, you would have to update every page that contains it.
PyBlade lets you define this shared structure once as a base layout. You can define a shared layout once and let your other templates extend it, keeping the common structure in one place while each page provides its own content.
This is called template inheritance.
Defining a Layout
A layout is a reusable template that provides the common structure shared by multiple pages. Other templates can then inherit this layout and provide the content specific to each page.
Let's start with a simple base layout:
<html>
<head>
<title>My App - {{ title }}</title>
</head>
<body>
<header>
<h1>Welcome to My App</h1>
</header>
<aside>
...
</aside>
<main>
@block("content")
<p>Nothing to display yet.</p>
@endblock
</main>
<footer>
© @now('%Y') - My App
</footer>
</body>
</html>The important part here is the @block directive. A block defines a named area of the layout that child templates can replace.
In this example, content has some default content. If a child template does not provide its own content block, this default content will be displayed.
You can define as many blocks as your layout needs:
@block("header")
<h1>Welcome to My App</h1>
@endblock
@block("content")
<p>Nothing to display yet.</p>
@endblockWe'll see how child templates override these blocks in the next section.
You'll also notice the title variable in the <title> element. Rather than making the page title a block, we'll use a named slot so that each child template can provide its own title.
Extending a Layout
Once you have a layout, a child template can inherit it using the @extends directive:
@extends("layouts.base")The value passed to @extends uses dot notation to identify the layout file. So layouts.base refers to templates/layouts/base.html.
PyBlade also supports regular file paths, meaning you could write @extends("layouts/base.html") instead.
After extending a layout, the child template can provide content for the blocks defined by the parent. Child templates use the @block directive to override a block:
@extends("layouts.base")
<pb-slot name="title">Home</pb-slot>
@block("content")
<p>Welcome to the home page!</p>
@endblockWhen PyBlade renders this template, the content of @block("content") replaces the corresponding @block("content") in the parent layout.
If the parent defined default content for that block, the child's block replaces it completely.
Adding to existing content
Sometimes you don't want to completely replace the content provided by the parent. You may want to keep it and add something of your own.
For this, PyBlade provides the @parent directive:
@block("sidebar")
@parent
<p>This content is added to the sidebar.</p>
@endblock@parent keeps the parent's content and places the child's content after it.
For example, if the parent contains:
<aside>
@block("sidebar")
<p>Default sidebar content.</p>
@endblock
</aside>the child can keep that content and add more:
@block("sidebar")
@parent
<p>This content comes from the child template.</p>
@endblockThe default slot
Not all content in a child template needs to be inside a @block directive.
As we saw in Components - Slots, PyBlade uses slot as the default, unnamed slot. Template inheritance follows the same behavior: any content in the child template that is not inside a @block is automatically collected into the default slot.
For example:
@extends("layouts.base")
<h1>Welcome to my page!</h1>
<p>Content on this page is not inside a block.</p>The parent layout can render this content using:
<main>
{{ slot }}
</main>This provides a convenient way to pass the main content of a child template to its layout without creating a named block for it.
The slot variable is not escaped by default because it is intended to contain rendered HTML. Only use slots with trusted or properly sanitized content. Passing untrusted user input directly into a slot can introduce cross-site scripting (XSS) vulnerabilities.
Named slots
Named slots work the same way in template inheritance as they do in components.
When a child template needs to provide a specific piece of content to the layout, you can define a named slot using <pb-slot>:
<pb-slot name="title">Home</pb-slot>The parent layout can then access the slot using its name:
<title>My App - {{ title }}</title>You can define as many named slots as your layout requires. The same <pb-slot> syntax and slot behavior used with components also apply here.