Skip to content

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-bake CLI rewrites your .html files 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.

Turn it on in the plugin:

vite.config.ts
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.

For bare-HTML sites (the @inlinecms/loader drop-in), the inlinecms-bake CLI folds published content straight into your markup:

Terminal window
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.

OptionDescription
--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-injectDon’t inject the runtime snapshot <script>.
--no-bakeForce 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).

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):

Terminal window
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 a repository_dispatch with a Bearer token, so a GitHub Actions workflow keyed on the event_type runs 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…

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?”

Baking is opt-in and follows an “any off wins” precedence — it happens only if no layer disabled it:

LayerHow to disable
Local optionbake: false (Vite) / --no-bake (CLI)
Local environmentINLINECMS_BAKE=off in the build environment
Server kill-switchINLINECMS_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.

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.

Was this page helpful? Your feedback goes straight to the docs team.