bolt/core
Bolt is a lightweight, Symfony-based content management system with flexible content types, Twig templating, taxonomies, menus, and a powerful admin UI. This core package provides the foundation for building and extending Bolt-powered sites.
Bolt is a Symfony-based CMS, not a Laravel package — the first real decision is how it sits relative to your Laravel app, because that choice constrains everything after it:
Minimal steps to a working instance:
composer create-project into its own directory (do not drop it into an existing Laravel composer.json — the Symfony/Doctrine dependency tree will fight you)..env, run the schema/migration step once./bolt for the admin UI and confirm the version reads 6.1.9.Where to look first, in order:
config/contenttypes.yaml — this is the schema-as-code of your entire CMS. Everything (admin forms, routing, storage, templates) derives from it. Read it before writing a line of PHP.config/taxonomy.yaml — categories/tags/tagsets; set these up early so editors self-serve.templates/ — Twig theme files, if you're using the full-stack mode.src/ only when you need an extension point; patching core is a tax you'll pay on every frequent patch release.First use case to validate: one content type, one listing query, one detail query, rendered in your app. If that feels clunky, change the integration mode now — not after 15 content types exist.
Content types are your schema, treat them like migrations. Commit contenttypes.yaml, review changes in PRs, and lint it in CI:
bin/console lint:yaml config
bin/console lint:twig templates
bin/console lint:xliff translations
bin/console doctrine:schema:validate --skip-sync
Mirror the field names into a Laravel-side DTO or cast layer so a renamed field fails a test instead of silently returning null in production.
Model by concern, not by page. article, author, category — never homepage or landing-page-v2. Compose pages in the consumer. This single habit decides whether the CMS is still pleasant at month six.
Wrap Bolt behind one service in Laravel. A BoltContentRepository (or similar) with typed methods per content type, rather than HTTP calls scattered through controllers. Put caching in that layer:
Cache::remember("bolt:article:{$id}:{$locale}", 300, fn () => $this->fetch($id));
Bolt reads are cheap but not free, and almost all your traffic is the same handful of records. Short TTL plus explicit invalidation on publish (webhook or a scheduled warmer) covers it.
Query deliberately. Bolt is Doctrine-backed; use Bolt's own query API rather than hand-rolling DQL. Filter by status and taxonomy at the source, not in PHP after the fact — you'll otherwise ship unpublished drafts to a listing page.
Editorial workflow is a config problem. Statuses, taxonomies and the admin UI's roles/permissions cover most approval flows. If they don't, that's an extension point (custom console command, Twig extension, event listener) — not a fork.
Auth: keep it boring. Don't try to make Bolt read Laravel sessions. Either keep two user tables (usually fine — editors are few) or front /bolt with a reverse proxy that enforces Laravel auth and injects a trusted header.
Deploy as immutable build output. Build admin assets in your pipeline, treat vendor/ and compiled public assets as artifacts, and run the schema/migration step as a distinct one-shot deploy phase — not per web node.
chore(deps)/chore(deps-dev) bump (axios, undici, ip-address, source-map-js, brace-expansion, compression, proxy-addr, shell-quote). No features, no deprecations, no breaking changes — a normal patch upgrade. But note these are JavaScript dependencies landing in Bolt's admin front end: if you vendor or rebuild compiled assets, rebuild after upgrading or you'll keep shipping the old bundles and wonder why the changelog didn't match your symptoms.^6.1, let patches flow, and read the changelog between minors — that's where behaviour moves.$model->save() after composer require is about something else entirely.bin/console lint:yaml config) → cache clear → record status/permissions → the query in your consumer. It's the first or second step nine times out of ten.vendor/bin/rector process, vendor/bin/ecs check src, vendor/bin/phpstan analyse, composer validate --strict — mirror this discipline in any extension or bridge code you write.How can I help you explore Laravel packages today?