Migrate a Node / Express site
Server-rendered Express apps can host Focus:CMS in two ways. The right choice depends on how much template logic lives in JS vs HTML.
Option A — React island per template
Keep your Express routes and EJS/Handlebars/Pug templates. Inject a single React island into each editable page that hosts <EditableRawHtml> and the editor toolbar.
-
Add React + Focus:CMS to your project:
npm install react react-dom focus-cms @supabase/supabase-js zustand fast-json-patch -
Build a small client bundle (
editor-island.tsx) that mounts on a target<div>in each rendered page:import { createRoot } from "react-dom/client"; import { EditableRawHtml } from "./components/editable-raw-html"; const host = document.getElementById("focus-cms-host"); if (host) { const bodyHtml = host.dataset.bodyHtml ?? ""; createRoot(host).render( <EditableRawHtml html={bodyHtml} htmlPath="blocks/0/html" /> ); } -
In your Express template, expose the page body as a data attribute and let the island take over in edit mode:
<div id="focus-cms-host" data-body-html="{{ pageHtml | escapeAttribute }}"> {{{ pageHtml }}} </div> <script src="/editor-island.js" defer></script>
The island stays inert until a Supabase Auth session is present.
Option B — Migrate page bodies to a static Next.js shell
Cleaner long-term. Move page content from Express templates into focus_cms_content rows. Serve the editable surface from a Next.js static export pointed at the same Supabase project. Keep the Express app for non-page logic (API endpoints, webhooks, dynamic forms).
The two apps can share a domain via Cloudflare:
yourdomain.com/*→ Next.js static export on Cloudflare Pagesyourdomain.com/api/*→ Express on a Worker (or wherever it currently lives)
See the Next.js recipe for the rest of the setup.
Cross-key edits
Either option uses the cross-key store for navigation, footer, and site meta. See the theme guide for the full pattern.
When to pick which
- Option A when most of your site's value is in JS-driven dynamic features (admin panels, calculators, forms) and the editable surface is small.
- Option B when most of your site is content pages with a small amount of dynamic behaviour. Migrating gets you free static-CDN performance, simpler ops, and a cleaner editor story.