Build-Time Hydration (Bake)
Fold published content into your build for instant first paint and SEO, with an opt-in deploy trigger that keeps it fresh.
By default, InlineCMS applies published content at runtime: the page loads, the SDK fetches the published overrides, and the content swaps in. Bake moves that work to build time — it pulls your project’s published snapshot and folds it into the compiled output, so the very first paint is published content with no API round-trip, and crawlers see real content in the HTML.
Two surfaces, one model:
- React / Vite — the build embeds the snapshot and the SDK hydrates from it instantly (no fetch, no flash). Best for app-shell sites.
- Static HTML — the
inlinecms-bakeCLI rewrites your.htmlfiles with the published content folded directly into the markup. Crawler-visible, no JS required.
A snapshot is a cache, never a source of truth: editors always fetch live (to see drafts), and a missing or stale snapshot transparently falls back to the runtime layer. The guiding invariant is that edited content can never break your build — every failure ships a working, no-bake build.
React / Vite
Section titled “React / Vite”Turn it on in the plugin:
import { inlineCMS } from '@inlinecms/vite-plugin'
export default defineConfig({ plugins: [ inlineCMS({ apiKey: 'your-key', apiUrl: 'https://cms.yourdomain.com', bake: true, // ← fetch the published snapshot at build and embed it }), react(), ],})Now vite build (never the dev server) fetches the snapshot, injects it as an
inline <script> before your app bundle, and writes a build report:
dist/ index.html # contains <script id="inlinecms-snapshot">…</script> inlinecms-build.json # what was baked (version, counts, pages) assets/…At runtime, an unauthenticated visitor hydrates every store from that embedded
snapshot with zero /v1 requests — instant published content, and the page
still renders if the API is unreachable. An editor (signed in via handoff)
ignores the snapshot and fetches live so they see drafts.
The compiled HTML carries the snapshot data, but a client-rendered app still paints via JavaScript. For crawler-visible static HTML from a React app you need a prerender/SSG step; for plain pages, use the static-HTML baker below.
Static HTML
Section titled “Static HTML”For bare-HTML sites (the @inlinecms/loader drop-in), the inlinecms-bake CLI
folds published content straight into your markup:
npx inlinecms-bake index.html \ --out dist \ --api-key icms_your_key \ --api-url https://cms.yourdomain.com \ --page /Given a page like:
<h1 inline-cms inline-cms-key="hero-title">Authored heading</h1><div inline-cms-component="gallery" inline-cms-key="hero"></div>…the output dist/index.html has the published values in the markup:
<h1 inline-cms inline-cms-key="hero-title">Wake up where the garden meets the sea.</h1><div inline-cms-component="gallery" inline-cms-key="hero"> <div class="inlinecms-hero">…rendered tiles…</div></div>It resolves each element’s key with the same precedence as the loader
(inline-cms-key → build fingerprint → structural fallback), renders built-in
components (galleries) to static markup, re-sanitizes the content server-side,
and injects the snapshot <script> so the loader still attaches for editing
(and runs as a baked visitor — no API calls). Elements with no published
override keep their authored content as the fallback.
CLI options
Section titled “CLI options”| Option | Description |
|---|---|
--out <dir> | Output directory. Default: write <name>.baked.html beside the input. |
--api-url <url> | API base. Env: INLINECMS_API_URL. Default: https://api.inlinecms.com. |
--api-key <key> | Project API key. Env: INLINECMS_API_KEY. Required. |
--page <path> | Page path used for content lookup, applied to all inputs. Default: /. |
--metadata <file> | Build-report path. Default: <out>/inlinecms-build.json. |
--no-inject | Don’t inject the runtime snapshot <script>. |
--no-bake | Force a no-bake pass (copy inputs through unchanged). |
The CLI never fails your build on an expected error: a disabled bake, an
unreachable API, or the server kill-switch all copy the inputs through unchanged
and exit 0. It exits 1 only on a usage error (missing key, unreadable file).
Keeping it fresh: deploy triggers
Section titled “Keeping it fresh: deploy triggers”Baked content is captured at build time, so it only updates when you rebuild. Configure a deploy trigger and InlineCMS fires your existing pipeline on every publish (debounced, so a burst of edits coalesces into one deploy).
Set hooks on the project (admin only):
PATCH /v1/projects/<id>Authorization: Bearer <token>
{ "deployHooks": [ { "id": "prod", "type": "generic", "url": "https://api.netlify.com/build_hooks/abc123" }, { "id": "gh", "type": "github", "url": "https://api.github.com/repos/me/site/dispatches", "secret": "ghp_…", "eventType": "inlinecms-publish" } ]}generic— POSTs the URL as-is. Covers Netlify, Vercel, and Cloudflare Pages build hooks, where the per-project URL is itself the credential.github— sends arepository_dispatchwith a Bearer token, so a GitHub Actions workflow keyed on theevent_typeruns a rebuild.
Every trigger carries the pinned snapshot version so the resulting build is
traceable. Secrets are write-only — the API returns hasSecret: true but never
the value.
A matching GitHub Actions workflow:
on: repository_dispatch: types: [inlinecms-publish]jobs: rebuild: runs-on: ubuntu-latest steps: - run: echo "Rebuilding for ${{ github.event.client_payload.snapshotVersion }}" # …your build + deploy…The build report
Section titled “The build report”Every bake writes inlinecms-build.json so deploys stay traceable and rollbacks
stay honest:
{ "baked": true, "reason": "enabled", "snapshotVersion": "sha256:324ebbfc…", "generatedAt": "2026-06-05T21:46:37.497Z", "fetchedAt": "2026-06-05T21:46:37.436Z", "pages": ["/"], "counts": { "content": 7, "objectFields": 0, "objectCollections": 0, "objectTypes": 1 }}When baked is false, reason explains why (disabled, fetch failed, or the
server kill-switch) — no forensics required to answer “why isn’t static
updating?”
Turning it off (the kill-switch)
Section titled “Turning it off (the kill-switch)”Baking is opt-in and follows an “any off wins” precedence — it happens only if no layer disabled it:
| Layer | How to disable |
|---|---|
| Local option | bake: false (Vite) / --no-bake (CLI) |
| Local environment | INLINECMS_BAKE=off in the build environment |
| Server kill-switch | INLINECMS_BAKE=off on the API — forces safe-mode for every build, even ones you can’t redeploy |
In all cases the build still succeeds; content reverts to runtime application until the next successful bake.
How it works
Section titled “How it works”The published snapshot comes from GET /v1/snapshot:
your project’s published content grouped by page path, plus object overrides, a
deterministic version hash, and the server bakeEnabled flag. The same builder
backs the deploy trigger’s pinned version, so the value travels end to end.