Installation Run:
composer require flobbos/page-composer
php artisan page-composer:install
Accept the default name (or specify one) during installation.
Access the CMS
Register a Livewire component in your routes (e.g., routes/web.php):
Route::get('/admin/pages', PageComposer::make('pages')->route());
Visit /admin/pages to see the editor UI.
First Page Creation
Display Content in Blade
Use the page helper in your Blade template:
@page('homepage', 'content')
Where homepage is the page name and content is the slot (e.g., default).
/resources/views/vendor/page-composer/ (Override default UI templates).php artisan page-composer:sync-row-space (Fix layout issues post-install).home.@page('home', 'default')
Extend Existing Elements: Create a custom element by publishing the stub:
php artisan vendor:publish --tag=page-composer-elements
Edit app/PageComposer/Elements/CustomElement.php and register it in config/page-composer.php:
'elements' => [
\App\PageComposer\Elements\CustomElement::class,
],
Element Structure:
namespace App\PageComposer\Elements;
use Flobbos\PageComposer\Contracts\Element;
class CustomElement implements Element {
public function render() { ... }
public function config() { ... } // Livewire properties
}
Dynamic Page Loading:
Fetch pages via the Page model:
use Flobbos\PageComposer\Models\Page;
$homepage = Page::where('name', 'home')->first();
Render content in Blade:
@if($homepage)
@foreach($homepage->rows as $row)
@foreach($row->elements as $element)
{!! $element->render() !!}
@endforeach
@endforeach
@endif
Conditional Rendering: Use slots for modular content:
@page('home', 'header')
@page('home', 'footer')
config/page-composer.php:
'grid' => [
'columns' => 12, // Default 12-column grid
'gutter' => '1rem',
],
Sync row space after changes:
php artisan page-composer:sync-row-space
use Flobbos\PageComposer\Http\Livewire\PageEditor;
class CustomPageEditor extends PageEditor {
protected $rules = [
'custom_field' => 'required',
];
}
Register in app/Providers/AppServiceProvider.php:
PageComposer::extend('pages', function () {
return CustomPageEditor::class;
});
Cache Pages: Cache rendered pages for performance:
$cachedContent = Cache::remember("page_{$page->id}", now()->addHours(1), function () use ($page) {
return $page->render();
});
Media Library: Integrate with Spatie Media Library for image/video handling:
use Spatie\MediaLibrary\HasMedia;
class ImageElement implements Element, HasMedia {
public function registerMediaCollections(): void {
$this->addMediaCollection('images');
}
}
Tailwind/Alpine.js: Use Alpine.js for dynamic interactions within elements:
<div x-data="{ open: false }">
<button @click="open = true">Toggle</button>
<div x-show="open">Content</div>
</div>
Inertia.js: For SPA-like experiences, pair with Inertia:
public function render() {
return Inertia::render('PageEditor', [
'pages' => Page::all(),
]);
}
Stale Row Space
php artisan page-composer:sync-row-space and republish affected pages.Element Serialization
Arrayable or Jsonable in your element class:
use Illuminate\Contracts\Support\Arrayable;
public function toArray() { return [...] }
Livewire Hooks
updated() or mounted() sparingly; prefer config() for properties.Permission Handling
Page::where('name', $pageName)->gate(['update']);
Database Bloat
php artisan page-composer:prune --days=30
Element Rendering
storage/logs/livewire.log for Livewire errors.{{ dd($element) }} in Blade to inspect data.Layout Issues
available_space column in page_rows table:
SELECT * FROM page_rows WHERE available_space IS NULL;
Livewire Wire:ignore
<div wire:ignore> unless intentional.Default Element Order
config/page-composer.php under 'elements'.Grid Overrides
'grid' => [
'columns' => 16,
'gutter' => '0.75rem',
],
Element Icons
Element class:
public function icon() {
return 'heroicon-o-star';
}
Custom Storage
Flobbos\PageComposer\Contracts\PageRepository.Element Presets
Page::create([
'name' => 'home',
'content' => json_encode([
'rows' => [
[
'elements' => [
['type' => 'text', 'content' => 'Hello World'],
],
],
],
]),
]);
API Endpoints
Route::get('/api/pages/{name}', function ($name) {
return Page::where('name', $name)->with('rows.elements')->firstOrFail();
});
Webhooks
use Flobbos\PageComposer\Events\PageUpdated;
PageUpdated::dispatch($page);
EventServiceProviderHow can I help you explore Laravel packages today?