Add content management to a Laravel app you already run. Pagible is a Composer package that brings structured content, versioning, GraphQL and JSON:API, an AI editor, and an MCP server — without taking over your app.
Every Laravel project hits the same question eventually. The client wants to edit the content themselves, and you have to decide who owns the application.
None of the usual answers are clean.
Run WordPress in headless mode and you take on a second runtime, a second auth system, and a REST API you didn't design. Adopt a hosted platform like Contentful and you pay per record while your content lives on someone else's servers. Build a bespoke admin panel and you spend the next two months rebuilding a tree editor, a media library, and a draft/publish flow instead of shipping the product.
| Approach | What you take on |
|---|---|
| Headless WordPress | A second runtime, a second auth system, a REST API you didn't design |
| Hosted platform (Contentful) | Per-record pricing, your content on someone else's servers |
| Bespoke admin panel | Months rebuilding a tree editor, media library, and publish flow |
| PagibleAI | One Composer package; your app stays yours |
PagibleAI starts from a different premise: add content management to a Laravel app you already run. You composer require it, and your app stays your app. The CMS moves in as a set of packages, brings its own models, migrations, admin panel, and APIs, and stays out of the way the rest of the time.
Here's what stands out from a developer's seat.
The install is a normal Composer package
There's no service to host and nothing external to wire up. If you have a Laravel 11, 12, or 13 app on PHP 8.2+, you add the package and run the migrations. That's the whole install:
composer require aimeos/pagible
php artisan cms:install
php artisan migrate
Then grant yourself editing rights:
php artisan cms:user -e editor@example.com
The admin panel is served from your own app at /cmsadmin. It's a Vue 3 and Vuetify single-page app talking to a GraphQL endpoint. No external dashboard, no webhook round-trips, no second deployment to keep in sync.
Underneath, it's a monorepo split into focused packages: core models, admin, AI, GraphQL, JSON:API, search, MCP, backup, import, and theming. They share the Aimeos\Cms\ namespace. You install one meta-package and get all of it, but each concern stays isolated, so the surface you have to reason about stays small.
What it's built on
There's not much exotic here. Each capability leans on a package the Laravel community already knows, so most of the stack is familiar before you read a line of Pagible's own code.
| Package | Role |
|---|---|
laravel/framework |
Laravel 11 / 12 / 13 |
nuwave/lighthouse |
GraphQL admin API |
laravel-json-api/laravel |
JSON:API content delivery |
laravel/scout |
Full-text search engine |
laravel/mcp |
MCP server |
laravel/reverb |
Real-time broadcasting (optional) |
laravel/cashier |
Payments and paywalls — Stripe, Paddle, Mollie (optional) |
aimeos/prisma |
Broad LLM provider integration |
aimeos/laravel-nestedset |
Optimized nested-set page tree |
intervention/image |
Image processing and previews |
league/commonmark |
Markdown rendering |
ezyang/htmlpurifier, enshrined/svg-sanitize
|
Content sanitization |
The admin panel is its own front end on top: Vue 3, Vuetify, and Vite, built into a bundle that ships with the package.
Content is structured, not a wall of HTML
This is what sets Pagible apart from the WordPress lineage. A page isn't one big rich-text field. It's a node in a tree that holds an ordered list of typed content elements.
Three models carry the weight:
- Page — a node in a nested-set tree, with drag-and-drop reordering, UUIDs, and soft deletes.
-
Element — a content block with a
typeand adatapayload. Pages reference their elements in order, and the same element can be shared across many pages. - File — media, with AI-generated descriptions and automatically sized previews.
cms_pages (nested-set tree)
└─ Page ─┬─ ordered elements ── cms_elements typed, reusable blocks
├─ attached files ── cms_files media + AI descriptions
└─ every change ── cms_versions immutable snapshots
A content element is a small, typed structure:
[
'id' => 'ByC2ze',
'type' => 'text',
'group' => 'main',
'data' => ['text' => '## A heading\n\nSome **markdown** body copy.'],
]
Each type maps to a Blade partial in the theme. The entire renderer for a text element in e.g. theme/views/text.blade.php is one line:
@markdown($data->text ?? '')
You get a generous set of ~30 content element types out of the box including:
| Type | Renders |
|---|---|
text |
Markdown body copy |
hero |
Full-width banner with background media |
cards |
Grid of linked cards |
image-text |
Image alongside text |
pricing |
Pricing tables |
slideshow |
Image carousel |
code |
Syntax-highlighted snippets |
questions |
FAQ accordion |
Adding your own type means defining its data shape and writing the partial that renders it. You keep full control of the markup, and you can override any view or drop in your own components.
One detail matters for security. The mapping from a type to its handler comes from a trusted schema keyed by the type name, never from the stored element data. A content author can't smuggle a different handler into a record's payload, which closes a whole class of injection problems before it starts.
The payoff is the one Contentful built a business on. Your front end consumes predictable, typed data instead of parsing arbitrary HTML. The same content can render as a web page, feed a mobile app, or drive a sitemap, with no one copy-pasting markup.
Two APIs, pointed in opposite directions
Pagible is API-first in a literal sense. The admin panel is just one client of the API, not a privileged path into the database.
Editing runs over GraphQL, built on Lighthouse. It's the read-write surface the admin uses, and the same one your own tooling, scripts, or integrations can talk to.
Delivery runs over a read-only JSON:API that follows the spec, tuned for the public side. The split is deliberate. The write API can afford to be expressive; the read API is built to serve content fast and cache hard. Rendered pages are cached per-page with a configurable TTL, a day by default, so the common case never touches the editing machinery.
Prefer to render server-side? The theme package ships Blade templates and small directives like @markdown and @localDate, and all of it is yours to override.
Versioning an editorial team can trust
Every change to a page, element, or file becomes an immutable snapshot in cms_versions. Editors see the latest draft. The public sees the most recently published version. Those are two different things, on purpose.
That model gives you the workflow features for free:
- Save a draft without touching the live site.
- Schedule a version to go live later.
cms:publishpromotes anything whosepublish_athas passed, on a schedule. - Revert to any earlier snapshot when an edit goes wrong.
- Keep a bounded history (ten versions per item by default) as an audit trail.
For teams, there's an unsaved-changes guard, concurrent-edit protection, and a Git-like three-way merge when two people touch the same content. A half-finished draft is never one stray click from production.
AI is wired in per task, not bolted on as a chatbot
The "AI" in PagibleAI isn't one assistant sitting in a corner. It's a set of content operations, each routed to whichever provider does that job best, configured by environment variable.
The defaults look like this:
| Task | Provider |
|---|---|
| Write and refine copy, describe media | Gemini |
| Generate and edit images (inpaint, repaint) | Gemini |
| Translate | DeepL |
| Erase, isolate background, uncrop, upscale | Clipdrop |
| Transcribe audio | OpenAI Whisper |
Each task reads its own CMS_AI_{TASK} / _MODEL / _API_KEY trio. You configure only the providers you use, and you can point any task at a different model or provider without touching code. There are batch commands too. cms:description, for example, walks through pages and media that lack descriptions and writes SEO meta text and alt text for them.
Translation is the standout. Content can be translated into more than 35 languages while keeping its structured shape intact, so a translated page is still a real page, not a flattened copy. The whole AI layer is there when you want it and out of the way when you don't — it's truly optional.
The MCP server is the genuinely new part
This is where Pagible reads the room. The same operations the admin panel performs are exposed as Model Context Protocol tools — 33 of them, covering pages, elements, files, image editing, translation, and search.
The result: an assistant like Claude can run the CMS the way an editor would. Not by scraping a UI or guessing at an undocumented API, but through typed tools with real authorization behind them.
"Draft a pricing page from these bullet points, generate a hero image, translate it into German, and schedule it for Monday" stops being a feature request. It becomes a sequence of tool calls an agent makes against your content, with the same permission checks and versioning a human edit goes through.
The CMS turns into something an agent can operate, not just a UI a human clicks through.
It works because the architecture was structured-first from the start. When content is typed and the operations are explicit, an agent has something solid to act on. That's the upside of not storing everything as a wall of HTML.
Scaling, and the boring parts that matter
The same install runs on a single SQLite file for a brochure site and on a database cluster for millions of pages. It's Octane-friendly and built for cloud-native deployment.
Search reflects that range. A custom Laravel Scout engine speaks the native dialect of whatever database you're on: FTS5 on SQLite, MATCH … AGAINST on MySQL, tsvector on PostgreSQL, CONTAINSTABLE on SQL Server. No separate search cluster for something the database already does.
Multi-tenancy is single-database by design. Every model is scoped by a tenant_id through a global Eloquent scope, resolved by the Laravel tenancy package you probably already use. Multi-domain routing is a config flag. Real-time collaboration is opt-in over Laravel Reverb, broadcasting compact, metadata-only events so two editors don't clobber each other.
The security posture is the kind you'd rather inherit than assemble: user content sanitized through HTMLPurifier, CSP headers enforced, hCaptcha on forms, URL validation on submitted links, and rate limiting across the API.
Migrating in? There's a WordPress importer that moves a site over with an Artisan command, plus per-tenant backup and restore with integrity checks.
Want to charge for content? A separate aimeos/pagible-cashier package wires Pagible into Laravel Cashier, so you can put pages behind a paywall or sell course access through Stripe, Paddle, or Mollie. Its installer prompts for a provider, writes the credentials, and adds the Billable trait to your user model. Like the rest, it's opt-in — install it only if you need it.
Who it's for, and the honest caveats
Pagible fits when you have a Laravel app and want the people who own the content to manage it without you handing over the codebase. The headless APIs, structured content, and MCP tooling make it a solid base for anything where content feeds more than one front end.
Two things to weigh. It's young and moving fast, and the AI features need third-party API keys, which means accounts and may need a budget if you want the full set.
It's MIT licensed, so you can use it in commercial work with no per-site fees and no lock-in.
None of the caveats change the central pitch, which is the thing I keep coming back to: it installs into your app instead of asking your app to live inside it. If you've ever lost a weekend to a headless WordPress proxy, that alone is worth the composer require.
The code and docs are at pagible.com and on GitHub. If you try it, I'd like to hear which part of the workflow surprised you.