appdezign/lara-pro-cms
Lara Pro CMS is a powerful, extremely flexible content management system built on Laravel by Firmaq Media, a digital agency in Haarlem, the Netherlands. It offers a complete developer guide to help you build and customize sites quickly.
Day-one reality check. Everything you need to know is not in the repo. The GitHub repo carries a README (five lines) and release notes; the actual developer guide lives at docs.laracms.nl. Before you write a line of code, read that guide end-to-end, because the repo will not answer questions like "what tables does this own?" or "how do I register a content type?".
Minimal install sequence (verify each step against the docs — the exact provider/publish tags are not documented in-repo):
composer require appdezign/lara-pro-cms
php artisan vendor:publish --provider="..." # find the real provider name in vendor/
php artisan migrate
php artisan storage:link
npm install && npm run build # Tailwind 3.x + Vite
Then, before building anything, do the five-minute recon that saves a week:
# What does this package actually register?
grep -r "class .*ServiceProvider" vendor/appdezign/lara-pro-cms/src
php artisan route:list | grep -i cms
php artisan about
ls vendor/appdezign/lara-pro-cms/config
ls vendor/appdezign/lara-pro-cms/database/migrations
That gives you the service providers, route prefixes, config keys, and the schema you'll be living with. Dump the migrations into a schema map and keep it — you will refer to it constantly, because the release notes never mention schema changes.
Lock the dependency chain immediately. This package is pinned to Laravel 13 + Filament 5 + Livewire 4 and has pivoted framework tiers three times inside one minor version range (v10.3.0 Tailwind, v10.5.0 Filament 5/Livewire 4, v10.6.0 Laravel 13). Use exact constraints, not carets:
"appdezign/lara-pro-cms": "10.7.7",
"filament/filament": "5.x.y",
"livewire/livewire": "4.x.y"
First use case. Pick a single content type on a greenfield surface — a marketing page or a docs hub — not a replacement of an existing CMS. The realistic first deliverable is: one content type modelled in the admin, one Blade view rendering it, one published/unpublished state. That exercises the whole vertical slice (model → Filament resource → frontend route → cache) with the smallest possible blast radius.
Treat content types as Eloquent models plus Filament resources. That is the only architecture this package can reasonably have given the stack, and it's also your exit strategy: if your content lives in plain Eloquent models with normal relationships and accessors, a future migration away from the package is a data migration, not a rewrite. Actively avoid burying business logic inside Filament resource classes or package-specific field abstractions.
Model content the way you'd model any Laravel domain:
// Keep the query surface yours, not the package's
$page = Page::query()
->published()
->where('slug', $slug)
->with('blocks.media')
->firstOrFail();
If the package ships its own base model or trait, use it — but keep your application-facing query scopes and accessors in your own classes so the frontend never depends on the package's internals.
Cache the read path from day one. Livewire + Filament admin is chatty, but the frontend doesn't have to be. Wrap rendered content fragments in Cache::remember keyed on the model and a content version tag, and bust that tag from the model's saved/deleted events. This is the single highest-leverage thing you can do on this stack: it decouples public-site performance entirely from Filament's query patterns.
Use the admin panel as the editorial contract, not as an API. The "headless-ready" claim is a hypothesis, not a documented feature — nothing in the supplied artefacts describes the API's contract shape, pagination, auth, or versioning. Before promising any decoupled consumer (React/Vue/Inertia/mobile), run an explicit spike: hit the endpoints, record the actual JSON shape, check whether it's versioned, and write it into an OpenAPI stub yourself. If there's no usable API, serve content through Blade and let other services scrape your own HTTP pages — don't invent a contract you can't support.
Seeding and content migration is bespoke engineering. There is no documented importer. Expect to write an artisan command or a Filament importer that maps legacy records onto the package's tables. Budget it as real work (several days per content type), not as a tooling purchase.
Register your own Filament resources alongside the package's, in your own panel or in the package's panel if the docs allow extension. Keep a thin app/Filament/ layer so that when a Filament major lands you're only re-testing your own resources against the new API — not reverse-engineering someone else's.
Smoke-test suite as a hard requirement. Because release notes say "Several improvements," your only signal that an upgrade broke something is your own tests. Minimum viable coverage: admin CRUD for each content type, the publish/unpublish transition, and one frontend render per content type.
The release notes are the biggest day-to-day hazard. Thirty-plus releases in the sample, nearly all reading "Several improvements," including framework pivots. Practically: before every upgrade, diff vendor/ against the previous version rather than reading the changelog. git diff --no-index over two installed trees, or composer update on a scratch branch and review the vendor diff. Assume an hour minimum, a day-plus for anything touching Filament or Livewire majors.
Version pinning is not optional. A "Several improvements" release can be a silent breaking change to a Filament resource, a Tailwind class, or a migration. Pin exactly, upgrade deliberately, in a scheduled window.
Expect to maintain an internal fork. Zero stars, zero dependents, a single agency vendor. If a CVE lands in the admin panel or a defect blocks you, upstream turnaround is unknown. Keep a thin patch set or fork branch so you can ship a fix without waiting. Budget the ceremony: branching, rebasing on each upstream release, review.
Asset and styling conflicts are likely. Tailwind (v10.3.0) means config merge, content-path scanning, and possible purging collisions with your own design system. Check tailwind.config.js merge behaviour, Vite entry points, and whether the package publishes CSS you can't tree-shake. Pre-v10.3 themes/tutorials you find online are Bootstrap-era and now a rework liability — ignore them.
Auth and route collisions. The package owns an authenticated admin panel. Audit config/auth.php guards, middleware ordering, and whether the package's route prefix collides with your existing /admin. If you run Sanctum/Breeze/Jetstream, confirm the CMS panel doesn't hijack the web guard or the root layout. Test with your own auth stack installed, not on a bare skeleton.
No multi-tenancy. If you're an agency or building SaaS, isolation is yours to design (Spatie multitenancy + Filament panel scoping or global scopes). That's a product-scale commitment, not a flag. Decide before the pilot whether you need it, because retrofitting tenancy into a package that owns the schema is painful.
Admin performance needs a budget. Livewire's payload grows with table size. Test Filament tables at realistic volume (10k+ records), not seed data — enforce pagination, add DB indexes on the columns editors sort/filter by, and eager-load relationships in the resource's getEloquentQuery(). Editorial page-load time is your baseline metric; instrument it in the pilot.
Docs are off-repo, so cache them. The external guide is the only real documentation. When you discover how something works (extension points, field types, the API), write it down in your repo — a docs/cms-notes.md with the schema map, provider list, and the answers you had to dig out of vendor/. You will be the only source of that knowledge for the next engineer.
Know the exit cost before you commit. Content in package-owned tables plus deep coupling to Filament resource classes is expensive to leave. Keep content in plain Eloquent models, keep your frontend behind your own query layer, and the exit becomes a data migration rather than a rebuild.
How can I help you explore Laravel packages today?