Add the ContentGraph plugin alongside your framework's plugins:
import { contentGraph } from "contentgraph/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [contentGraph()],
});
Server imports and types
The plugin defaults to the in-memory backend. It writes .contentgraph/types.d.ts for editor types and delivers runtime content through Vite's virtual modules and production bundle. To also publish standalone runtime files for consumers outside Vite, use contentGraph({ backend: "generated" }). The standalone CLI and build API still default to generated storage.
Server modules import content from contentgraph:server. Include .contentgraph/**/*.d.ts in tsconfig.json for the generated collection types. The plugin rejects direct and transitive client imports of the server runtime.
Development updates
The plugin excludes generated content artifacts from Vite's watcher and preserves your configured ignores. It watches authored sources and configuration separately, so publishing a large collection does not trigger application file events for every generated payload.
Development delivers payloads in the same bounded, lazy batches used for builds. Concurrent delivery of a revision shares its serialization work.
Vite watches source and configuration changes. Connect the browser-safe onContentUpdate() subscription from contentgraph:client to your framework's data refresh:
import { onContentUpdate } from "contentgraph:client";
const unsubscribe = onContentUpdate(async () => {
await refreshPageData();
});
refreshPageData() stands for your router's refresh function. Register it in your framework's lifecycle hook, and unsubscribe on cleanup. The production subscription is a no-op.
See the framework guides for the server boundaries and refresh methods used by the examples.
Production builds
Content preparation runs in server environments. Client compilation still enforces the server import boundary and does not repeat content loading or Markdown compilation.
Before bundling, the plugin packs server payloads into lazy source modules targeting 128 KiB. This also avoids per-item facade modules in hosts that require strict entry signatures. Default metadata queries do not import bodies; rendering one entry can load neighboring payloads in its module. Your output and chunking settings continue to control the final bundle. Standalone generated artifacts remain ordinary per-item ESM modules.
Large metadata indexes and payload batches are compressed when gzip/base64 reduces their size by at least ten percent. Metadata is restored synchronously by a portable decoder. Payload batches are imported and expanded once on first body or render access, and failed loads can be retried. Rich metadata, heading aliases, and detached results keep the same value semantics.