Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Pagebuilder Laravel Package

hansschouten/laravel-pagebuilder

Laravel Pagebuilder provides a drag-and-drop, block-based page editor for Laravel apps. Create reusable content blocks, manage page templates, and render dynamic pages with Blade, no separate CMS needed. Ideal for admin-managed marketing pages and content-driven sites.

View on GitHub
Deep Wiki
Context7

Getting Started

The v0.31.0 release note is only a pointer to the upstream tag, so treat the v0.30.0...v0.31.0 compare view as the actual changelog before you touch composer.json. This is a 0.x package: minor bumps have historically carried signature and config changes, so pin it exactly ("hansschouten/phpagebuilder": "0.31.0") rather than using a caret constraint, and upgrade in a branch with the diff open.

Minimal path:

  1. composer require hansschouten/phpagebuilder:0.31.0. On Laravel versions without package auto-discovery, register the package service provider and facade alias in config/app.php.
  2. Publish everything and commit the output: config (config/phpagebuilder.php), migrations, and the block/editor views under resources/views/vendor/.... Commit the published migrations — see the gotcha below.
  3. php artisan migrate, then confirm the pages-style tables exist before wiring any routes.
  4. Create a layout Blade view (the package expects a layout name in config, e.g. layouts.app) that yields the builder content where you want it rendered.
  5. First use case: one CMS-managed page ("About"). Create it in the builder UI at the configured route prefix, then render it from a controller with the package's page-load call — typically {!! PageBuilder::loadPage($page) !!} (facade) or the equivalent phpagebuilder() helper, inside a view that extends your layout. Verify in this order: admin panel loads → page saves → block content appears on the front end → uploaded image resolves from public/.

Where to look first, in this order: config/phpagebuilder.php (layout view, uploads directory, middleware, route prefix, registered blocks), the published block views, then the published migrations to learn the schema. Most "it doesn't work" issues in this package are a config value or an unregistered block, not a bug.

Implementation Patterns

Layout-first. Decide the wrapper view before anyone starts authoring pages. The builder stores block content only; chrome, nav, and meta belong in your layout. Give the layout a consistent contract (a $page variable for title/SEO fields) so every page inherits it.

Custom blocks as the unit of work. Day-to-day authoring value comes from blocks, not from the generic rich-text block. The pattern is a block class (render logic, thumbnail, view data) plus a Blade partial. Keep the class thin: resolve data, return a view, let Blade do the markup. Register each new block in config and restart/clear view cache — an unregistered block silently fails to appear in the editor.

Routing. Pages carry a route/slug field, so rendering is a lookup: either an explicit route per page, or a catch-all Route::get('{slug}', ...)->where('slug', '.*') registered after all static routes. Handle misses with a real 404 rather than rendering an empty layout, and keep the builder prefix out of your sitemap.

Lock down the admin panel immediately. Set the package's middleware config to ['web', 'auth'] (plus a gate or role check in a service provider) on day one, and put the route prefix behind an IP allowlist or separate guard if the panel is internet-facing. These routes are auto-registered and easy to forget in a launch checklist — check php artisan route:list after install.

Cache the rendered output, not the block queries. Rendering walks block classes per request. Cache::rememberForever("page:{$page->id}:{$page->updated_at}", fn () => view(...)) gets you most of the win with trivial invalidation. If you cache full responses instead, only cache pages that don't depend on session state — never cache an authenticated variant.

Swap the page model instead of editing vendor code. Point the config's model entry at App\Models\Page extends the package model and add your own scopes, relations, and casts there. Same for the repository/manager accessors: decorate, don't fork. This is what keeps the next 0.x upgrade cheap.

Content parity across environments. Write an artisan command that exports pages and blocks to JSON and imports them into staging/prod, and a seeder that plants a few fixture pages locally. Without this, content and code drift and nobody can reproduce a rendering bug.

Testing. There are no model factories shipped, so write Page::factory() yourself and feature-test the rendering route with assertSee. Keep one HTML snapshot fixture per custom block; it's the fastest way to catch a breaking change from a package bump.

Gotchas and Tips

  • The release note is not the changelog. For 0.31.0, read the commit list and the diff of the config file and block base class. Check specifically for renamed config keys, changed block method signatures, and migration alterations — those three are where 0.x upgrades bite.
  • Published migrations decouple you from the package. Once published, new upstream migration changes never reach you. Diff database/migrations against the package's on every upgrade and add your own follow-up migration rather than re-publishing over live tables.
  • Escaping is your responsibility. The package renders block HTML unescaped by design. {!! !!} on anything a non-trusted editor typed is the single largest XSS surface here — restrict the HTML block to trusted roles, or sanitize on save.
  • Uploads default to a public directory with no image pipeline. Whitelist extensions and block SVG/HTML uploads unless you're sure, and be aware that a .php-named file in a public folder on a misconfigured server is an RCE. If that's a concern, move uploads behind a controller that sets Content-Type explicitly.
  • Blank output with no exception usually means a block class resolved but its view didn't. Run php artisan view:clear, confirm the published view path, and temporarily wrap the render call in a try/catch to surface the swallowed error.
  • Route cache staleness after upgrade. If pages suddenly 404 after bumping, clear route:clear and config:clear before debugging anything else.
  • Asset publishing is separate from view publishing. The editor's CSS/JS ships under its own publish tag; if your front-end build (Vite/Tailwind purge) is scoped too broadly, it can strip classes the builder's own stylesheet depends on. Keep package assets in public/vendor/... and out of your pipeline.
  • Escape hatches worth knowing: the config's page-model override, custom blocks, published views, and the manager/repository accessors on the main package instance. Between those four you can cover almost any requirement without touching vendor/ — which is exactly what makes the next 0.x bump a fifteen-minute job instead of a rewrite.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator