Blog
Best headless CMS for React in 2026 when you need content API and user sessions
Search for react cms and most lists talk about Sanity Studio, Storyblok, or Payload living inside your repo. That helps when the hard problem is who edits content. It does not always help when the hard problem is who logs in to your app.
A React marketing site might only need published pages and assets. A membership app, customer dashboard, or logged-in community needs structured content and signup, login, and sessions for your users. This post is for that second case. It explains what "React CMS" usually means, how to fetch content from a React app (Vite, Create React App, React Router, or a thin backend), and when a separate auth vendor still makes sense.
What people mean by "React CMS"
The phrase shows up in three different conversations:
| Meaning | What you are really picking | Examples people mention |
|---|---|---|
| Editor experience | A dashboard marketers use to build pages | Storyblok, Sanity Studio, Prismic |
| Developer experience | Schema and admin live in your JavaScript repo | Payload, custom React admin panels |
| Content delivery | Any headless API your React app consumes | Sanity, Contentful, Strapi, NomaCMS |
React is a frontend. It does not require a CMS that ships a React-specific plugin. What it needs is a stable HTTP API, predictable JSON, and (if you have members) a clear auth story.
Most 2026 roundups score the first two rows: visual editing, TypeScript schema, real-time collaboration. That is fair for content-heavy marketing sites. If your app also has a members area, add a fourth checklist item: where end-user sessions live.
The auth question most roundups skip
Headless CMS products handle editor login. That is how your team opens the dashboard and changes entries.
Your React app often needs a different kind of login: project users (customers, members, subscribers) who sign up inside the product you ship.
Those are not the same credential:
| CMS editor access | End-user (project) auth | |
|---|---|---|
| Who | You, your team, client editors | People using your React app |
| Typical credential | Dashboard login or personal API key | Session access + refresh tokens |
| Purpose | Manage collections, entries, assets | Signup, login, profile, logout |
Many teams pick a strong headless CMS for content, then add Clerk, Auth0, Supabase Auth, or Firebase Auth for users. That works. It also means two products to configure, two docs to maintain, and glue code in your React app or API layer every time a token expires.
If logged-in users are part of the product from day one, ask upfront: is end-user auth bundled with the CMS, or always a second vendor?
How React apps usually talk to a headless CMS
You do not need a React-only SDK. You need JSON over HTTPS.
Option 1: REST from your backend (safest default)
Your React app calls your API. Your server calls the CMS with a personal access token that never ships in the browser bundle.
Every Content API request to NomaCMS uses:
https://app.nomacms.com/api
project-id: <your-project-uuid>
Authorization: Bearer <personal-access-token>
Accept: application/jsonCreate the token in User Settings → API Keys while the correct workspace is selected. Keep it on the server or in Edge functions, not in VITE_* env vars that end up in client builds.
Option 2: @nomacms/js-sdk in React
The official SDK wraps the same Content API with typed methods and consistent errors. Install it in a Vite or CRA app, or on a small Node backend:
npm install @nomacms/js-sdkimport { createClient } from "@nomacms/js-sdk"
const noma = createClient({
projectId: import.meta.env.VITE_NOMA_PROJECT_ID!,
apiKey: undefined, // do not put a full CMS key in the browser
})For public published content, prefer a backend route that holds the API key. For end-user auth, the SDK supports project user sessions in the browser when you configure token storage:
const noma = createClient({
projectId: import.meta.env.VITE_NOMA_PROJECT_ID!,
// apiKey optional here if you only call /auth/* routes from the browser
projectUserAuth: {
autoRefresh: true,
tokenStorage: {
getAccessToken: () => localStorage.getItem("noma_user_access") ?? undefined,
setAccessToken: (token) =>
token
? localStorage.setItem("noma_user_access", token)
: localStorage.removeItem("noma_user_access"),
getRefreshToken: () => localStorage.getItem("noma_user_refresh") ?? undefined,
setRefreshToken: (token) =>
token
? localStorage.setItem("noma_user_refresh", token)
: localStorage.removeItem("noma_user_refresh"),
clear: () => {
localStorage.removeItem("noma_user_access")
localStorage.removeItem("noma_user_refresh")
},
},
},
})
await noma.signInWithPassword({
email: "[email protected]",
password: "their-password",
})
const user = await noma.me()Auth methods (signUp, signInWithPassword, signInWithSocial, refreshSession, signOut, and others) use the same client. Social login accepts a provider id_token (Google today). See Project Auth overview and Installation & Setup for the full surface.
Important: A project user access token identifies a signed-in app user. It does not replace a personal access token for Content API routes. content.list() and other CMS methods always use your personal access token (apiKey), not the session token from login. Fetch published posts from a server route or pass apiKey only on the backend:
// Server only — never expose NOMA_API_KEY in Vite client bundles
const noma = createClient({
projectId: process.env.NOMA_PROJECT_ID!,
apiKey: process.env.NOMA_API_KEY!,
})
const posts = await noma.content.list("posts", {
state: "published",
locale: "en",
paginate: 20,
})Option 3: REST only (no SDK)
React Native and some minimal SPAs skip npm packages and call https://app.nomacms.com/api/auth/login for sessions and your own backend route such as GET /api/posts for published content. The same rules apply: server-side keys for content management, user tokens for session routes. The mobile apps guide walks through that pattern in more detail; the credential model is identical for React web clients.
When content and sessions in one platform helps
Consider a hosted headless CMS with project auth built in when:
- Your React app needs members, not just readers (profiles, saved items, gated pages)
- You want one API base (
https://app.nomacms.com/api) for content and/auth/*routes - You are a solo dev or small team and do not want to operate a CMS server plus a separate auth stack
- You might ship mobile later and want the same REST contract from iOS or Android
NomaCMS fits that shape: collections and fields in the dashboard, delivery over REST and @nomacms/js-sdk, and project auth for signup, login, refresh, logout, profile, sessions, email verification, and user-owned API keys. Every plan includes the REST API, SDK, MCP server, and project auth. Basic starts at $15/mo with a 7-day free trial.
That is not the only valid architecture. It is the path with the least vendor stitching when auth is a product requirement, not a future phase.
When Sanity, Payload, or Storyblok is the better buy
Name competitors fairly. You may still prefer another CMS when:
Sanity or Storyblok
Pick these when marketers need visual page building and real-time preview matters more than bundled end-user auth. Sanity's Studio and Storyblok's in-context editing are strong when non-developers compose pages daily. Plan for a separate auth product if your React app needs login.
Payload (in your repo)
Pick Payload when your team wants the CMS schema inside the Next.js or React monorepo, TypeScript types everywhere, and you are fine running the admin as part of your deploy. Payload's auth story is primarily CMS admin access, not a hosted multi-tenant end-user auth layer for your customers. Many teams still add Clerk or similar for app users.
Strapi or Directus (self-hosted)
Pick self-hosted options when data residency, custom plugins, or zero SaaS dependency is non-negotiable and you have someone to patch servers. Budget time for auth integration unless you build it yourself.
None of these are wrong answers. They optimize for different primary jobs.
A simple decision checklist for React teams
Before you commit:
- Who edits? Developers only, or marketers and clients too?
- Who logs in? Nobody, CMS editors only, or end users in your React app?
- Where do secrets live? API keys on a server you control, not in the client bundle?
- How many apps? One SPA today, or web + mobile sharing the same user accounts?
If only (1) matters, a visual or in-repo CMS from any major roundup is enough. If (2) is yes on launch week, shortlist platforms that ship project auth or accept the cost of a second auth vendor on day one.
Next steps
- JS SDK setup — install,
createClient, and token storage - Project Auth overview — routes, tokens, and how auth differs from Content API keys
- Headless CMS for mobile apps — same REST model for native clients
- Start a free trial — 7 days, no card required