Migrate an Astro site
Astro is islands-friendly by design, which fits Focus:CMS nicely. Each editable surface becomes a React island; the rest of the page stays as plain HTML.
1. Add React + Focus:CMS
npx astro add react
npm install focus-cms @supabase/supabase-js zustand fast-json-patch
2. Mount the provider once
src/components/FocusShell.tsx:
import { FocusCMSProvider, supabaseAdapter } from "focus-cms";
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
import.meta.env.PUBLIC_SUPABASE_URL,
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
);
export default function FocusShell({ children, contentKey }) {
return (
<FocusCMSProvider
adapter={supabaseAdapter({
client: supabase,
siteSlug: "your-site",
key: contentKey,
})}
isEditor={() => /* your check */}
requireEditQuery={false}
>
{children}
</FocusCMSProvider>
);
}
src/layouts/Base.astro:
---
import FocusShell from "../components/FocusShell.tsx";
const contentKey = Astro.url.pathname === "/" ? "home/index"
: `pages/${Astro.url.pathname.replace(/^\//, "").split("/")[0]}`;
---
<html>
<body>
<FocusShell contentKey={contentKey} client:load>
<slot />
</FocusShell>
</body>
</html>
3. Editable fields
---
import { RichEditableText, EditableImage } from "focus-cms";
const { hero } = Astro.props;
---
<section>
<RichEditableText path="heading" as="h1" client:visible>
{hero.heading}
</RichEditableText>
<EditableImage path="image" src={hero.imageUrl} alt="" client:visible />
</section>
client:visible hydrates the editor primitives lazily. client:load for the provider so it's available as soon as the page loads.
4. Raw HTML pages
For Squarespace-imported pages: mount <EditableRawHtml> as a React island inside an Astro layout. Same pattern as the HTML recipe, just hosted from Astro.
5. Toolbar + cross-key edits
Copy components/editor-toolbar.tsx and lib/cross-key-store.ts from the reference theme. Mount the toolbar once in the Base layout, hydrated with client:load.
Why Astro?
If your site is mostly content (a blog, a marketing site, a docs site) and you want the smallest possible JS payload, Astro is a great fit. Focus:CMS becomes the only JS-driven surface; the rest of the page stays zero-JS by default. Display-mode Lighthouse scores stay where Astro's defaults put them.
For dynamic apps (dashboards, calculators, multi-step flows), Next.js is the better stack — see the Next.js recipe.