Skip to main content
mobile

Building an MVP Mobile App with React Native + Expo (2026 Practical Guide)

A step-by-step playbook for shipping a mobile MVP in 4–6 weeks using React Native and Expo. Real project structure, libraries I actually use, and what to skip.

15 de maio de 2026·12 min de leitura·LLoic Bachellerie

The Goal

Ship a working iOS + Android app in 4–6 weeks. Not a prototype. A real binary in TestFlight and Google Play Internal Testing, ready to hand to design partners.

This is the exact stack I use for Tier 2 client MVPs. It's optimized for speed without painting yourself into a corner.

The Stack

  • Framework: React Native + Expo SDK 53
  • Language: TypeScript (strict mode, always)
  • Router: Expo Router (file-based, drop-in replacement for react-navigation)
  • State: Zustand for UI state, TanStack Query for server state
  • Forms: React Hook Form + Zod
  • UI: Custom components on top of NativeWind (Tailwind for RN) or react-native-paper if you want Material out of the box
  • Backend: Supabase or Firebase (auth + DB + storage in one)
  • Analytics: Posthog React Native SDK
  • Crash reporting: Sentry
  • Builds: EAS Build (cloud)
  • OTA updates: EAS Update

That's it. No Redux. No custom build pipeline. No native modules unless absolutely required.

Project Setup in 10 Minutes

npx create-expo-app@latest my-app -t default-typescript
cd my-app
npx expo install expo-router react-native-screens react-native-safe-area-context
npm install zustand @tanstack/react-query react-hook-form zod
npm install nativewind tailwindcss
npx tailwindcss init

Configure Tailwind for NativeWind (tailwind.config.js needs content: ["./app/**/*.{js,jsx,ts,tsx}"]), wrap your root layout in a QueryClientProvider, and you're rendering.

Folder Structure That Doesn't Become a Nightmare

app/
  (auth)/
    login.tsx
    signup.tsx
  (tabs)/
    index.tsx
    profile.tsx
  _layout.tsx
components/
  forms/
  layout/
  ui/
lib/
  api/
  hooks/
  stores/
  utils/
constants/
types/

Two things matter:

  1. Group routes by access layer using Expo Router groups: (auth) for logged-out screens, (tabs) for the main tabbed app.
  2. Keep lib/api/ separate from screens. A screen should never call fetch directly. All network calls go through a typed query/mutation hook.

The Auth Pattern

Supabase or Firebase, your choice. Both work the same way at the surface:

  1. Wrap the app in an AuthProvider that exposes user, loading, signIn, signOut.
  2. The root _layout.tsx reads user and decides whether to show (auth) or (tabs).
  3. Use expo-secure-store for token persistence, never AsyncStorage for credentials.

Skip building your own JWT refresh logic. Both Supabase and Firebase SDKs handle it.

Forms: Don't Roll Your Own

React Hook Form + Zod is the only setup I use. Define a schema, infer types, validate on blur.

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});
 
type LoginInput = z.infer<typeof loginSchema>;
 
const { control, handleSubmit, formState } = useForm<LoginInput>({
  resolver: zodResolver(loginSchema),
});

This pays for itself the first time you change a field name and TypeScript catches every screen that depended on it.

Server State with TanStack Query

export function useListings() {
  return useQuery({
    queryKey: ['listings'],
    queryFn: () => api.listings.list(),
    staleTime: 60_000,
  });
}

The benefits: caching, refetch on focus, optimistic updates, retry logic - all free. The first MVP I built without TanStack Query, I burned a week rebuilding 60% of it from scratch.

Push Notifications That Actually Work

Use expo-notifications. The 10-minute version:

  1. Request permission on first launch (after a clear in-app prompt explaining why).
  2. Get the Expo push token.
  3. Store it in your backend, associated with the user.
  4. Send via Expo's push service from a server endpoint.

Done. No FCM key wrangling, no APNs certificates. Expo handles both stores.

EAS Build: Don't Touch Xcode

EAS Build runs in the cloud, hands you a .ipa and .aab in 15 minutes, and submits to both stores via eas submit. You will not open Xcode for an MVP. You will not generate provisioning profiles by hand. This alone saves a week per project.

npx eas build --profile preview --platform all
npx eas submit --platform ios

OTA Updates Without App Store Review

Bug in production? EAS Update pushes a new JS bundle in under a minute. No Apple review for JS-only fixes. The native shell stays the same, the JS layer updates next time the user opens the app.

You still need a real store submission when you change native dependencies or app metadata. For everything else, OTA is your friend.

What to Skip on an MVP

  • Custom navigation animations
  • Dark mode (ship light, add dark in v1.1)
  • Tablet layouts (build for phone first)
  • Internationalization (unless your launch market needs it Day 1)
  • Custom splash screens - the Expo default is fine
  • Onboarding tooltips and tours
  • Settings screens beyond "log out" and "delete account" (the second is required by Apple)
  • Detailed analytics dashboards inside the app

Add these in v1.1 based on what users actually ask for.

The 4-Week Schedule

WeekWhat ships
1Project setup, auth, basic navigation, design tokens
2Core feature 1 fully working end-to-end
3Core feature 2 + push notifications + crash reporting
4Polish, TestFlight + Internal Testing submission, design partner onboarding

If you're not in TestFlight by end of week 4, the scope was too big. Cut features, don't add hours.

Common Mistakes I See

  • Building a full design system before any feature works. Build the feature with rough components first. Refactor into a system in v1.1.
  • Picking native iOS modules early. Wait until you've hit the actual limitation. 90% of the time, the JS solution is fine.
  • Underestimating the App Store review process. Budget 5 business days for the first review, including a near-certain rejection over a missing privacy detail.
  • Ignoring the Delete Account requirement. Apple requires it. Build it in week 4, not after rejection.

The 5% You'll Need a Native Module For

Bluetooth, ARKit, HealthKit, complex camera processing, background audio. For all of these, check expo-modules first. If it's not there, you'll need to drop to native code or wait until v2.

Want Help Shipping It?

I do this for a living. If you want a fixed-price quote to take your MVP idea to TestFlight in 4–6 weeks, book a call.


Share:
Newsletter

Receba insights práticos de engenharia

Agentes de voz com IA, fluxos de automação e entregas rápidas. Sem spam, cancele quando quiser.

Posts Relacionados