Codesistency Full-Stack Expo React Native Build Method

Build and ship a production-ready, full-stack cross-platform mobile app using React Native, Expo, Clerk, NeonDB, Drizzle, NativeWind, and Zustand — from blank folder to authenticated, styled, database-connected app.

// TL;DR

The Codesistency Full-Stack Expo React Native Build Method is a technology-opinionated path for building and shipping a production-ready cross-platform mobile app from a blank folder. It combines React Native, Expo, Clerk (auth), NeonDB (Postgres), Drizzle (ORM), NativeWind (styling), and Zustand (state) — all on free tiers with no credit card required. Use it whenever you're starting a new React Native project from scratch and want a structured route through environment setup, file-based routing, social OAuth, database integration, styling, dark mode, and state management. It targets both iOS and Android from a single TypeScript codebase.

// When should you use the Codesistency Full-Stack Expo React Native Build Method?

Use this skill whenever you are starting a new React Native mobile project from scratch and need a structured, technology-opinionated path from environment setup through authentication, database integration, styling, and state management.

// What do you need before starting this React Native build?

  • App conceptrequired
    What the app does at a high level (e.g., grocery list manager, to-do app, social feed)
  • Target platformsrequired
    iOS, Android, or both
  • Auth providers desiredrequired
    Which OAuth providers to enable: Google, Apple, GitHub, Facebook, etc.
  • Screen listrequired
    The named screens the app needs (e.g., List, Planner, Insights, Auth)
  • Data entities
    The main data objects the app manages (e.g., grocery items, users, tasks)

// What are the core principles behind this build method?

Cross-Platform First

Write one codebase in TypeScript/JavaScript targeting both iOS and Android simultaneously via React Native. Never write platform-specific code unless absolutely necessary.

Expo Over Bare React Native

Always initialise with Expo rather than bare React Native. Expo is to React Native what Next.js is to React — same core, plus performance optimisations, file-based routing, API routes, and production-ready tooling.

File-Based Routing via the App Folder

Anything placed inside the special `app/` folder automatically becomes a screen/route. Use folders with `index.tsx` plus `_layout.tsx` for every route group so you can attach layout logic and navigation configuration.

Route Groups for Logical Separation

Wrap related screens in bracketed folders (e.g., `(auth)/`, `(home)/`) to create route groups. Each group gets its own `_layout.tsx` that handles redirection logic for that concern (e.g., auth guard, home guard).

Development Build Over Expo Go for Production Features

Expo Go is for simple learning projects. Whenever native modules are needed (OAuth sessions, crypto, push notifications, etc.) switch to a development build using `npx expo run:ios` or `npx expo run:android`. Delete the `ios/` or `android/` folder and rebuild whenever native module errors appear.

Custom Hooks for Business Logic

Extract reusable logic (e.g., social OAuth flow, loading state management) into dedicated custom hooks under a `hooks/` directory. Components stay thin; hooks own the async work and state.

NativeWind for Styling (Tailwind for React Native)

Use NativeWind v4 (not v5 — not stable) as the styling layer. Configure `tailwind.config.js` to scan the entire `src/` folder. Define light/dark CSS variables in `global.css` using `:root` and `.dark` selectors, and set `darkMode: 'media'` so the app follows the device colour scheme automatically.

Environment Variables in .env, Gitignored Always

All secrets (database connection strings, Clerk publishable keys) live in a `.env` file that is immediately added to `.gitignore`. Never commit credentials.

Free-Tier Stack Until Scale

The chosen stack (Clerk free tier: 50,000 MAU; Neon free Postgres; NativeWind; Zustand; Drizzle ORM) costs nothing until meaningful scale is reached. No credit card is required to ship a complete app.

// How do you build a full-stack Expo app step by step?

  1. 1

    Initialise the Expo project with the default template

    Run `npx create-expo-app@latest --template default .` inside an empty project folder. Check the video description for whether the `@next` flag is currently needed. Use TypeScript throughout — it provides type safety and is strongly preferred over JavaScript.

  2. 2

    Reset the project to a clean slate

    Run `npm run reset-project` and press 'No' when asked to preserve example files. This deletes all default template screens, components, and constants so you start from scratch without confusion.

  3. 3

    Add secrets to .env and gitignore immediately

    Create `.env` at the project root. Add `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY` from the Clerk dashboard and the Neon Postgres connection string. Immediately add `.env` to `.gitignore` before any commits.

  4. 4

    Set up NativeWind v4 (Tailwind for React Native)

    Follow nativewind.dev Get Started exactly: install packages, create `tailwind.config.js` at root, create `global.css` at root, create `babel.config.js`, create `metro.config.js`, import `global.css` inside the root `_layout.tsx`. In `tailwind.config.js` set `content` to scan all files under `./src/**/*.{js,jsx,ts,tsx}` — not just `components/`. Set `darkMode: 'media'`. Define your brand colour tokens as CSS variables in `global.css` under `:root` (light) and `.dark` (dark) selectors, then reference them in `tailwind.config.js` under `theme.extend.colors`.

  5. 5

    Configure the root `_layout.tsx` with providers

    The root layout lives at `app/_layout.tsx`. This is a special file — always named `_layout` with `.tsx` or `.jsx` extension. Wrap the `<Stack>` navigator with `<ClerkProvider publishableKey={...} tokenCache={...}>` and `<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>`. Import `useColorScheme` from React Native for the theme toggle. Import `global.css` here (adjust relative path: go up from `app/` to project root).

  6. 6

    Create route groups for Auth and Home with guard layouts

    Create `app/(auth)/` and `app/(home)/` folders (brackets make them route groups — they don't appear in the URL). Each needs a `_layout.tsx`. Auth layout: use Clerk's `useAuth` hook — if `!isLoaded` return null; if `isSignedIn` redirect to `/(home)`; else render `<Stack>`. Home layout: if `!isSignedIn` redirect to `/(auth)/sign-in`; else render `<Stack>`. This is the navigation guard pattern.

  7. 7

    Build the custom OAuth authentication screen

    Create `app/(auth)/sign-in.tsx`. Do NOT use Clerk's pre-built `<OAuthView>` for custom branding. Instead build your own UI with `<SafeAreaView edges={['top']}>` to prevent status bar overlap. Use a custom hook `useSocialOAuth` (see step 8) to handle the flow. Include a pressable button per provider (Google, Apple, GitHub) that calls `handleSocialOAuth('oauth_google')`, `handleSocialOAuth('oauth_apple')`, or `handleSocialOAuth('oauth_github')`. Show a loading state per button using `loadingStrategy` — if `loadingStrategy === 'oauth_google'` show a spinner/connecting text on that button only. Disable all buttons while any is loading.

  8. 8

    Create the `useSocialOAuth` custom hook

    Create `src/hooks/useSocialOAuth.ts`. Import `useSSO` from `@clerk/clerk-expo`. State: `loadingStrategy` (null | string). Logic: `handleSocialOAuth(strategy)` — if already loading, return immediately (guard against concurrent flows). Set `loadingStrategy = strategy`. In try/catch call `await startSSOFlow({ strategy })`. If no `createdSessionId` or no `setActive`, show an `Alert` with title 'Sign In Incomplete'. Else call `setActive({ session: createdSessionId })`. Finally block: reset `loadingStrategy` to null. Return `{ handleSocialOAuth, loadingStrategy }`.

  9. 9

    Switch to a Development Build when native modules are required

    Run `npx expo install expo-dev-client` once. Then run `npx expo run:ios` (or `run:android`). This builds a custom native app on your device/simulator — necessary for OAuth sessions, Expo Crypto, and any native module. Whenever you see 'no native module found' errors: delete the `ios/` folder and re-run `npx expo run:ios`. This is the standard fix. You only need to rebuild when adding new native packages; subsequent JS changes hot-reload normally.

  10. 10

    Set up Neon Postgres database and connect with Drizzle ORM

    Sign up at neon.tech, create a project, copy the connection string into `.env`. Install Drizzle ORM and the Neon serverless driver. Define your schema using Drizzle's schema syntax (tables, columns, relations). Run migrations. Use the connection string from the environment variable — never hardcode it.

  11. 11

    Build each screen inside the appropriate route group

    For every screen, create a folder inside `app/(home)/` (e.g., `list/`, `planner/`, `insights/`) with an `index.tsx` inside. Use `<FlatList>` instead of `Array.map` for any lists — it is more performant. Use `<TextInput>` instead of HTML `<input>`. Use `<Pressable>` instead of `<button>` or `<TouchableOpacity>` (Pressable is the modern approach). Use `onPress` instead of `onClick`.

  12. 12

    Add global state management with Zustand

    Install Zustand. Create stores under `src/store/`. Zustand is easy to initialise — create a store with `create()`, define state and actions. Import and use in components with `useStore`. No Provider wrapper needed.

  13. 13

    Implement tab navigation with platform-native styling

    Create a tab layout using Expo Router's `<Tabs>` component inside a nested `_layout.tsx`. For iOS liquid glass effect on tabs, apply platform-specific styles. Each tab maps to a screen folder. Configure tab bar icons using vector icons from `@expo/vector-icons` (FontAwesome, Ionicons, etc.).

  14. 14

    Add feedback/error reporting with Sentry

    Sign up at Sentry using the special link (provides $80 free credits). Install the Sentry Expo SDK. Use Sentry to implement a feedback button so users can report bugs or request features. Add Sentry to `app.json` under `expo.plugins`.

  15. 15

    Commit after each section and push to GitHub

    After every major section: stage all changes, generate a commit message, commit, and sync to GitHub. Create the repo as private during development; make it public when releasing source code. This is the cadence used throughout — commit after setup, after auth, after each screen.

// What are real examples of this method in action?

A solo developer wants to build a shared household task manager app for iOS and Android with Google and Apple sign-in.

Initialise with `npx create-expo-app@latest --template default .`, reset project, add Clerk keys and Neon connection string to `.env`. Set up NativeWind v4. Create `(auth)/` route group with sign-in screen using `useSocialOAuth` hook wired to `oauth_google` and `oauth_apple` strategies. Create `(home)/` route group with screens: `tasks/` (FlatList of tasks), `add/` (form with TextInput + Pressable submit), `profile/` (Clerk user info + sign out). Define a `tasks` table in Drizzle/Neon schema. Use a Zustand store for local task state. Build tab navigation across the three home screens.

A developer gets a 'no native module found' error after installing a new Expo package for push notifications.

This is the standard native module error pattern. Stop the dev server. Delete the `ios/` folder (and/or `android/`). Run `npx expo run:ios` to rebuild the development build with the new native module compiled in. Do not attempt to fix this through JS-only changes — the fix always requires a rebuild.

A developer wants dark mode to work automatically in their app.

In `tailwind.config.js` set `darkMode: 'media'`. In `global.css` define CSS variable colour tokens under `:root` for light mode and under `.dark` for dark mode (same variable names, different values). Wrap the root Stack with `<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>` using `useColorScheme` from React Native. Components using NativeWind classes like `bg-background` or `text-foreground` will then automatically switch between the light and dark CSS variable values without any additional logic.

// What mistakes should you avoid when building with Expo and Clerk?

  • Using Expo Go for features that require native modules (OAuth, Crypto, etc.) — always switch to a development build with `npx expo run:ios` / `npx expo run:android` when native modules are needed.
  • Installing NativeWind v5 — it is not stable; always use v4.
  • Setting the `content` array in `tailwind.config.js` to only scan `components/` — it must scan the entire `src/` folder so Tailwind works in screens, hooks, and all other files.
  • Importing `global.css` with a wrong relative path in `_layout.tsx` — the file is at the project root, so from inside `app/` you must go up two levels.
  • Committing the `.env` file to GitHub — always add it to `.gitignore` before the first commit.
  • Using `Array.map` to render lists instead of `<FlatList>` — FlatList is more performant for mobile and is the correct React Native pattern.
  • Using `<TouchableOpacity>` instead of `<Pressable>` — Pressable is the modern, preferred approach.
  • Not wrapping screens with `<SafeAreaView edges={['top']}>` — text and content will overlap with the device status bar.
  • Allowing concurrent OAuth flows — the `useSocialOAuth` hook must guard against this by returning early if `loadingStrategy` is already set.
  • Not rebuilding after installing native packages — JS hot reload does not pick up new native modules; a full rebuild is required.
  • Placing screens directly as files (e.g., `contact.tsx`) instead of as folders with `index.tsx` — file-only routes cannot have their own `_layout.tsx` for customisation.
  • Forgetting to add the Clerk plugin string to `app.json` under `expo.plugins` when using the development build with Clerk's native features.

// What key terms should you know for this React Native stack?

Cross-Platform Development
Writing one codebase in JavaScript/TypeScript that deploys to both iOS and Android — the core value proposition of React Native.
Expo
A platform built on top of React Native that adds extra features, optimised components, file-based routing, API routes, and production deployment tooling. Analogous to Next.js on top of React.
Development Build
A custom-compiled version of your app installed on your device or simulator via `npx expo run:ios` / `npx expo run:android`. Required whenever native modules are used. Replaces Expo Go for anything beyond simple apps.
Expo Go
The default Expo app from the App Store/Play Store used for simple learning projects. Cannot run custom native modules.
File-Based Routing
The system where any file placed inside the `app/` folder automatically becomes a screen/route. Folder structure = URL/navigation structure.
App Folder
The special directory (`app/`) in an Expo Router project where every file becomes a navigable screen. Central to the file-based routing system.
Route Group
A folder wrapped in brackets (e.g., `(auth)/`, `(home)/`) that groups related screens without affecting the navigation URL. Used to co-locate screens and share a `_layout.tsx`.
_layout.tsx
A special file within any route folder that wraps child screens with shared UI, navigation configuration, or redirect guards. Must be named exactly `_layout` with a `.tsx` or `.jsx` extension.
Stack Navigator
The `<Stack>` component from Expo Router that implements screen-stacking navigation — new screens slide in from the right on iOS, from the top on Android, and can be popped back like a stack of plates.
NativeWind
Tailwind CSS implemented for React Native. Allows using className with Tailwind utility classes directly on React Native components. Use v4 (v5 is not stable).
Clerk
The authentication platform used for user management and social OAuth. Provides hooks (`useAuth`, `useUser`, `useSSO`, `useClerk`), pre-built components, and secure token handling. Free tier supports 50,000 monthly active users.
Strategy
The OAuth provider identifier passed to Clerk's `startSSOFlow` — one of `oauth_google`, `oauth_apple`, or `oauth_github`. Used as the argument to `handleSocialOAuth(strategy)`.
useSocialOAuth
A custom hook (created in this methodology) that encapsulates the Clerk SSO flow, loading-per-button state, concurrent-flow guard, and error alerts. Returns `{ handleSocialOAuth, loadingStrategy }`.
loadingStrategy
State value inside `useSocialOAuth` that stores which OAuth button is currently in flight (e.g., `'oauth_google'`). Null when idle. Used to show per-button loading spinners and disable all buttons during a flow.
Neon
Cloud-hosted serverless Postgres provider. Handles database deployment so you don't manage your own server. Used with Drizzle ORM.
Drizzle
The ORM (Object Relational Mapper) used to define the Postgres schema in TypeScript and run migrations against the Neon database.
Zustand
Lightweight global state management library. No Provider wrapper needed. Used for app-wide state like item lists or user preferences.
FlatList
The performant React Native list component. Always use `<FlatList>` instead of `Array.map` for rendering lists of data on mobile.
Pressable
The modern React Native component for touchable/tappable elements. Preferred over the older `<TouchableOpacity>`. Uses `onPress`, `onLongPress`, `onDoublePress` instead of `onClick`.
SafeAreaView
A React Native wrapper component that constrains content to the safe area of the screen (avoiding the status bar and notch). Use `edges={['top']}` to apply it only to the top.
Sentry
Error monitoring and user feedback platform integrated via the Expo Sentry SDK. Used to add a feedback button so users can report bugs and feature requests.

// FREQUENTLY ASKED QUESTIONS

What is the Codesistency Full-Stack Expo React Native Build Method?

It's a structured, opinionated method for building a production-ready cross-platform mobile app from scratch using React Native with Expo, Clerk for authentication, NeonDB Postgres, Drizzle ORM, NativeWind for styling, and Zustand for state. It walks you from an empty folder through setup, file-based routing, social OAuth, database integration, and dark mode — entirely on free tiers with no credit card required.

What tech stack does this React Native method use?

The stack is React Native + Expo (framework), Clerk (authentication with 50,000 free MAU), NeonDB (serverless Postgres), Drizzle (TypeScript ORM), NativeWind v4 (Tailwind for React Native), and Zustand (global state). All components run on free tiers until meaningful scale, and TypeScript is used throughout for type safety.

How do I set up a new Expo React Native project from scratch?

Run `npx create-expo-app@latest --template default .` inside an empty folder using TypeScript, then run `npm run reset-project` and choose 'No' to delete the example files. Add secrets to a `.env` file and immediately gitignore it. Next, configure NativeWind v4 following nativewind.dev, then wire providers into the root `app/_layout.tsx`.

How do I add social login (Google, Apple, GitHub) to a React Native app?

Use Clerk's `useSSO` hook wrapped in a custom `useSocialOAuth` hook that manages `loadingStrategy` state, guards against concurrent flows, calls `startSSOFlow({ strategy })`, then `setActive` on success. Build a custom sign-in screen with one Pressable per provider calling `handleSocialOAuth('oauth_google')`. OAuth requires native modules, so you must use a development build, not Expo Go.

How does this method compare to using bare React Native?

This method always initialises with Expo rather than bare React Native, because Expo is to React Native what Next.js is to React — same core plus file-based routing, API routes, optimised components, and production tooling. Bare React Native requires manual native configuration; Expo development builds give you native modules (OAuth, crypto, push) with a single `npx expo run:ios` command.

When should I use a development build instead of Expo Go?

Switch to a development build whenever you need native modules — OAuth sessions, Expo Crypto, push notifications, or any custom native package. Expo Go only supports simple learning projects and cannot run custom native code. Run `npx expo install expo-dev-client` then `npx expo run:ios` or `run:android` to build a custom native app on your device.

What results can I expect from following this build method?

You'll produce an authenticated, styled, database-connected cross-platform app running on both iOS and Android from one codebase, complete with social OAuth, automatic dark mode, tab navigation, global state, and error reporting. Because the entire stack sits on free tiers (Clerk's 50k MAU, Neon Postgres, NativeWind, Zustand, Drizzle), you can ship a complete product at zero cost.

Why should I use NativeWind v4 and not v5?

Use NativeWind v4 because v5 is not stable. Follow nativewind.dev exactly: install packages, create `tailwind.config.js`, `global.css`, `babel.config.js`, and `metro.config.js`. Set the `content` array to scan the entire `src/` folder — not just `components/` — and set `darkMode: 'media'` so the app follows the device colour scheme automatically.

How do I fix a 'no native module found' error in Expo?

Stop the dev server, delete the `ios/` folder (and/or `android/`), then re-run `npx expo run:ios` to rebuild the development build with the new native module compiled in. This is the standard fix — never attempt a JS-only workaround. You only need to rebuild when adding new native packages; subsequent JS changes hot-reload normally.

What is file-based routing in Expo Router?

File-based routing means any file placed inside the special `app/` folder automatically becomes a screen or route — folder structure equals navigation structure. Use folders with `index.tsx` plus `_layout.tsx` for every route group, and wrap related screens in bracketed folders like `(auth)/` and `(home)/` to create route groups that share layout logic without affecting the URL.

// GET THIS SKILL — FREE

Use this skill in your AI

Every skill on SkillForge is free. Drop your email and copy this skill straight into Claude, ChatGPT, or any LLM.

We'll email you when new skills drop. Unsubscribe anytime.