Skip to content

Why we built a WordPress-shaped CMS for Laravel ​

If you write Laravel for a living, you have probably been handed a WordPress site. The client wants "the same site, but in Laravel". It has ten years of posts, an ACF setup somebody built in 2016, a WooCommerce shop, a contact form whose messages the sales team still reads, and URLs that Google has known for a decade.

The code is the easy part. The data is what eats the budget.

The itch ​

Every time, a rewrite ran into the same wall. We had to design new tables for content that already had a perfectly good shape, then write an importer for wp_postmeta and for options stored as PHP serialize(). After that came ACF's field references, WooCommerce's order tables, and the redirect rules from an SEO plugin. Then we had to test that importer on the real database, freeze the content, run it one last time, and chase the URLs that came out different.

At some point the obvious question came up: why move the data at all? WordPress's schema has held up for twenty years. What people want to leave is the code around it, not wp_posts.

What we considered ​

  • Rewrite and import. The usual way. It works, but it is a project of its own, and the import is the part that loses things.
  • Headless WordPress. It keeps the data and puts Laravel or a JS front in front of the REST API. But WordPress, its plugins and their updates are all still there.
  • Eloquent models over the WordPress tables. There are packages that do this, and they are a good start. But a site is more than models. It also needs permalinks, the main query, canonical redirects, the template hierarchy, sign-in with WordPress passwords, roles, menus and widgets, and the plugins' data.
  • An existing Laravel CMS. Statamic, Filament-based CMSs and others are good products with their own content models. For a WordPress site, that means the import again (kitloom vs Statamic goes through where each one fits).

What we built ​

kitloom is a Laravel CMS whose storage is the WordPress database. Point it at one and run php artisan migrate. That adds kitloom's own tables and leaves WordPress's rows as they are. The same site then answers at the same URLs, and users sign in with the passwords they had.

Inside, there is no WordPress code: no hooks, no wp-admin. It is ordinary Laravel:

  • Eloquent models over the WordPress tables. The models speak Laravel names ($post->title, $post->field('price'), Settings::get()). Writers fill slugs and dates and check permissions, and events fire on publish, trash and delete.
  • Requests handled the way WordPress handles them. Rewrite rules come from the permalink settings, then the main query and the template hierarchy. The themes are Blade, Vue or React through Inertia. Every page can also answer as JSON or Markdown.
  • Modules that read plugins' data in the plugins' own formats. ACF for custom fields, Contact Form 7 and Flamingo for forms, WooCommerce for the shop, Rank Math for SEO and redirects, and others.
  • Two admins, Filament or Nova, with the same core and modules. Every module is a Composer package with an admin for each.
  • An MCP server, so an AI agent can work on the site. The agent has the rights of the user it acts as.

The hard parts ​

  • Serialized PHP everywhere. kitloom reads it (never as objects) and writes JSON from then on. That is also why the move is one way: WordPress does not read the JSON back.
  • The edges of WordPress's URL handling. Pages nested under pages, attachment pages and paged archives behave as WordPress does, and so does the canonical 301 from ?p=123. We compare kitloom with WordPress page by page on the same database, and that script is now part of the migration guide.
  • Plugin formats that are documented nowhere but in the plugin's code. We rewrote each one from the data model, without copying plugin code. We studied what the plugin writes, saved those rows as fixtures, and tested against them.
  • Not becoming WordPress. It is tempting to rebuild hooks. We used Laravel's own tools instead: contracts, container tags and events.

What it is not ​

  • It does not run WordPress plugins or themes. A plugin's data comes along when there is a module for it.
  • It has no block editor. Existing blocks are drawn, and new content is written in a visual editor.
  • It is not a CMS with no WordPress in it. The tables are WordPress's, and we say so on the home page.
  • It is not free. kitloom is a paid product, sold once with a year of updates and a 14-day refund. There are sandboxes to try it first. That pays for maintaining dozens of modules against plugins' data formats.

Try it, and tell us where it breaks ​

We are most interested in the sites where it does not work yet, such as a plugin you depend on or a URL that answers differently. That is how the next module gets chosen.

Last updated:

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]