If you're picking a mobile stack in 2026, you have three real options: native (Swift + Kotlin), React Native with Expo, or Flutter. We've shipped in all three. For apps that live or die on shipping speed — side projects, indie SaaS, MVPs that need to be real in weeks — React Native with Expo on top of Supabase wins more often than not.
Here's the case, and the three places it bites.
Why this combination ships faster
One language, one mental model. TypeScript on the client, TypeScript in Supabase Edge Functions, SQL for schema. There's no context switch between "mobile code" and "backend code." You write a feature on the phone, add a migration, write an edge function, and ship — all in one editor.
Supabase is a proper backend, not a glorified database. Postgres with row-level security, auth (email, OAuth, Apple, Google), storage, realtime, edge functions. The pieces that usually take three or four SaaS integrations are one dashboard. You can still drop down to raw SQL when Postgres's guarantees matter — RLS policies, triggers, deferred constraints — none of which Firebase gives you.
Expo's managed workflow is now viable for real apps. The "eject to bare" conventional wisdom from 2020 is outdated. Config plugins, EAS Build, and dev clients let you add native modules without touching Xcode or Android Studio. We've shipped a push-notification + biometric-lock + Apple-Sign-In app without running a single pod install.
What a week of work looks like
A typical day-one for a new feature on this stack:
// 1. Add a migration
create table public.items (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users on delete cascade,
title text not null,
created_at timestamptz default now()
);
alter table public.items enable row level security;
create policy "items_self" on public.items
for all using (auth.uid() = user_id);// 2. Call it from the app — RLS enforces scoping, the client stays dumb
const { data } = await supabase.from("items").select("*");That's the whole loop. No REST endpoint to write, no auth middleware, no "whose data is this" check on the server — RLS handles it.
The three places it bites
1. In-app purchases are not optional. Supabase doesn't touch Apple's or Google's billing. You still need RevenueCat or Stripe for IAP, and the paywall UX is its own engineering problem. Budget a week to get this right.
2. Expo updates break in subtle ways. An SDK bump from 54 → 55 → 56 usually means three days of "why does the keyboard behave differently now." Our Expo upgrade guide covers the exact landmines.
3. RLS is correct, but writing it is hard the first time. The policies read like English but the failure modes — 406s on inserts, empty arrays instead of 403s — are counterintuitive. Plan to spend an afternoon learning when using fires vs with check.
When not to use this stack
- AR, heavy 3D, or real-time graphics — go native.
- Apps with hard offline requirements across complex relational data — WatermelonDB helps, but SQLite on the device stops being trivial at around 30 relations.
- Regulated data where you need audit trails and SOC 2 on day one — Supabase ships these, but you're past the "indie hacker" stage anyway.
For everything else? Pick up a starter kit and ship.
Read more
Expo SDK Upgrades: The Gotchas Nobody Warns You About
A battle-tested field guide to Expo SDK upgrades — what breaks, what quietly changes behavior, and how to test an upgrade without shipping a broken build.
Apple Sign-In, Google Sign-In, and Magic Links: Which Auth Flow Ships Fastest?
A practical comparison of the three most common mobile auth flows — how long each actually takes to wire up, what breaks on submission, and when to use which.
RevenueCat vs Stripe for Mobile In-App Purchases: When to Use Which
A decision framework for mobile payments: when RevenueCat's abstraction pays for itself, when Stripe's native sheet is enough, and when you need both.