How to Connect a Headless CMS to Your React App
Web Dev
September 1, 2026
8 min read
0 views

How to Connect a Headless CMS to Your React App

Connecting a headless CMS to a React app means fetching structured content over an API and rendering it as components. No more hardcoding copy into your JSX. The mechanics are simple: define a content model, call an endpoint, map the response to markup. The part that trips people up isn't the fetch call. Tutorials gloss over three decisions. Where the fetch runs. How draft content reaches a live page. What happens the day a field holds rich text instead of a string.

React remains the most-used JavaScript framework, with the State of JS 2025 survey putting adoption at 83.6%. A growing share of that base pairs React with a react js cms rather than hardcoding content. Why? A CMS lets a non-developer publish without a pull request. A marketer fixing a typo in a hero headline shouldn't need a code review and a deploy. This walkthrough covers the actual integration, not another platform comparison.

What Does "CMS with React" Actually Mean?

A cms with react setup is two systems talking over HTTP. The CMS stores content as typed fields (title, body, author, image) behind a REST or GraphQL API. React fetches that JSON and renders it into components. Nothing about React changes. You're replacing a static string or a local Markdown file with a network call.

The CMS doesn't care what renders the response. It could be React, Vue, or a native app. That decoupling is the entire pitch of a headless CMS over a template-based one like classic WordPress, where the CMS renders its own HTML.

Step 1: Define a Content Model

Before writing a single fetch, decide what shape the content takes. For a blog post, that's usually:

  • title (text)

  • slug (text, unique)

  • body (rich text or MDX)

  • publishedAt (date)

  • featuredImage (media reference)

Most CMS admin panels let you build this as a schema without writing migration code. The schema is what the API returns, so get the field names right before you start wiring components against them.

Step 2: Fetch the Content

This is where the first real decision shows up: where does the fetch run?

Client-side with useEffect (the old default)

The familiar approach: a component mounts with no content, a useEffect hook kicks off a fetch to the CMS endpoint, and the response lands in state once it resolves, triggering a second render. Until then, the component returns a loading placeholder.

It works, but it ships a loading spinner to every visitor and costs an extra render pass. Search crawlers that don't execute JavaScript see an empty page until the fetch resolves.

Server-side with React Server Components (the current default)

The newer approach flips the order: the component itself is an async function that awaits the CMS fetch directly and returns the finished markup in one pass, no state, no effect, no second render.

No hook, no loading state, no client-side waterfall. The HTML that reaches the browser already has the content in it, which is better for both SEO and first paint. If you're on Next.js App Router or another RSC-capable framework, this is the pattern to reach for first. Client-side fetching still earns its keep for content that updates after the page loads, like a live comment count.

Caching the Request

A blog post doesn't change every request, so refetching it on every page load wastes a round trip. Next.js caches fetch calls automatically inside Server Components and gives you two knobs worth knowing: next: { revalidate: 60 } for time-based revalidation, and on-demand revalidation via revalidatePath or revalidateTag triggered from a CMS webhook when content actually changes. The second approach is the one to reach for once you have real traffic — it means a page stays cached until the moment an editor publishes, instead of going stale on a timer or refetching content that hasn't moved.

Step 3: Render Rich Text Without Fighting It

This is the underused angle most integration guides skip entirely: how the CMS stores your body copy determines how much glue code you write.

Two common shapes:

HTML strings. The CMS gives you a raw HTML string like <p>Some <strong>bold</strong> text</p>, and you drop it into the page with React's dangerouslySetInnerHTML prop, which injects the markup as-is with no parsing or sanitizing on your end.

Fast to ship, but it locks you out of using real React components inside the body. Want an editor to drop in an interactive pricing table mid-article? Not without a custom parser.

Structured rich text (portable text, structured text, MDX). The CMS returns a tree or a document your renderer walks, swapping in real components per node type. Sanity's portable text and DatoCMS's structured text both work this way. MDX goes further. The body itself is Markdown with JSX embedded, so <PricingTable plan="startup" /> can sit directly in the copy an editor writes. React renders it the same as any other component.

The tradeoff: structured formats need a renderer that understands the schema, which is more setup than one dangerouslySetInnerHTML line. That setup cost pays for itself the first time a content editor asks for a custom block instead of a screenshot pasted into a paragraph.

Step 4: Wire Up Draft Preview

Editors need to see unpublished changes before they go live, without deploying anything. Next.js and SvelteKit both call this Draft Mode. The CMS opens a preview URL carrying a secret token, pointed at a route in your app built to handle it. That route checks the token against an environment variable, rejects the request with a 401 if it doesn't match, and otherwise enables draft mode and redirects the editor to the page itself, now rendering the unpublished version for that session only.

Skip this step and every content change requires a real publish just to eyeball it, which is the exact workflow a CMS is supposed to remove.

Step 5: Handle the Missing-Content Case

Every tutorial shows the happy path where the slug exists and the API returns 200. In production, a slug gets mistyped, an entry gets unpublished, or the CMS has a bad minute. Handle it explicitly instead of letting a blank page ship: check the response status before trusting the body. A 404 from the CMS should trigger your framework's own not-found handling, so the visitor gets a proper 404 page instead of a component crashing on undefined fields. Any other non-2xx status should throw, so it surfaces to an error boundary instead of silently rendering half a page.

Pair that with an error.js boundary (or your framework's equivalent) so a CMS outage degrades to a friendly retry screen instead of a stack trace in front of a visitor. This is a five-minute addition during setup and a much longer incident postmortem if it's missing when the CMS API has a bad deploy.

What Breaks When You Skip Content Modeling?

Teams that skip modeling usually end up with one flat "body" field holding everything. Author bio, related links, a rating, all crammed into HTML that should be structured data. Six months in, every content need becomes a regex against a giant blob instead of a typed field. Model the content once, up front, even for a one-page blog.

Do I Need Server-Side Rendering to Use a Headless CMS with React?

No. A create-react-app or Vite SPA can fetch client-side with useEffect or a library like TanStack Query and render fine. You give up the SEO and first-paint benefits of server rendering. That matters for public blog content. It matters far less for an authenticated dashboard, since that content isn't indexed anyway.

FAQ

Does a headless CMS require GraphQL?

No. Most offer both REST and GraphQL delivery. GraphQL lets you request exactly the fields a component needs in one round trip; REST is simpler to debug with a browser or curl. Pick based on team familiarity, not a rule.

Can I use a headless CMS with Create React App?

Yes, client-side fetching works the same regardless of build tool. You lose server rendering, so plan for a loading state and weaker SEO on public pages.

What's the difference between a react js cms and a traditional CMS?

A traditional CMS like classic WordPress renders its own HTML. It hands the browser a finished page. A headless setup returns JSON or structured content over an API, and your React app owns the rendering. That split is what lets the same content feed a web app, a mobile app, and a kiosk from one source.

How do I handle images from a headless CMS in React?

Most headless CMS platforms return an image as a reference object with a URL and metadata (width, height, alt text) rather than a raw file. Pass that URL straight into Next.js's <Image> component, or your framework's equivalent, so you get responsive sizing and lazy loading without extra work. Skipping this and dropping the URL into a plain <img> tag still works, it just leaves layout-shift and bandwidth savings on the table.

The Practical Starting Point

Model the content before you fetch it. Render server-side where the framework supports it. Pick a rich-text format that lets editors drop in real components instead of screenshots. A lightweight MDX-based headless CMS for React like Draftbase handles that last part by design. Components with typed props render straight through React and RSC. A content editor composes with real UI instead of copy-pasting HTML. Skip the platform comparison for now and get one post fetching correctly end to end. The pattern doesn't change much as the content model grows.

Loading comments...

Related Articles

Why You Should Consider Drupal Development for Your Small Business

Why You Should Consider Drupal Development for Your Small Business

Web Design Psychology 101: Create First Impression On Your Visitors

Web Design Psychology 101: Create First Impression On Your Visitors