Noma
React Native · Expo · Mobile CMS · 2026

CMS for React Native

Noma gives React Native teams an API-first content layer for iOS and Android apps with secure backend integration, offline-friendly delivery patterns, localization, and release-safe publishing.

Positioning

Why React Native teams choose Noma

Mobile apps need predictable APIs, small payloads, locale-aware delivery, and safe rollout controls. Noma provides structured content, assets, and publish lifecycle primitives without coupling content operations to app binary deployments.

This page is a practical architecture guide for React Native and Expo projects. It complements CMS for Next.js and the broader CMS for mobile apps page.

Architecture

Use a backend-for-frontend boundary

Keep all privileged CMS calls in a trusted server layer. Your React Native app should call a mobile API endpoint that returns app-safe payloads.

// backend/lib/noma.ts
import { createClient } from "@nomacms/js-sdk";
 
export function getNomaServerClient() {
  const projectId = process.env.NOMA_PROJECT_ID;
  const apiKey = process.env.NOMA_API_KEY;
  if (!projectId || !apiKey) {
    throw new Error("Missing NOMA_PROJECT_ID or NOMA_API_KEY");
  }
  return createClient({ projectId, apiKey });
}
// backend/routes/mobile-feed.ts
import type { Request, Response } from "express";
import { getNomaServerClient } from "../lib/noma";
 
export async function mobileFeed(req: Request, res: Response) {
  const noma = getNomaServerClient();
  const locale = String(req.query.locale ?? "en");
 
  const result = await noma.content.list("app_feed", {
    state: "published",
    locale,
    paginate: 20,
    sort: "created_at:desc",
  });
 
  const items = "data" in result ? result.data : result;
  res.json({ items });
}

This boundary lets you enforce auth, locale policy, response shaping, cache headers, and abuse protection in one place.

Mobile auth

Session and token handling

For Expo and React Native, store sensitive tokens in secure storage and avoid plain key-value stores for credentials. Keep OAuth exchanges and provider token verification on your backend.

// app/auth/token-storage.ts (Expo example)
import * as SecureStore from "expo-secure-store";
 
export const tokenStorage = {
  async get() {
    return SecureStore.getItemAsync("noma_access_token");
  },
  async set(value: string) {
    await SecureStore.setItemAsync("noma_access_token", value);
  },
  async clear() {
    await SecureStore.deleteItemAsync("noma_access_token");
  },
};

See official guidance for Expo authentication and Expo SecureStore.

Offline-first

Repository pattern for fast UX

A practical mobile default is local-first reads with background sync. Noma query controls like state, locale, pagination, and sorting fit this pattern well.

// app/data/feed-repository.ts
// Local-first read with background refresh.
export async function getFeed() {
  const cached = await localDb.feed.getAll();
  if (cached.length) {
    refreshFeedInBackground().catch(() => {});
    return cached;
  }
  return refreshFeedInBackground();
}
 
async function refreshFeedInBackground() {
  const res = await fetch("/mobile-api/feed?locale=en");
  const json = await res.json();
  await localDb.feed.replaceAll(json.items);
  return json.items;
}

For detailed Flutter-style offline architecture guidance that also maps to React Native repository design, see offline-first support.

Localization

Deliver multilingual app content

Query localized content through locale-scoped reads and translation-aware fetches. Use published snapshots for production content and reserve draft reads for controlled preview workflows.

Related guide: CMS for multilingual sites.

Release operations

Publish and rollback safely

Content can change more frequently than mobile binaries. Noma lets you patch draft content, publish intentionally, inspect immutable versions, and revert quickly during incidents.

// backend/content-release.ts
const noma = getNomaServerClient();
 
await noma.content.patch("app_config", configUuid, {
  data: { home_banner: "Spring update" },
});
 
await noma.content.publish("app_config", configUuid);
 
const versions = await noma.content.versions.list("app_config", configUuid);
// Roll back quickly if needed:
await noma.content.versions.revert("app_config", configUuid, versions[0].uuid);
Automation

React Native content ops with MCP

Use @nomacms/mcp-server and curated workflows from Agent Skills to automate repetitive schema, localization, and content QA tasks.

Checklist

Before shipping to production

  • Keep NOMA_API_KEY server-only and never embed it in app bundles.
  • Default mobile read paths to published state and explicit locale.
  • Use secure token storage for native auth sessions.
  • Define cache and sync policy for offline and weak-network behavior.
  • Test rollback playbooks for content incidents before launch.

SDK setup · Project Auth · Headless CMS overview

Now available

Start building with Noma

Create a free account, spin up a project, and ship structured content with our API, SDK, and AI tools.