Layouts
Layouts define the structural wrapper for your page content. They control the header, footer, and other zones that surround the main content area.
What is a Layout?
A layout is a Blade template that defines the overall structure of your pages. It includes:
- Header — Site navigation, logo, and other header elements
- Content — The main page content area (
@yield('content')) - Footer — Site footer, copyright, and other footer elements
Layout files live in resources/views/layouts/ and are resolved by name (e.g. layouts.page for the page type).
Creating a Layout
Basic Layout
Create a layout Blade file:
{{-- resources/views/layouts/page.blade.php --}}
<html @editor('dark') lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $meta_title ?? ($title ?? '') . ' | ' . config('app.name') }}</title>
<meta name="description" content="{{ $meta_description ?? '' }}">
</head>
<body class="page-layout">
@sections('header')
<main class="min-h-screen">
@yield('content')
</main>
@sections('footer')
</body>
</html>The @sections Directive
@sections is a custom page builder directive (not standard Blade @section). It is self-closing.
@sections('header')
@sections('footer')
@sections('announcement')It renders a layout section from the page JSON data. The content comes from the layout.header or layout.footer zones defined in the page JSON — not from inline Blade.
The directive searches the header zone first, then the footer zone, matching by key. For example, @sections('header') renders whatever section is defined under layout.header.sections.header in the page JSON.
Available Variables
Layout views receive these variables automatically:
| Variable | Description |
|---|---|
$title | Page title from the database |
$meta_title | SEO meta title |
$meta_description | SEO meta description |
$meta_keywords | SEO meta keywords |
$page | Page Eloquent model (shared globally via View::share) |
Per-Page Layouts
Pages can define custom layout sections in their JSON. The layout key controls which Blade layout file to use and what sections to render in the header/footer zones:
{
"layout": {
"type": "page",
"header": {
"sections": {
"header": {
"type": "site-header",
"settings": { "sticky": true },
"blocks": {},
"order": [],
"disabled": false
}
}
},
"footer": {
"sections": {
"footer": {
"type": "site-footer",
"settings": {},
"blocks": {},
"order": [],
"disabled": false
}
}
}
},
"sections": {
"main": {
"type": "page-content",
"settings": {},
"blocks": {},
"order": [],
"disabled": false
}
},
"order": ["main"]
}When the editor saves this data, @sections('header') in your layout Blade file renders the site-header section, and @sections('footer') renders the site-footer section.
Custom Blade Page Layouts
Custom Blade pages (pages/{slug}.blade.php) can also override layout sections using the @layout directive. This allows you to tweak header/footer settings without a full page JSON.
Syntax
@extends('layouts.page')
@layout([
'header' => [
'sections' => [
'header' => [
'settings' => ['sticky' => false],
],
],
],
])
@section('content')
<main>
<p>Custom blade page body</p>
</main>
@endsectionThe @layout directive accepts a partial layout array — the same structure used in page JSON's layout object. Only the keys you specify are overridden; everything else inherits from the default/shared layout.
Partial Config Examples
Override a single setting:
@layout([
'header' => [
'sections' => [
'header' => [
'settings' => ['sticky' => false],
],
],
],
])Add a new section:
@layout([
'header' => [
'sections' => [
'announcement' => [
'type' => 'announcement',
'settings' => ['text' => 'Sale!'],
'blocks' => [],
'order' => [],
],
],
'order' => ['announcement', 'header'],
],
])Override both header and footer:
@layout([
'header' => [
'sections' => [
'header' => [
'settings' => ['logo' => '/custom-logo.png'],
],
],
],
'footer' => [
'sections' => [
'footer' => [
'settings' => ['tagline' => 'Custom tagline'],
],
],
],
])How It Works
@layout([...])stores the partial config as pending overrides- The next
@sections('key')call in the layout view applies the overrides to$__pb_layoutviaPageData::mergeLayout() - The merged layout is used for rendering
The overrides use the same 3-layer merge as page JSON — they are applied as the highest-priority layer on top of the default and shared layout configs.
WARNING
@layout must be placed after a blank line following @extends. Blade's compiler requires @extends to be the first statement in the view. Placing @layout immediately after @extends without a blank line causes @extends to be silently dropped, resulting in an empty rendered output.
{{-- CORRECT: blank line between @extends and @layout --}}
@extends('layouts.page')
@layout([...])
@section('content')
...
@endsection{{-- WRONG: no blank line — @extends will be dropped --}}
@extends('layouts.page')
@layout([...])
@section('content')
...
@endsectionLayout Zones
| Zone | Description | How to render |
|---|---|---|
header | Site header area | @sections('header') |
footer | Site footer area | @sections('footer') |
content | Main page content | @yield('content') |
The header and footer zones are rendered by @sections. The content zone is standard Blade — defined with @section('content') in pagebuilder::page and yielded with @yield('content') in your layout.
Layout Types
The layout.type field in the page JSON maps to a Blade view name: layouts.{type}.
Page Layout
The default layout type:
{
"layout": {
"type": "page"
}
}This loads resources/views/layouts/page.blade.php.
Custom Layout Types
Define custom layout types by creating additional Blade files:
{{-- resources/views/layouts/simple.blade.php --}}
<html @editor('dark') lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<title>{{ $meta_title ?? ($title ?? '') . ' | ' . config('app.name') }}</title>
</head>
<body class="simple-layout">
@sections('announcement')
@sections('header')
<main class="min-h-screen">
@yield('content')
</main>
@sections('footer')
</body>
</html>{
"layout": {
"type": "simple"
}
}No Layout
Set "layout": false to render without header/footer zones:
{
"layout": false,
"sections": {
"main": { "type": "hero" }
},
"order": ["main"]
}Shared Layout Settings
Layout configurations can be shared across multiple pages instead of being duplicated in each page's JSON. This is managed by the LayoutSettings service.
How It Works
Layout configs are stored in settings.json (configured via theme_settings_path) under the _pagebuilder.layouts.{type} key:
{
"pagebuilder": {
"colors.primary": "hsl(168 94% 7%)"
},
"_pagebuilder": {
"layouts": {
"page": {
"header": {
"sections": {
"header": {
"type": "site-header",
"settings": { "sticky": true },
"blocks": {},
"order": []
}
},
"order": ["header"]
},
"footer": {
"sections": {
"footer": {
"type": "site-footer",
"settings": { "copyright": "2026" },
"blocks": {},
"order": []
}
},
"order": ["footer"]
}
}
}
}
}Page JSON Layout Formats
The layout field in page JSON supports two formats:
String Format (Shared Layout)
{
"layout": "page"
}When layout is a string, the page inherits the shared layout from LayoutSettings. The editor displays the full layout config from _pagebuilder.layouts.page.
Object Format (Page-Specific Override)
{
"layout": {
"type": "page",
"header": {
"sections": {
"header": {
"type": "site-header",
"settings": { "logo": "/custom.png" }
}
},
"order": ["header"]
},
"footer": {
"sections": {},
"order": []
}
}
}When layout is an object, the page has a page-specific override. The full layout config is stored in the page JSON.
Missing Layout
If the layout key is missing, it defaults to the shared "page" layout.
Layout Resolution Priority
Layout data is merged from three layers (lowest to highest priority):
- Default — Schema defaults from
LayoutParser(from@schema()in layout Blade files) - Shared — Layout config from
LayoutSettings(_pagebuilder.layouts.{type}) - Page-specific — Layout object from page JSON (if
layoutis an object)
Example: If the shared layout sets header.settings.logo = "/default.png" and a page's layout sets header.settings.logo = "/custom.png", the page will use /custom.png.
How Layout Saving Works
When the editor saves a page, PageStorage::save() determines where to store the layout based on the existing page.json:
| Existing page.json layout | Save behavior |
|---|---|
| Not exists | Save layout to LayoutSettings, store "page" string in page JSON |
String ("page") | Save layout to LayoutSettings, store string in page JSON |
Object ({...}) | Save full layout object to page JSON, strip source key |
This means:
- New pages automatically get shared layouts
- Existing pages with string layouts continue to use shared layouts
- Existing pages with object layouts keep their page-specific overrides
LayoutSettings Service
The LayoutSettings service manages shared layout configurations:
use PageBuilder\Services\LayoutSettings;
$layoutSettings = app(LayoutSettings::class);
// Get all layout configs
$all = $layoutSettings->all();
// Get a specific layout as raw array
$pageLayout = $layoutSettings->get('page');
// Get a specific layout as a type-safe LayoutConfig DTO
$layoutConfig = $layoutSettings->getConfig('page');
// Save a layout config
$layoutSettings->save('page', [
'header' => [
'sections' => [
'header' => [
'type' => 'site-header',
'settings' => ['sticky' => true],
'blocks' => [],
'order' => [],
],
],
'order' => ['header'],
],
'footer' => [
'sections' => [],
'order' => [],
],
]);
// Delete a layout config
$layoutSettings->delete('page');
// Flush cache (after saving)
$layoutSettings->flush();Layout Editor Source
When the editor loads a page, the API response includes a source field in the layout object:
{
"layout": {
"type": "page",
"source": "shared",
"header": { ... },
"footer": { ... }
}
}| Source | Meaning |
|---|---|
"shared" | Layout is inherited from LayoutSettings |
"page" | Layout is a page-specific override |
This field is editor metadata — it is stripped on save and never persisted to disk.
Layout Resolution
The system resolves layouts in this order:
- Page JSON —
layout.typein the page data selects the Blade view - Theme Layout — Layout from the active theme's
views/layouts/directory - App Layout — Layout from
resources/views/layouts/ - Default — Package default layout
Tips
- Keep layouts simple — Don't add complex logic to layouts
- Use
@sections— Self-closing directive for header/footer zones, content comes from page JSON - Responsive design — Make layouts responsive by default
- SEO basics — Include
$meta_title,$meta_descriptionin the<head> - Editor support — Always include
@editoron the<html>tag - Use shared layouts — Store common header/footer configs in
LayoutSettingsto avoid duplicating across pages