Real-estate CMS. Built with Laravel and Vue.
A CMS for a property consultancy. Block editor, listings, pages, blog, SEO. The demo runs as the fictional “Best Real Estate Consultant In Your City™”.
Live demo (opens in a new tab) Admin at /login: demo@example.com / password. Resets nightly.
- Stack
- Laravel 13, Inertia, Vue, Octane/FrankenPHP, Postgres
- Results
- 231 automated tests. 22 domain models, running in production. Atomic release switches. No downtime.
Problem
Small real-estate businesses publish a lot of near-identical content: listings, area guides, blog posts. Site builders are slow to write in, poor for search, and take nothing from your own scripts.
The brief: a CMS the business runs day to day, that ranks, and that accepts drafts from elsewhere, including AI.
Built
- Data model: 22 domain models behind listings, pages and blog.
- Authoring: Editor.js. Content is stored as structured blocks and renders the same on every public page.
- SEO: meta and JSON-LD on public pages.
- Import API: scripts push AI-written posts, pages and listings. They land as drafts for review.
- Zero-downtime deploys: release directories, atomic symlink switch.
Decisions
- Inertia instead of a separate SPA and API
- The admin is Vue. Routing, validation and auth stay in Laravel. One codebase, one deploy, no duplicated form rules. The import API is the only public JSON surface.
- Structured blocks, not HTML
- Editor.js stores JSON blocks. Rendering stays predictable, the import API can validate before saving, and AI drafts cannot inject arbitrary markup.
- Octane on FrankenPHP
- The app stays booted between requests. Public pages are fast with no page cache to invalidate on every save.
- Drafts first for anything imported
- Nothing from the API goes live on its own. Scripts create and update drafts. A person publishes.
Next time
Write the import API contract first, as a schema, and generate validation from it. It grew alongside the scripts that call it.
Add visual regression tests for public templates earlier. Unit and feature tests catch logic errors, not a broken layout.