Framework support

One CMS, every JavaScript framework

Draftbase ships native MDX rendering for React and Next.js, React Native, Astro, Remix, and Vue. One typed SDK, and the same compileMDX() API shape across all of them. Everything else talks to the same fetch-based SDK directly.

Updated

A drop-in renderer, not a data client you build on top of. React-based frameworks use @draftbase/renderer; Vue uses the separate @draftbase/renderer/vue entry point. Same compileMDX(source) call, same { ok, Content } return shape, no framework-specific mental model to relearn.

Next.js

Native

App Router and Server Components call cms.getEntry() directly at render time, then render it via <MDXContent>, with no client-side data-fetching layer needed.

React

Native

@draftbase/renderer’s <MDXContent> evaluates an entry’s MDX field and renders it as real JSX, with your own components dropped in via a components map.

React Native

Native

The same compileMDX() call from @draftbase/renderer. It’s plain React under the hood, so it runs unmodified with your app’s native components as the components map.

Vue

Native

compileMDX() from @draftbase/renderer/vue evaluates the same MDX into a real Vue vnode tree. No separate parser, same API shape as the React entry point.

Astro

Native

Compile server-side in the frontmatter, then hand the result to a small React island (@astrojs/react) to render.

Remix

Native

Plain React, no RSC required. Call compileMDX() from a loader and render the result.

Fetch-based SDK, no dedicated renderer

@draftbase/sdk on GitHub

These call the same typed, read-only delivery API through @draftbase/sdk, a plain fetch client with no browser-only or Node-only APIs, and no framework-specific renderer package. Flutter is the exception: Dart can't load a JavaScript SDK, so it calls the same REST delivery API directly.

Svelte / SvelteKit

SDK

No shared vnode model with MDX’s JSX runtime, so this one stays fetch-only: pull typed fields and an MDX string, render with toHtml() or Svelte’s own MDX tooling (e.g. mdsvex).

Angular

SDK

Fetch-only, since Angular templates compile to its own render functions rather than JSX vnodes. Call the SDK from a resolver or service, then render the MDX string with toHtml().

Flutter

SDK

Dart, so no JavaScript SDK. Call the REST delivery API directly with http or dio, and render the returned Markdown/MDX string with a Dart Markdown widget.

Node.js / Express

SDK

No UI to render. The SDK is a plain fetch client usable from any Node runtime for scripts, feeds, or server-side jobs.

Choosing a frontend framework for a headless CMS

Framework comparisons usually argue about syntax. For a content-driven site, syntax is the part that matters least — and if the word itself is doing heavy lifting here, start with what a framework actually is. What matters is the fetch boundary: where the request for content happens, and whether the visitor is waiting on it. Every framework here can render the same entry. They differ in when.

Three boundaries exist, and the ranking rarely changes. Fetch at build time and the content is already in the HTML, so a visitor waits on nothing — the Jamstack architecture is this boundary taken to its conclusion. Fetch on the server per request and the visitor waits once, on a connection between two data centres. Fetch in the browser after hydration and the visitor waits for the bundle, then the API call, then the render — three round trips deep, all of them on a phone network.

That is a Core Web Vitals decision, not a preference. Largest Contentful Paint is the vital that still fails most often: good LCP scores reached 62% of mobile pages in 2025, up from 44% in 2022, which leaves close to four in ten pages failing it. (Source) A client-side content fetch pushes the LCP element behind two waits it never needed. Interaction latency, by contrast, is rarely the problem on a content page — there is not much to interact with.

So the practical order for judging a framework: can it fetch content outside the browser, can it cache and revalidate that fetch without a redeploy, and how much JavaScript does it ship for a page that is mostly prose. Next.js, Astro, Nuxt, SvelteKit, and Remix all answer the first question yes. A plain client-side React or Angular SPA does not, unless you add a rendering layer.

Everything after that is team fit, and team fit wins more projects than benchmarks do. The framework your team already ships in has fewer unknown failure modes than the one that scored better in a comparison table. Judge on the fetch boundary first, then pick from whatever passes — the longer version of that call lives in how to choose a web framework, and the build-time end of the boundary in static vs. dynamic websites.

Using Draftbase with each framework

Ordered by how often teams ask for them. Every one of these calls the same read-only delivery API; what changes is how the MDX gets from that response into rendered UI.

Next.js

A Server Component awaits cms.getEntry() in its own body, then passes the rich text field to <MDXContent>. No data-fetching library, no loading state, and none of the SDK reaches the browser bundle. Cache with a route segment revalidate value or unstable_cache, and revalidate on a publish webhook when content has to appear immediately. The tradeoff is that this is the only framework where the renderer can await inside a component — everywhere else you compile in a loader.

React

Outside RSC, call compileMDX(source) from wherever you load data and render the Content component it returns. Your own components go in through a components map, so a <Callout> in an entry renders as your <Callout>. The tradeoff for a browser-only React app is the fetch boundary above: content arrives after hydration, so prefer a framework that can fetch on the server for anything public.

Vue headless CMS: Vue and Nuxt

@draftbase/renderer/vue exposes the same compileMDX(source) call and returns a real Vue vnode tree, not an HTML string. Call it in a Nuxt server route or an asyncData-style loader to keep the fetch off the client. Vue components map in by name the same way React's do. The one difference worth knowing: the Vue entry point is separate, so a React app never resolves Vue and vice versa.

Astro

Fetch and compile in the frontmatter block, which runs at build time for a static page or per request for SSR output, then hand the compiled content to a small React island via @astrojs/react. This is the cheapest fetch boundary available: content is in the HTML and the island exists only to render it. Two of the four example sites are built this way. Start from what Astro is if it is new to you, or wiring Astro to a headless CMS for the build itself.

Angular

Fetch-only, and that is a runtime fact rather than a gap in the roadmap: Angular templates compile to its own render functions, so there is no shared vnode model for MDX's JSX runtime to target. Call @draftbase/sdk from a service or a resolver, render the MDX string with toHtml(), and bind the result. Typed fields work exactly as they do elsewhere. Your own components can't render inside the content.

Svelte and SvelteKit

Same story, same reason: Svelte compiles to its own reactive primitives. Load typed fields in a SvelteKit load function, then either render the MDX string with toHtml() or run it through Svelte's own MDX tooling such as mdsvex if you want Svelte components inside content. SvelteKit's server-side load keeps the fetch boundary where it belongs.

React Native

Plain React underneath, so it runs unmodified in a native app. React Native has no intrinsic div or p, so a dedicated entry point, @draftbase/renderer/react-native, ships default Text/View/Image mappings — including a styled default EntryLink — so every standard markdown element renders out of the box. Wire up Text, View, and Image once via createReactNativeRenderer(); override or disable the default styling, or swap individual tags, only where you need to.

Flutter

Dart, so there is no JavaScript SDK to install and there will not be one. Call the REST delivery API directly with http or dio, then render the returned Markdown/MDX string with a Dart Markdown widget. You get typed content by construction — the schema still governs what the API returns — but the typing lives in your own Dart models, not in a generated client.

Framework support matrix

One answer to "does it work with X". Native renderer means content renders as components in that framework; fetch-only means typed content arrives and the MDX renders as HTML.

FrameworkNative MDX rendererTyped SDKWhere content is fetchedExample site
Next.jsYes, @draftbase/rendererYesServer Component, SSG or SSRStorefront, course platform
ReactYes, via compileMDX()YesYour loader or effect
Vue / NuxtYes, @draftbase/renderer/vueYesServer route or loader
AstroYes, via a React islandYesFrontmatter, build time or SSRPortfolio, blog
RemixYes, via compileMDX()YesLoader
Svelte / SvelteKitNo. toHtml() or mdsvexYesSvelteKit load function
AngularNo. toHtml()YesService or resolver
React NativeYes, default components built inYesYour loader or effect
FlutterNo. Dart Markdown widgetNo JS SDK. REST delivery APIYour own http/dio call

The renderer column splits on one technical fact, not on effort spent: React and Vue both accept a pluggable JSX runtime, and Svelte, Angular, and Flutter compile to their own. That is why the split is stable rather than a roadmap item.

How framework support works

Every framework talks to the same read-only delivery API, gated by an API key and separate from the management API used to author content. That single surface is what makes broad framework support possible without a renderer for each one.

Typed fields everywhere

@draftbase/sdk generates a typed client from your template schema, so entry.fields is typed the same way whether it's called from a Next.js Server Component or a Vue composable.

No framework-specific build

The SDK is plain fetch calls and plain objects. No browser globals, no Node-only APIs. It runs unmodified in a server runtime, a browser bundle, or React Native.

A shared JSX runtime, not a shared UI framework

React and Vue can both back compileMDX() because they each accept a pluggable JSX runtime. Svelte compiles to its own reactive primitives instead. Its entries stay a plain MDX string, renderable with toHtml() or Svelte's own MDX tooling.

Cacheable, revalidatable delivery

Reads are safe to cache at the framework's edge layer and revalidate on a webhook when an entry publishes, regardless of which framework fetched them.

Common pitfalls integrating a headless CMS with a frontend framework

The one that costs the most is fetching content from a client component with the API key in the bundle. Any visitor can read it, and a key that can read drafts can read them from anyone's browser. Keep the key in a server-side environment variable and fetch on the server, in a loader, or at build time. If a framework can't do any of those, it is the wrong framework for public content.

The second is having no revalidation strategy. Content gets fetched once at build, the site is deployed, and an edit never appears until someone redeploys by hand. Pick one of the two mechanisms up front: a time-based revalidate window, or a publish webhook that invalidates the affected path. Both are a few lines. Neither is something to retrofit after an editor complains.

The third is compiling MDX in the browser when it could be compiled once on the server. Compilation is the same work either way; doing it per visitor moves it onto the slowest machine involved and adds the compiler to the bundle. The fourth is treating the delivery API as a database and calling it from every component, which turns one page into a waterfall of round trips. Fetch what a page needs in one request, with include resolving references, and pass the result down.

All four share a root cause. The framework decides where code runs, and an integration written without deciding that first defaults to the browser, which is the one place you want neither the key nor the work.

Four working example sites

Each one is a public repo you can clone, seed and deploy in an afternoon. Two Astro, two Next.js, all statically built against the same delivery API — the API key stays in CI and never reaches the browser.

npx @draftbase/create my-site

Scaffolds any of these, logs you in and mints an API key. CLI source

Portfolio

Astro

The smallest useful integration. One query, two templates, static output.

  • Media fields
  • Rich text
  • Cursor pagination

Blog

Astro

Posts, authors and tags, with an RSS feed generated from the same content.

  • Reference fields
  • Entry tags
  • RSS + sitemap

Storefront

Next.js

A product catalogue with checkout on Stripe Payment Links. No cart server.

  • List-valued media
  • JSON fields
  • Server Components

Course platform

Next.js

Courses, modules and lessons, resolved two references deep in one request.

  • include: 2
  • Build-time search
  • Client state

Start building on your framework

Define a template once, fetch it from any JavaScript stack, and publish an entry in one sitting.

Hobby is free, no card. Startup is $49/mo when you outgrow it. The price is on the pricing page, where prices go.

No migration quarter, no kickoff workshop. Define a template and ship something today.

Frequently asked questions

Which frameworks does Draftbase render MDX for natively?

@draftbase/renderer ships a dedicated JSX-runtime integration for React, Next.js, Astro, and Remix (all plain React under the hood); a separate entry point, @draftbase/renderer/react-native, for React Native, with default Text/View/Image mappings built in; and a separate entry point, @draftbase/renderer/vue, for Vue. All three expose the identical compileMDX(source) API ({ ok: true, Content } or { ok: false, error }), so switching frameworks means changing the import, not the calling code.

Is there a Svelte CMS renderer?

Not a dedicated one, and there isn’t likely to be. Svelte compiles to its own reactive primitives rather than JSX vnodes, so there’s no shared runtime to hook into the way React and Vue share one. @draftbase/sdk still returns typed fields and a plain MDX string, renderable with toHtml() or Svelte’s own MDX tooling (mdsvex).

Does adding Vue support make my React bundle bigger?

No. @draftbase/renderer and @draftbase/renderer/vue are separate entry points that each import only their own framework, so a React app never resolves Vue, and vice versa. Both react and vue are optional peer dependencies.

Can I use Draftbase as a headless CMS for Angular or Flutter?

Yes, in fetch-only form. Angular calls @draftbase/sdk from a service or resolver and renders the MDX string with toHtml(), since Angular templates compile to their own render functions rather than JSX vnodes. Flutter is Dart, so there is no JavaScript SDK to install: call the REST delivery API with http or dio and render the returned Markdown with a Dart Markdown widget. Neither gets a dedicated renderer package.

Do I need a specific Node.js version or framework to use the SDK?

No. @draftbase/sdk is a plain fetch-based client with generated types from your template schema. It runs in any modern Node.js runtime, in a browser, or in React Native, independent of framework.

What is the best frontend framework for a headless CMS?

The one your team already ships in, as long as it can fetch content outside the browser. That single capability is what separates the options in practice: fetching at build time or on the server puts content in the HTML, while fetching after hydration makes a visitor wait for the bundle, then the API call, then the render. Next.js, Astro, Nuxt, SvelteKit, and Remix all fetch on the server or at build time. A browser-only React or Angular SPA does not without a rendering layer added. Judge candidates on that fetch boundary, on whether content can be revalidated without a redeploy, and on how much JavaScript ships for a page that is mostly prose — then pick whichever passing framework your team knows best.

Does a headless CMS work with any frontend framework?

Yes, if it exposes a plain HTTP API. Draftbase serves published content over a read-only, key-gated REST delivery API returning JSON, so anything that can make an HTTP request can read it, including Dart, Go, or a shell script. What differs per framework is rendering rich text: React, Next.js, React Native, Astro, Remix, and Vue get a native MDX renderer that turns an entry into components, while Svelte, Angular, and anything non-JavaScript render the MDX string as HTML with toHtml() or their own Markdown tooling.

Can I use Draftbase with a static site generator like Astro?

Yes. Compile MDX at build time inside an Astro frontmatter block for a fully static page, or at request time for SSR output, then render it through a React island.

Related reading

Go deeper on Frameworks