A React CMS delivers content your app can render without a custom parser
It ties content storage to React's component model, not a generic block editor. A blog post or landing page ships as JSX your app already knows how to render. Draftbase is a React CMS built on MDX. It has a typed SDK and a drop-in renderer for Next.js. No proprietary rich-text format sits between the content and the component tree.
import { cms } from "@/lib/cms/client";
import { MDXContent } from "@draftbase/renderer";
const post = await cms.getEntry<BlogPostFields>(id);
// post.fields.content is typed MDX
<MDXContent source={post.fields.content} />`getEntry` is generated from your template schema, so `post.fields.content` is a typed string, not `any`. `<MDXContent>` evaluates it and renders straight to React.
What is a React CMS
A React CMS hands content back to your app as data. A React component can render it directly. That's instead of an HTML fragment, or some proprietary block format. The distinction matters, since a React app already renders UI as a tree of components. A CMS that only outputs HTML strings forces a choice. Trust that HTML with dangerouslySetInnerHTML. Or write a parser that turns it back into components. Neither option is built for React. Both treat React as just another template target, not the real rendering layer.
Most CMS products predate React. They were built to output whole pages, not small reusable pieces. A React CMS closes that gap. It stores content in a format. Usually MDX, or a tree of JSON nodes. That format maps onto JSX with no translation step. Fields on an entry become typed props. A rich text field becomes a component tree your renderer already knows how to walk.
There's a rendering-time win here too. MDX compiles down to real JSX. A page built on a React CMS renders as real server-rendered markup on first paint. It's not a client-side widget waiting on a script to fetch and load content. A blog post or a docs page shows up as real markup right away. A browser or a crawler can read it fast. The same way a hand-written component would. There's no loading skeleton standing in for content while a script fetches it.
This matters for most frontend projects today. React remains the most-used JavaScript framework. 81.1% of developers use it. (Source) A CMS built around how React actually renders serves that majority directly. It doesn't treat React as just one output target among several.
Two storage formats show up in practice. Some React CMS products store a component tree as JSON. That's an array of nodes with a type, props, and children. Hand-built from a visual editor. Others, Draftbase included, store MDX: plain Markdown with embedded JSX. MDX has an edge here. It's already a spec with parsers and editor tools built for it. Not a custom schema every product reinvents on its own.
React CMS vs. generic headless CMS vs. Next.js-only content
Teams building React content today usually pick one of three approaches. The first is MDX files committed straight to the repo. No database, no editor UI. Content ships with a deploy. This works fine until a non-developer needs to edit copy. Then every change needs a pull request. The second is a generic headless CMS with a React SDK layered on top. Content is delivered by API. But the rich text field arrives as a proprietary JSON blob. You still have to convert it to JSX yourself. That format usually looks like a tree of typed nodes: a paragraph node, a heading node, an embed node. Each vendor picks its own shape. The renderer you write for one CMS won't carry over to the next.
Draftbase is the third path. It keeps the git-free editing of a headless CMS. But it stores rich text as plain MDX, not a proprietary format. So <MDXContent> can render an entry the moment it's fetched. Component reuse works the same way it would in your repo. Drop your own <Callout> or <ProductCard> into an article. Same as an MDX file in your repo. Content stays editable outside a deploy.
None of these approaches is always right. Repo-committed MDX still fits a docs site run entirely by engineers. There, a pull request is the natural review step. A React CMS earns its place once marketing, support, or content teams need to publish. No developer needed in the loop. And the content still has to look like the rest of the product. Not an embedded iframe.
The generic-headless-CMS path often costs the most over time. Even though it looks fastest at the start. The first integration is a weekend of work. Install an SDK, map a few fields, ship a page. The renderer for the rich-text format keeps growing from there. It picks up a new case for every block type an editor adds. Until it's a small parser your team maintains forever.
How Draftbase implements React-native content
Draftbase's React support isn't a bolt-on SDK wrapper. It's the format content is stored in. Plus two packages that move it into your app in as few steps as possible. Each piece below covers one step. Getting an entry from the database onto the page as a typed, rendered component. Your team doesn't have to assemble that path from smaller parts.
MDX rendering built in
@draftbase/renderer ships <MDXContent>, an async component that evaluates an entry's MDX field and renders it. No separate MDX pipeline to wire up yourself. It accepts a components map, so your own React components render inline.
Typed fields, not any
@draftbase/sdk generates a typed client from your template schema. So entry.fields matches the fields you defined, not any. Add a field, regenerate. The type checker catches every place that needs updating.
Server components fetch directly
The SDK is a plain fetch client. A Server Component can call cms.getEntry() during render. No extra data-fetching layer needed. No client-side hook, no loading state to manage on the page.
Cacheable delivery API
Delivery reads are read-only and API-key gated, separate from the management API. They're safe to wrap in Next.js's fetch cache. Or revalidate on a webhook. A published entry updates the page without a redeploy.
85% of new React projects now start with Next.js. That's from the State of React 2025 survey, reported by Strapi. (Source) That default matters for a CMS's SDK. A fetch-based client with no client-only code runs unmodified inside a Server Component. A client built around browser globals doesn't. The same survey found 48% of React developers already use React 19 daily. 41% are still on React 18. (Source) Draftbase's SDK targets both. No version-specific build needed. It's just fetch calls and plain objects.
Draft content follows the same path. Entries hold a draft state and a published state. A Server Component that previews a draft calls cms.entries.get(id), the management-scoped read. Not the delivery-scoped getEntry. Both return the same field shape. So the same <MDXContent> call shows a live page and a draft preview. No second rendering path to maintain.
Schema changes flow through the same generated types. Add a field to a template in the dashboard. It's available the next time you run codegen. The generated interface picks it up as an optional field. Until it's marked required. There's no manual mapping file to update on the frontend. Say a component expects a field the schema no longer has. That shows up as a type error at the exact spot that needs fixing. Not as a blank spot on a live page.
React content approaches at a glance
Laid side by side, the tradeoffs come down to two things. Who edits content. How much rendering code your team has to maintain. The table below compares the three approaches from above. It shows the specifics that decide which one a team picks.
| Capability | Plain Next.js + Markdown files | Generic headless CMS + React | Draftbase |
|---|---|---|---|
| Content editor for non-devs | None | CMS's own UI, disconnected from repo | Drop-in editor via Draftbase dashboard |
| Type safety | Manual | None — fields are any | Generated types via SDK codegen |
| Rendering | You write the MDX pipeline yourself | You write a custom rich-text renderer | <MDXContent> ships built in |
| Component reuse in content | Native, since it's your repo | Not supported | Supported — MDX embeds your own React components |
The row that tends to surprise teams is type safety. It looks like a nice-to-have. Until a template gains a field mid-project. The generic-CMS column's any lets that change compile cleanly. It quietly breaks at runtime instead. Generated types turn that into a build-time error. The rendering row carries a similar hidden cost. A custom renderer isn't a one-time build. It's an ongoing job that grows every time an editor asks for a new content block.
Common pitfalls building a React CMS integration
The most common mistake: treating a CMS's rich text field as safe HTML. Safe enough to drop straight into the page. Most CMS output isn't sanitized for that. Doing it anyway opens a security hole the moment an editor pastes in untrusted markup. It also blocks component reuse. A raw HTML string can't render a live React component the way JSX can. MDX compiled through a real evaluator, like <MDXContent>, avoids both problems. It parses content as MDX, not trusted HTML. It only renders the components you explicitly pass in.
The second mistake is fetching content on every render with no caching. Say a page has a hero entry, a related-posts list, and an author record. That turns into a waterfall of API calls. One round trip per section. The third mistake is hand-writing TypeScript types for CMS fields. Then letting them drift from the real schema once a field gets added. That mismatch usually shows up as undefined on a live page. Generating types from the schema, the way @draftbase/sdk's codegen does, keeps the two in sync on its own. No one has to remember to update a type file by hand.
Start building with a React CMS
Define a template, drop <MDXContent> into a page, and publish an entry in one sitting.
Frequently asked questions
What is a React CMS?
A React CMS is a content management system that delivers content in a format React can render directly — usually MDX or a component tree — instead of an HTML string or a proprietary rich-text document. Draftbase is a React CMS: entries store MDX, and @draftbase/renderer ships a component that renders it, so there's no translation step between what an editor writes and what your app renders.
Does Draftbase work with Next.js App Router and Server Components?
Yes. The @draftbase/sdk client is a plain fetch-based client with no browser-only APIs, so a Server Component can call cms.getEntry() during render with no extra configuration or client-side data-fetching library. It works the same way inside a Route Handler or a Server Action.
Is Draftbase's templated?
Yes. @draftbase/sdk generates a typed client from your template schema, so entry.fields matches the fields you defined instead of coming back as any. Adding or renaming a field and regenerating the client surfaces every call site that needs updating as a type error.
Can I use my own React components inside CMS content?
Yes, through MDX. Pass a components map to <MDXContent> and any component name used in an entry — a <Callout> or <ProductCard>, for example — renders as that component, the same way it would in an MDX file in your repo. Content editors can drop in interactive UI, not just formatted text.
Do I need a separate rich-text renderer for React CMS content?
No. @draftbase/renderer ships <MDXContent>, which evaluates an entry's MDX field and renders it as React elements. There is no proprietary JSON format to write a custom walker for, and no separate markdown pipeline to configure in your app.