A plugin-first CMS on Laravel 13 and React 19. Every feature is a plugin — including the storefront that renders the site.
composer create-project cogeze/cogeze'article.show' => [
'route' => ['pattern' => '/blog/:slug'],
'renderer' => 'blade',
'view' => 'storefront::article.show',
'needs' => [
'article' => [
'command' => 'article.findBySlug',
'args' => ['slug' => ':slug'],
'required' => true,
],
],
'seo' => 'ssr',
'seoFrom' => 'article.seo',
]Not a CMS with plugin support bolted on. The core does not know what an article is, what a product is, or whether the site has a home page. It knows there are plugins, that they talk over a command bus, and that one of them renders the site.
A plugin never touches another plugin’s data — the boundary is a named command, not a table. A data plugin does not render — URLs and views belong to a storefront plugin. And URLs live in the database, not in code.
// A data plugin returns arrays, never Eloquent models.
// It owns the query; the storefront owns the URL and the markup.
$commander->register('article.findBySlug', function (array $args) {
$a = Article::query()->where('slug', $args['slug'])->first();
return $a ? ['name' => $a->name, 'slug' => $a->slug] : null;
});Removing a plugin cannot break another one, because nothing imports across the boundary. Changing the entire look and URL structure of a site is enabling a different storefront plugin. Changing one page’s path is an edit in the admin, not a deploy.