Embed MDCMS Studio and the content SDK into your own application
Wire MDCMS into a real application you own. This guide covers host app responsibilities, Studio embedding, authentication, and production configuration.
Need a running server first? See Self-Hosting. Need to
install the CLI and define your schema? See the Quick
Start.
Your mdcms.config.ts (created by mdcms init) drives both CLI sync and Studio embedding. The fields that matter for integration:
mdcms.config.ts
import { defineConfig, defineType } from "@mdcms/cli";import { z } from "zod";const BlogPost = defineType("BlogPost", { directory: "content/blog", localized: true, fields: { title: z.string().min(1), slug: z.string().regex(/^[a-z0-9-]+$/), },});export default defineConfig({ // Core runtime fields used by StudioEmbedConfig: project: "my-site", environment: "production", serverUrl: "https://cms.example.com", // Optional — locales are not part of StudioEmbedConfig but are // used by Studio when available on the full MdcmsConfig: locales: { default: "en", supported: ["en", "fr"], }, // Schema and sync config: contentDirectories: ["content"], types: [BlogPost],});
See the Schema Guide for field types, references, and environment overlays.
Studio is a React component that owns all rendering under a catch-all route (e.g., /admin/*). The recommended pattern splits server and client concerns so the config can be safely serialized across the boundary.
Use this when you do not register custom MDX components.
1
Create the server component
The server component imports your config, strips non-serializable values via createStudioEmbedConfig, and passes the result to the client component.
app/admin/[[...path]]/page.tsx
import { createStudioEmbedConfig } from "@mdcms/studio/runtime";import config from "../../../mdcms.config";import { AdminStudioClient } from "./admin-studio-client";export default async function AdminPage() { return <AdminStudioClient config={createStudioEmbedConfig(config)} />;}
2
Create the client component
The client component renders Studio. The basePath prop tells Studio which URL prefix it owns.
app/admin/[[...path]]/admin-studio-client.tsx
"use client";import { Studio, type MdcmsConfig } from "@mdcms/studio";export function AdminStudioClient({ config }: { config: MdcmsConfig }) { return <Studio config={config} basePath="/admin" />;}
basePath is required. Studio cannot infer its mount prefix from deep links
like /admin/content/posts/abc. Set it to the path prefix your catch-all
route uses.
Other React frameworks: The pattern is the same for any framework — create a catch-all route that renders the <Studio> component. In Remix, use a splat route (admin.$.tsx). In Vite with React Router, use a wildcard route (/admin/*). The key requirement is that Studio receives all sub-paths under its mount point.
If your app registers custom MDX components for the Studio editor (e.g., <Chart>, <Callout>, <PricingTable>), use prepareStudioConfig on the server to extract TypeScript prop metadata at build time.
1
Register components in your config
Add component registrations to your mdcms.config.ts. Each component needs a name, importPath, and a load function. Optional: propHints for widget customization, propsEditor/loadPropsEditor for fully custom prop editing UI.
prepareStudioConfig reads your component source files and extracts TypeScript prop types so the Studio editor can generate form controls automatically.
app/admin/[[...path]]/page.tsx
import { resolve } from "node:path";import { prepareStudioConfig } from "@mdcms/studio/runtime";import config from "../../../mdcms.config";import { AdminStudioClient } from "./admin-studio-client";export default async function AdminPage() { const appRoot = process.cwd(); const prepared = await prepareStudioConfig(config, { cwd: appRoot, tsconfigPath: resolve(appRoot, "tsconfig.json"), }); // Extract only the serializable metadata for the client const componentMeta = prepared.components?.map((c) => ({ name: c.name, ...(c.extractedProps ? { extractedProps: c.extractedProps } : {}), })) ?? []; return ( <AdminStudioClient preparedComponents={componentMeta} schemaHash={ "_schemaHash" in prepared ? (prepared._schemaHash as string) : undefined } /> );}
3
Build the client config
The client component merges extracted prop metadata back onto the component registrations (which include the runtime load callbacks).
prepareStudioConfig reads your TypeScript source files at build time. It
requires cwd pointing to your project root and tsconfigPath to your
tsconfig.json. If your components are in a separate package, adjust these
paths accordingly.
Studio communicates directly with the MDCMS server API. Authentication determines how Studio identifies the current user.
Cookie (default)
Token
The default mode. Studio sends requests with credentials: "include" and the server manages session cookies. CSRF protection is automatic via the x-mdcms-csrf-token header.
<Studio config={config} basePath="/admin" />
Required server config: The MDCMS server must allow your host app’s origin for CORS and cookie delivery: