PHPackages                             coderstm/laravel-page-builder - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. coderstm/laravel-page-builder

ActiveLibrary[Templating &amp; Views](/categories/templating)

coderstm/laravel-page-builder
=============================

A section-based page builder for Laravel using JSON layouts, sections, blocks, and themes.

v1.1.8(1mo ago)263↑185.7%1MITTypeScriptPHP ^8.2CI passing

Since Mar 14Pushed 1mo agoCompare

[ Source](https://github.com/coders-tm/laravel-page-builder)[ Packagist](https://packagist.org/packages/coderstm/laravel-page-builder)[ Docs](https://github.com/coderstm/laravel-page-builder)[ RSS](/packages/coderstm-laravel-page-builder/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (13)Versions (24)Used By (1)

Laravel Page Builder
====================

[](#laravel-page-builder)

[![Build Status](https://github.com/coders-tm/laravel-page-builder/workflows/tests/badge.svg)](https://github.com/coders-tm/laravel-page-builder/actions)[![Total Downloads](https://camo.githubusercontent.com/cd45a1ab8b7c81e72219bea056e6ef44de0922c7795bba508db37c7e4f9574a6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f636f64657273746d2f6c61726176656c2d706167652d6275696c646572)](https://packagist.org/packages/coderstm/laravel-page-builder)[![Latest Stable Version](https://camo.githubusercontent.com/0535278bdbbf876574110406c7d6022ed351e1968b90192d57ed871a426c4d27/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636f64657273746d2f6c61726176656c2d706167652d6275696c646572)](https://packagist.org/packages/coderstm/laravel-page-builder)[![License](https://camo.githubusercontent.com/a1e1bf1cccc59c63178383c9bf3121a99128d2e5182baf66ecc5baca87580013/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f636f64657273746d2f6c61726176656c2d706167652d6275696c646572)](https://packagist.org/packages/coderstm/laravel-page-builder)

 [![Intro](art/intro.gif)](art/intro.gif)

A modern page builder for Laravel that allows you to build dynamic pages using layouts, sections and JSON rendering. It includes a visual editor, layout system, reusable sections and multi-theme support.

Features
--------

[](#features)

- **Blade-native rendering** — sections and blocks are regular Blade views with typed PHP objects
- **`@schema()` directive** — declare settings, child blocks, and presets directly in Blade templates
- **Visual editor** — React SPA with iframe live preview, drag-and-drop, and inline text editing
- **JSON-based storage** — page data stored as JSON files on disk for fast reads and easy version control
- **JSON templates** — fallback layouts for pages without a per-page JSON; supports `wrapper`, variable interpolation (`{{ $page->title }}`), and theme overrides
- **Per-page Layouts** — site header and footer are configurable per-page, stored in the page JSON
- **Recursive block nesting** — container blocks (rows, columns) can hold child blocks to any depth
- **Theme blocks** — register global block types that any section can accept via `@theme` wildcard
- **21+ Field Types** — from basic text inputs to advanced color pickers, icon selectors, and custom types
- **Page Meta Persistence** — SEO titles and descriptions are automatically managed and persisted across dynamic and preserved pages
- **Editor mode** — `data-editor-*` attributes injected only when the editor is active
- **Publishable assets** — config, views, migrations, and frontend assets can be published independently

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11.x or 12.x

Installation
------------

[](#installation)

```
composer require coderstm/laravel-page-builder
```

The package auto-registers its service provider via Laravel's package discovery.

### Run the install command

[](#run-the-install-command)

```
php artisan pagebuilder:install
```

This single command:

1. Publishes `config/pagebuilder.php`
2. Publishes database migrations
3. Publishes the compiled editor frontend assets to `public/pagebuilder/`
4. Scaffolds default starter views into your app:
    - `resources/views/layouts/page.blade.php` — base HTML layout
    - `resources/views/sections/` — announcement, header, hero, rich-text, content, footer
    - `resources/views/blocks/` — row, column, text

**Options**

FlagDescription`--force`Overwrite files that already exist`--migrate`Run `php artisan migrate` immediately after publishing```
# Overwrite existing files and run migrations in one step
php artisan pagebuilder:install --force --migrate
```

### Run migrations (if not using `--migrate`)

[](#run-migrations-if-not-using---migrate)

```
php artisan migrate
```

### Configuration reference

[](#configuration-reference)

`config/pagebuilder.php` is published to your application's `config/` directory:

```
return [
    // Path to page JSON data files
    'pages' => resource_path('views/pages'),

    // Path to section Blade templates
    'sections' => resource_path('views/sections'),

    // Path to theme block Blade templates
    'blocks' => resource_path('views/blocks'),

    // Path to JSON template files (fallback layouts for pages without a page JSON)
    'templates' => resource_path('views/templates'),

    // Middleware applied to editor routes
    'middleware' => ['web'],

    // Filesystem disk for asset uploads
    'disk' => 'public',

    // Directory within the disk for uploaded assets
    'asset_directory' => 'pagebuilder',

    // Reserved slugs that cannot be used for dynamic pages
    'preserved_pages' => ['home'],
];
```

### Publish resources individually

[](#publish-resources-individually)

If you need to re-publish a specific resource:

```
# Config
php artisan vendor:publish --tag=pagebuilder-config

# Database migrations
php artisan vendor:publish --tag=pagebuilder-migrations

# Editor frontend assets (React SPA)
php artisan vendor:publish --tag=pagebuilder-assets

# Built-in package views
php artisan vendor:publish --tag=pagebuilder-views
```

---

Creating Sections
-----------------

[](#creating-sections)

Sections are the top-level building blocks of a page. Each section is a Blade view that declares its schema using the `@schema()` directive.

### 1. Create the Blade file

[](#1-create-the-blade-file)

Place section templates in the configured sections directory (default: `resources/views/sections/`).

```
{{-- resources/views/sections/hero.blade.php --}}
@schema([
    'name' => 'Hero',
    'settings' => [
        ['id' => 'title',    'type' => 'text',  'label' => 'Title',    'default' => 'Welcome'],
        ['id' => 'subtitle', 'type' => 'text',  'label' => 'Subtitle', 'default' => ''],
        ['id' => 'bg_color', 'type' => 'color', 'label' => 'Background Color', 'default' => '#ffffff'],
    ],
    'blocks' => [
        ['type' => 'row'],
        ['type' => '@theme'],
    ],
    'presets' => [
        ['name' => 'Hero'],
        ['name' => 'Hero with Row', 'blocks' => [
            ['type' => 'row', 'settings' => ['columns' => '2']],
        ]],
    ],
])

editorAttributes() !!}
    style="background-color: {{ $section->settings->bg_color }}">

        {{ $section->settings->title }}
        {{ $section->settings->subtitle }}
        @blocks($section)

```

### 2. Understanding the `@schema()` array

[](#2-understanding-the-schema-array)

KeyTypeDescription`name`string**Required.** Human-readable name shown in the editor`settings`arraySetting definitions with `id`, `type`, `label`, `default``blocks`arrayAllowed child block types (inline definitions or theme refs)`presets`arrayPre-configured templates shown in the "Add section" picker`max_blocks`intMaximum number of child blocks allowed### 3. Section template API

[](#3-section-template-api)

Property / MethodDescription`$section->id`Unique instance ID`$section->type`Section type identifier (matches filename)`$section->name`Human-readable name from schema`$section->settings->key`Typed setting access with automatic defaults`$section->blocks``BlockCollection` of hydrated top-level blocks`$section->editorAttributes()`Editor `data-*` attributes (empty string when not editing)`@blocks($section)`Renders all top-level blocks---

Creating Blocks
---------------

[](#creating-blocks)

Blocks are reusable components that live inside sections (or inside other blocks). Block Blade files live in the configured blocks directory (default: `resources/views/blocks/`).

### Theme Blocks

[](#theme-blocks)

Theme blocks are registered globally and can be referenced by any section that declares `['type' => '@theme']` in its `blocks` array.

```
{{-- resources/views/blocks/row.blade.php --}}
@schema([
    'name' => 'Row',
    'settings' => [
        [
            'id' => 'columns',
            'type' => 'select',
            'label' => 'Columns',
            'default' => '2',
            'options' => [
                [
                    'value' => '1',
                    'label' => '1 Column',
                ],
                ['value' => '2', 'label' => '2 Columns'],
                ['value' => '3', 'label' => '3 Columns'],
            ],
        ],
        [
            'id' => 'gap',
            'type' => 'select',
            'label' => 'Gap',
            'default' => 'md',
            'options' => [
                [
                    'value' => 'none',
                    'label' => 'None',
                ],
                ['value' => 'sm', 'label' => 'Small'],
                ['value' => 'md', 'label' => 'Medium'],
                ['value' => 'lg', 'label' => 'Large'],
            ],
        ],
    ],
    'blocks' => [
        [
            'type' => 'column',
            'name' => 'Column',
        ],
    ],
    'presets' => [
        [
            'name' => 'Two Columns',
            'settings' => ['columns' => '2'],
            'blocks' => [
                [
                    'type' => 'column',
                ],
                ['type' => 'column'],
            ],
        ],
        [
            'name' => 'Three Columns',
            'settings' => ['columns' => '3'],
            'blocks' => [
                [
                    'type' => 'column',
                ],
                ['type' => 'column'],
                ['type' => 'column'],
            ],
        ],
    ],
])

editorAttributes() !!}
    class="grid grid-cols-{{ $block->settings->columns }} gap-{{ $block->settings->gap }}">
    @blocks($block)

```

```
{{-- resources/views/blocks/column.blade.php --}}
@schema([
    'name'     => 'Column',
    'settings' => [
        ['id' => 'padding', 'type' => 'select', 'label' => 'Padding', 'default' => 'none',
         'options' => [
             ['value' => 'none', 'label' => 'None'],
             ['value' => 'sm', 'label' => 'Small'],
             ['value' => 'md', 'label' => 'Medium'],
             ['value' => 'lg', 'label' => 'Large'],
         ]],
    ],
    'blocks' => [
        ['type' => '@theme'],
    ],
])

editorAttributes() !!} class="p-{{ $block->settings->padding }}">
    @blocks($block)

```

### Block template API

[](#block-template-api)

Property / MethodDescription`$block->id`Unique block instance ID`$block->type`Block type identifier (matches filename)`$block->settings->key`Typed setting access with defaults`$block->blocks``BlockCollection` of nested child blocks`$block->editorAttributes()`Editor `data-*` attributes`$section`Parent section (always available in block views)`@blocks($block)`Renders child blocks of this container### Block Detection: Local vs Theme Reference

[](#block-detection-local-vs-theme-reference)

In `@schema` `blocks` arrays, entries are detected as either **local definitions** or **theme-block references**:

EntryTypeDetection`['type' => 'column']`Theme referenceOnly has `type` key → resolved from block registry`['type' => '@theme']`WildcardAccepts any registered theme block`['type' => 'column', 'name' => 'Column', 'settings' => [...]]`Local definitionHas extra keys → used as-is---

Page JSON Structure
-------------------

[](#page-json-structure)

Pages are stored as JSON files in the configured pages directory. Each page contains sections, their settings, nested blocks, and render order.

```
{
  "title": "Home",
  "meta": {
    "description": "Welcome to our site"
  },
  "sections": {
    "hero-1": {
      "type": "hero",
      "settings": {
        "title": "Welcome",
        "subtitle": "Build amazing pages",
        "bg_color": "#f0f0f0"
      },
      "blocks": {
        "row-1": {
          "type": "row",
          "settings": { "columns": "2", "gap": "md" },
          "blocks": {
            "col-left": {
              "type": "column",
              "settings": { "padding": "md" },
              "blocks": {}
            },
            "col-right": {
              "type": "column",
              "settings": { "padding": "md" },
              "blocks": {}
            }
          },
          "order": ["col-left", "col-right"]
        }
      },
      "order": ["row-1"]
    }
  },
  "order": ["hero-1"]
}
```

---

Templates
---------

[](#templates)

Templates are **JSON fallback layouts** for pages that have no per-page page builder JSON file and no custom Blade view. They let you define a single file that controls which sections a whole category of pages renders — without requiring a separate `pages/{slug}.json` for every page.

### Page resolution order

[](#page-resolution-order)

```
1. Custom Blade view    pages/{slug}.blade.php   (highest priority)
2. Page builder JSON    pages/{slug}.json
3. Template JSON        templates/{template}.json or templates/page.json
4. 404

```

Templates are only consulted when both step 1 and step 2 miss. A template never overrides an existing page JSON.

### Creating a template

[](#creating-a-template)

Place template files in `resources/views/templates/` (configurable via `config('pagebuilder.templates')`).

```
// resources/views/templates/page.json  — default template used by all pages
{
    "sections": {
        "main": {
            "type": "page-content"
        }
    },
    "order": ["main"]
}
```

The `page.json` file is the **default template**. Any page without a page JSON, and without a specific template selected, renders through it.

### Template JSON schema

[](#template-json-schema)

FieldTypeRequiredDescription`sections`objectyesSection data map — same format as page JSON sections`order`string\[\]yesSection render order`layout`string | falsenoLayout type (e.g. `"page"`, `"full-width"`). Defaults to `"page"`. Pass `false` to render without header/footer zones`wrapper`stringnoCSS-selector string that wraps all sections in an HTML element### Assigning a template to a page

[](#assigning-a-template-to-a-page)

Set the `template` column on the `Page` model:

```
$page = Page::find(1);
$page->template = 'page.alternate';
$page->save();
```

Or when creating a page:

```
Page::create([
    'title'    => 'About Us',
    'slug'     => 'about',
    'template' => 'page.alternate',
    'content'  => 'About our company.',
]);
```

Template names map to filenames without the `.json` extension:

`template` fieldFile loaded`null` or `""``templates/page.json``"page"``templates/page.json``"page.alternate"``templates/page.alternate.json``"product"``templates/product.json`If the selected template file does not exist, the package falls back to `page.json`. If `page.json` also does not exist, a 404 is returned.

### The `wrapper` property

[](#the-wrapper-property)

The `wrapper` field wraps all rendered section HTML in a single HTML element. The value uses a CSS-selector-like syntax:

```
tag#id.class1.class2[attr1=val1][attr2=val2]

```

Supported wrapper tags: ``, ``, ``.

```
{
    "wrapper": "div#div_id.div_class[attribute-one=value]",
    "sections": { "main": { "type": "page-content" } },
    "order": ["main"]
}
```

Output:

```

```

### Variable interpolation

[](#variable-interpolation)

Template section settings support `{{ $page->attribute }}` placeholders. At render time they are replaced with the corresponding attribute from the `Page` Eloquent model.

```
{
    "sections": {
        "hero": {
            "type": "hero",
            "settings": {
                "title":       "{{ $page->title }}",
                "description": "{{ $page->meta_description }}"
            }
        },
        "main": { "type": "page-content" }
    },
    "order": ["hero", "main"]
}
```

Any column on the `Page` model can be used: `title`, `slug`, `content`, `meta_title`, `meta_description`, `meta_keywords`, or any custom column. Missing or `null` attributes resolve to an empty string.

### Alternative template example

[](#alternative-template-example)

```
// resources/views/templates/page.alternate.json
{
    "wrapper": "main#page-alternate.page-wrapper",
    "sections": {
        "main": {
            "type": "page-content"
        }
    },
    "order": ["main"]
}
```

### `layout: false` — rendering without header/footer

[](#layout-false--rendering-without-headerfooter)

Set `"layout": false` to skip the layout zone system entirely. No `@sections('header')` or `@sections('footer')` zones are rendered:

```
{
    "layout": false,
    "sections": {
        "main": { "type": "hero" }
    },
    "order": ["main"]
}
```

### Theme-aware templates

[](#theme-aware-templates)

If a theme is active, `TemplateStorage` checks the theme's `views/templates/` directory first. This allows themes to override the default `page.json` template or add new template files without touching the application's templates directory:

```
themes/my-theme/views/templates/page.json         ← overrides app templates/page.json
themes/my-theme/views/templates/product.json      ← theme-specific product template

```

---

Rendering Pages
---------------

[](#rendering-pages)

### In Controllers

[](#in-controllers)

```
use Coderstm\PageBuilder\Facades\Page;

class PageController extends Controller
{
    public function show(string $slug)
    {
        return Page::render($slug);
    }
}
```

### Programmatic Page Rendering

[](#programmatic-page-rendering)

```
use Coderstm\PageBuilder\Facades\Page;

// Render from slug (loads JSON from disk)
$html = Page::render('home');

// Render with extra meta passed to the page model/template
$html = Page::render('home', ['title' => 'My Home Page']);
```

---

Registering Additional Paths
----------------------------

[](#registering-additional-paths)

You can register additional directories for section and block discovery:

```
use Coderstm\PageBuilder\Facades\Section;
use Coderstm\PageBuilder\Facades\Block;

// In a service provider's boot() method
Section::add(resource_path('views/custom-sections'));
Block::add(resource_path('views/custom-blocks'));
```

### Manual Registration

[](#manual-registration)

Register a section or block programmatically without a Blade file:

```
use Coderstm\PageBuilder\Facades\Section;
use Coderstm\PageBuilder\Schema\SectionSchema;

Section::register('custom-hero', new SectionSchema([
    'name' => 'Custom Hero',
    'settings' => [
        ['id' => 'title', 'type' => 'text', 'label' => 'Title', 'default' => 'Hello'],
    ],
]), 'my-views::sections.custom-hero');
```

---

Setting Types
-------------

[](#setting-types)

The `@schema` settings array supports these built-in types:

TypeDescriptionExtra Keys`text`Single-line text input—`textarea`Multi-line text input—`richtext`Rich text editor (multi-line)—`inline_richtext`Rich text editor (single-line)—`select`Dropdown select`options: [{value, label}]``radio`Radio buttons`options: [{value, label}]``checkbox`Boolean toggle—`range`Numeric slider`min`, `max`, `step``number`Number input`min`, `max`, `step``color`Color picker (hex)—`color_background`CSS background (gradients)—`image_picker`Media library selector—`url`Link/URL input—`video_url`YouTube/Vimeo URL—`icon_fa`FontAwesome icon picker—`icon_md`Material Design icon picker—`text_alignment`Left/Center/Right segmented ctrl—`html`Raw HTML code editor—`blade`Blade template code editor—`header`Sidebar section divider`content``paragraph`Sidebar informational text`content``external`Dynamic API-driven selector—---

Editor
------

[](#editor)

### Accessing the Editor

[](#accessing-the-editor)

The editor is available at:

```
GET /pagebuilder/{slug?}

```

Protect it with authentication middleware in your config:

```
// config/pagebuilder.php
'middleware' => ['web', 'auth'],
```

### Editor API Endpoints

[](#editor-api-endpoints)

MethodURLDescription`GET``/pagebuilder/pages`List all pages`GET``/pagebuilder/page/{slug}`Get page JSON`POST``/pagebuilder/render-section`Live-render a section`POST``/pagebuilder/save-page`Save page JSON`GET``/pagebuilder/assets`List uploaded assets`POST``/pagebuilder/assets/upload`Upload an asset### Editor Helpers

[](#editor-helpers)

```
// Check if editor mode is active
pb_editor(); // Returns bool

// In Blade templates
@if(pb_editor())
    {{-- Editor-only content --}}
@endif
```

---

Custom Asset Providers
----------------------

[](#custom-asset-providers)

By default the editor stores uploaded assets through the built-in Laravel provider (`storage/app/public/pagebuilder`). You can replace it with any storage backend — S3, Cloudflare R2, Cloudinary, DigitalOcean Spaces — by passing a custom provider to `PageBuilder.init()`.

### Provider interface

[](#provider-interface)

A provider is a plain JavaScript object with two async methods:

```
const myProvider = {
  // Return a paginated list of assets
  async list({ page = 1, search = "" } = {}) {
    // Must return: { data: Asset[], pagination: { page, per_page, total } }
  },

  // Upload a File object, return the stored asset
  async upload(file) {
    // Must return: { id, name, url, thumbnail, size, type }
  },
};
```

The `url` field is what gets stored in page JSON and rendered in Blade — it must be a publicly accessible URL.

### Registering the provider

[](#registering-the-provider)

```

  PageBuilder.init({
    baseUrl: "/pagebuilder",
    assets: {
      provider: myProvider,
    },
  });

```

### AWS S3 / DigitalOcean Spaces / Cloudflare R2

[](#aws-s3--digitalocean-spaces--cloudflare-r2)

Keep uploads server-side through a thin Laravel proxy controller that writes to S3 using `Storage::disk('s3')`:

```
const s3Provider = {
  async list({ page = 1, search = "" } = {}) {
    const q = new URLSearchParams({ page, q: search });
    const res = await fetch(`/api/pagebuilder/assets?${q}`);
    if (!res.ok) throw new Error("Failed to fetch assets");
    return res.json();
  },
  async upload(file) {
    const body = new FormData();
    body.append("file", file);
    const res = await fetch("/api/pagebuilder/assets/upload", {
      method: "POST",
      headers: {
        "X-CSRF-TOKEN":
          document
            .querySelector('meta[name="csrf-token"]')
            ?.getAttribute("content") ?? "",
      },
      body,
    });
    if (!res.ok) throw new Error("Upload failed");
    return res.json();
  },
};
```

For Spaces/R2, configure the S3-compatible endpoint in `.env` — no JS changes required:

```
AWS_ENDPOINT=https://nyc3.digitaloceanspaces.com   # Spaces
# or
AWS_ENDPOINT=https://.r2.cloudflarestorage.com  # R2
AWS_USE_PATH_STYLE_ENDPOINT=true
```

### Cloudinary (direct browser upload)

[](#cloudinary-direct-browser-upload)

```
const cloudinaryProvider = {
  async list({ page = 1, search = "" } = {}) {
    const q = new URLSearchParams({ page, q: search });
    const res = await fetch(`/api/pagebuilder/cloudinary/assets?${q}`);
    if (!res.ok) throw new Error("Failed to fetch assets");
    return res.json();
  },
  async upload(file) {
    // Get a signed upload preset from your Laravel backend
    const sigRes = await fetch("/api/pagebuilder/cloudinary/sign", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-CSRF-TOKEN":
          document
            .querySelector('meta[name="csrf-token"]')
            ?.getAttribute("content") ?? "",
      },
      body: JSON.stringify({ filename: file.name }),
    });
    const { signature, timestamp, cloudName, apiKey, folder } =
      await sigRes.json();

    const body = new FormData();
    body.append("file", file);
    body.append("api_key", apiKey);
    body.append("timestamp", timestamp);
    body.append("signature", signature);
    body.append("folder", folder);

    const up = await fetch(
      `https://api.cloudinary.com/v1_1/${cloudName}/image/upload`,
      { method: "POST", body },
    );
    if (!up.ok) throw new Error("Cloudinary upload failed");
    const d = await up.json();

    return {
      id: d.public_id,
      name: d.original_filename,
      url: d.secure_url,
      thumbnail: d.secure_url.replace(
        "/upload/",
        "/upload/w_200,h_200,c_fill/",
      ),
      size: d.bytes,
      type: `${d.resource_type}/${d.format}`,
    };
  },
};
```

For the full provider contract and additional examples, see the [Developer Documentation](docs/index.md).

---

Blade Directives
----------------

[](#blade-directives)

DirectiveDescription`@blocks($section)`Renders all top-level blocks of a section`@blocks($block)`Renders child blocks inside a container block`@schema([...])`Declares schema (no-op at render time, extracted at registration)`@pbEditorClass`Outputs CSS class when editor mode is active---

Architecture Reference
----------------------

[](#architecture-reference)

### Key Classes

[](#key-classes)

ClassResponsibility`SectionRegistry`Discovers section Blade files, extracts schemas, provides lookup`BlockRegistry`Discovers block Blade files, extracts schemas, provides lookup`Renderer`Core rendering engine: hydrates JSON → objects, renders via Blade`PageRenderer`Loads page JSON, renders all enabled sections in order`PageStorage`Reads/writes page JSON files to disk`PagePublisher`Compiles pages into static Blade files`PageBuilder`Static API for editor mode, CSS/JS asset URLs---

Reporting Issues
----------------

[](#reporting-issues)

When reporting bugs, please include:

- PHP and Laravel versions
- Package version
- Steps to reproduce
- Expected vs actual behavior
- Relevant error messages or logs

---

Layout Sections
---------------

[](#layout-sections)

Pages can define a `layout` key for per-page overrides of structural slots (header, footer) that live **outside** the main `@yield('content')` block in your Blade layout.

```
{
    "sections": { "..." },
    "order": ["hero"],
    "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
                }
            }
        },
    }
}
```

Render layout sections in your Blade layout file using `@sections()`:

```
{{-- resources/views/layouts/page.blade.php --}}

        {{ $meta_title ?? ($title ?? '') . ' | ' . config('app.name') }}

    ...

    @stack('content_for_head')

    @sections('header')

    @yield('content')

    @sections('footer')

```

Layout sections are **non-sortable** — their position is determined by the Blade layout. In the editor they appear as fixed rows above and below the sortable page section list.

**Rules:**

- Keys that match `"header"` or carry `position: "top"` render in the top zone; everything else goes to the bottom zone.
- `disabled: true` causes `@sections()` to return an empty string for that slot.
- `_name` overrides the schema display name in the editor (same as page sections).

---

Theme Integration
-----------------

[](#theme-integration)

The package integrates with [qirolab/laravel-themer](https://github.com/qirolab/laravel-themer) for multi-theme support.

### Register Theme Sections and Blocks

[](#register-theme-sections-and-blocks)

If you're using a theme system you can set the active theme and the package will automatically register theme `sections` and `blocks` when the expected view paths exist. This is convenient when using a theme package or `qirolab/laravel-themer`.

```
use Coderstm\PageBuilder\Facades\Theme;
use Coderstm\PageBuilder\Facades\Section;
use Coderstm\PageBuilder\Facades\Block;

// Set the active theme (for example in a ThemeServiceProvider or middleware)
Theme::set('my-theme');

// The package will automatically register the following directories if they exist:
//   themes/my-theme/views/sections
//   themes/my-theme/views/blocks

// If you need to register additional paths manually you can still call:
Section::add(base_path('themes/my-theme/views/sections'));
Block::add(base_path('themes/my-theme/views/blocks'));
```

### Global Theme Settings

[](#global-theme-settings)

Define global design tokens (colors, fonts, spacing) in `config/pagebuilder.php`. Settings are grouped for display in the editor's Theme Settings panel:

```
'theme_settings_schema' => [
    [
        'name' => 'Colors',
        'settings' => [
            [
                'key'     => 'colors.primary',
                'label'   => 'Primary',
                'type'    => 'color',
                'default' => '#10b981',
                'css_var' => '--colors-primary',
            ],
            [
                'key'     => 'colors.background_dark',
                'label'   => 'Background (dark)',
                'type'    => 'color',
                'default' => '#0f0f0f',
                'css_var' => '--colors-background-dark',
            ],
        ],
    ],
    [
        'name' => 'Typography',
        'settings' => [
            [
                'key'     => 'fonts.body',
                'label'   => 'Body font',
                'type'    => 'google_font',
                'default' => 'Inter, sans-serif',
                'css_var' => '--fonts-body',
            ],
        ],
    ],
    [
        'name' => 'Radius & Shape',
        'settings' => [
            [
                'key'     => 'radius.base',
                'label'   => 'Radius (base)',
                'type'    => 'text',
                'default' => '0.25rem',
                'css_var' => '--radius-base',
            ],
        ],
    ],
],
```

**Schema fields**

FieldRequiredDescription`key`YesDot-notation key used to store and retrieve the value (`colors.primary`)`type`YesField type: `color`, `text`, `select`, `google_font`, etc.`label`YesHuman-readable label shown in the editor panel`default`YesFallback value used when no override has been saved`css_var`NoCSS custom property (e.g. `--colors-primary`) updated live in the preview**`css_var` — live preview sync**

When a `css_var` is declared on a setting, the editor updates that CSS custom property on the preview iframe's `:root` in real time as the user types — no page reload required. Declare your tokens in your theme stylesheet to consume them:

```
:root {
    --colors-primary: #10b981;
    --fonts-body: Inter, sans-serif;
    --radius-base: 0.25rem;
}

.btn-primary  { background-color: var(--colors-primary); }
body          { font-family: var(--fonts-body); }
.card         { border-radius: var(--radius-base); }
```

**`google_font` setting type**

Use `type: 'google_font'` to let editors pick a Google Font from a curated library. The selected font is automatically injected as a `` tag in the page `` via the `@pbThemeFont` Blade directive:

```
{{-- in your layout  --}}
@pbThemeFont
```

**Accessing values in Blade**

`$theme` is a `ThemeSettings` instance shared with all Blade views:

```

    :root {
        --colors-primary: {{ $theme->get('colors.primary', '#10b981') }};
        --fonts-body: {{ $theme->get('fonts.body', 'Inter, sans-serif') }};
    }

```

Use `$theme->get('key', 'default')` for dot-notation access with a fallback, or `$theme->key` for top-level keys.

**Editor reset options**

In the Theme Settings panel editors can:

- **Reset individual setting** — hover a setting row and click the reset icon to restore its `default` value.
- **Reset all** — click **Reset all** in the panel header to restore every setting to its schema default in one action. Both reset paths trigger live CSS var updates immediately.

### Theme Middleware

[](#theme-middleware)

You can use the provided `ThemeMiddleware` to automatically apply themes based on route parameters or session data.

```
// routes/web.php
Route::get('/shop/{theme_slug}', function () {
    // ...
})->middleware('theme:theme_slug');
```

---

Artisan Commands
----------------

[](#artisan-commands)

CommandDescription`pagebuilder:install`Publish config, migrations, assets, and scaffold starter views`pagebuilder:install --force`Same as above, overwriting any existing files`pagebuilder:install --migrate`Also run `php artisan migrate` after publishing`pages:regenerate`Rebuild the page registry cache (run after adding/removing page JSON files)`theme:link`Symlink theme asset directories into `public/themes/``theme:link --force`Overwrite existing symlinks---

License
-------

[](#license)

This package is released under a **Non-Commercial Open Source License**.

- Free to use, modify, and distribute for **non-commercial purposes**.
- **Commercial use is not permitted** without a separate license agreement.
- Contact  for commercial licensing.

See [LICENSE.md](LICENSE.md) for the full license text.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance97

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

23

Last Release

49d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/46da5f4e665d1259bc6a008c5919968518ff86da888a2a65a17e7f93900f8d19?d=identicon)[Dipak Sarkar](/maintainers/Dipak%20Sarkar)

---

Top Contributors

[![dipaksarkar](https://avatars.githubusercontent.com/u/33681276?v=4)](https://github.com/dipaksarkar "dipaksarkar (57 commits)")

---

Tags

bladeblade-templatecmscontent-managementdrag-and-droplanding-page-builderlaravellaravel-cmslaravel-packagemulti-themepage-builderschema-driventheme-systemvisual-editorwebsite-builderwebsite-editorjsonlaravelbladecontentcmsblockssectionsthemespage buildercoderstm

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/coderstm-laravel-page-builder/health.svg)

```
[![Health](https://phpackages.com/badges/coderstm-laravel-page-builder/health.svg)](https://phpackages.com/packages/coderstm-laravel-page-builder)
```

###  Alternatives

[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[tightenco/jigsaw

Simple static sites with Laravel's Blade.

2.2k438.5k29](/packages/tightenco-jigsaw)[laravellux/html

HTML and Form Builders for the Laravel Framework

35239.2k3](/packages/laravellux-html)[konekt/html

HTML and Form Builders for the Laravel Framework

24403.2k5](/packages/konekt-html)[bjuppa/laravel-blog

Add blog functionality to your Laravel project

483.3k1](/packages/bjuppa-laravel-blog)[hasinhayder/tyro-login

Tyro Login - Beautiful, customizable authentication views for Laravel 12 &amp; 13

2362.2k2](/packages/hasinhayder-tyro-login)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
