A batteries-included, mobile-first React Native monorepo starter — Expo + Turborepo + pnpm, wired with NativeWind v5 (Tailwind v4) and React Native Reusables components that are pre-fixed to render correctly on native (not just web).
The point of rnstack is to skip the days normally lost to monorepo + NativeWind + RNR setup quirks. Clone it, install, run — you get a working app with 30+ UI components, theming, and light/dark mode out of the box.
Status: working starter you can clone today. A
create-rnstackCLI (scaffold by project name, choose how many apps), an API layer with refresh-token auth, and prebuilt Home/Settings screens are planned — see Roadmap.
Expo SDK 56 (New Architecture, RN 0.85) · Expo Router · NativeWind v5 (Tailwind v4) · React Native Reusables · TypeScript 6 · pnpm + Turborepo · Biome · Node ≥ 22.13.
apps/
mobile/ Expo app (Expo Router) — the reference app
packages/
ui/ @repo/ui — React Native Reusables components, theme toggle, cn()
config/ @repo/config — shared tsconfig base + Biome config
pnpm install
cp .env.example apps/mobile/.env # set EXPO_PUBLIC_API_BASE_URL
pnpm start # turbo run start → expo startThen open the app on a device/emulator (press a for Android, i for iOS, w for web).
This project targets Expo SDK 56. The Expo Go in the app stores may not be updated for SDK 56 yet ("Project is incompatible with this version of Expo Go"). Install the SDK-56 build directly from Expo: https://expo.dev/go?sdkVersion=56&platform=android&device=true, or build a dev client (
eas build --profile development), or run in the browser (w).
pnpm format # biome format --write .
pnpm lint # turbo run lint
pnpm typecheck # turbo run typecheckrnstack is build-tool agnostic — it ships with no EAS / cloud account baked in. Pick the path that fits you.
pnpm start # turbo run start → expo start; press a / i / wRuns in Expo Go (SDK 56 — see the note above) or the browser. This is all most contributors need.
Compile a real binary on your own machine — no account required:
cd apps/mobile
npx expo run:android # builds & installs a debug APK on a device/emulator
npx expo run:ios # macOS + Xcode
# release artifacts via prebuild + native tooling:
npx expo prebuild # generates ios/ & android/ (gitignored)
cd android && ./gradlew assembleRelease # → app/build/outputs/apk/release/*.apkEAS Build compiles on Expo's servers, so you can produce an installable APK/AAB (and iOS builds) with no local native toolchain. rnstack does not configure EAS for you — it's tied to a personal account, so each developer links their own:
npm i -g eas-cli # or: pnpm dlx eas-cli
eas login
cd apps/mobile
eas init # links YOUR Expo project; writes owner + projectId into app.json
eas build:configure # generates an eas.json with build profiles
eas build --platform android --profile preview # prints a download URL / QR for the APKOn the first Android build, answer yes to "Generate a new Android Keystore?" — Expo creates and
stores the signing key for you (no local keytool). Builds run asynchronously; press Ctrl+C after
it queues and re-attach with eas build:list.
⚠️ Monorepo gotcha: run everyeascommand fromapps/mobile/(whereapp.jsonlives), not the repo root — at the root the CLI links the wrong project and writes a stray rooteas.json. Theowner/extra.eas.projectIdthateas initwrites are yours — they belong in your own copy, and are safe to commit in a private app repo (they're public identifiers, not secrets). The starter intentionally ships without them.
Theme lives in one place: apps/mobile/src/global.css. It has
three parts:
- Token values —
@layer base { :root { --primary: hsl(…); } }plus a@media (prefers-color-scheme: dark)block. Edit these values to re-brand. - Utility registration —
@theme inline { --color-primary: var(--primary); … }tells Tailwind v4 to generatebg-primary/text-primaryetc. - Content sources —
@source "../../../packages/ui/src/**/*.{ts,tsx}"so Tailwind scans the shared UI package (omit it and shared components render unstyled).
Components reference semantic tokens (bg-primary, text-foreground, …) — never literal
colors. Dark mode follows the system scheme; toggle it at runtime with the ThemeToggle
component (Appearance.setColorScheme()).
Native theming rules (each fixes a bug that only shows on device):
- Store tokens as full colors (
hsl(0 0% 9%)), not bare channels — channel tokens + opacity modifiers (bg-primary/90) flicker on theme change on native.- Radius tokens must be concrete rems (
0.5rem), notcalc(var(--radius) - 2px)— nestedcalc(var())collapses to 0 (square corners) on native.
Components come from the React Native Reusables CLI into packages/ui:
cd packages/ui
npx @react-native-reusables/cli@latest add <name> -y
# or all of them:
npx @react-native-reusables/cli@latest add --all -ycomponents.json aliases route them into @repo/ui. After adding, audit for native gotchas
(no CSS grid on native, Android TextInput clipping, web-only utilities) — see the
rnstack-project skill in .claude/skills/ for the
full checklist.
pnpm-workspace.yaml sets nodeLinker: hoisted. pnpm's default isolated store loads two copies
of react-native into one bundle (a Maximum call stack size exceeded crash at startup);
hoisting guarantees a single native runtime. This is Expo's recommended linker for monorepos.
Planned, not yet built:
create-rnstackCLI — scaffold a new monorepo by project name in one command.- Multi-app generation — choose how many apps to create under
apps/at init. - API layer — minimal data fetching with refresh-token auth (TanStack Query).
- Starter screens — Home and Settings, pre-wired.
ISC © Sanjay Kumar Sah