How Instagram, WhatsApp, Uber & Netflix Would Be Built Today Using Expo Router
Most React Native apps look clean in the beginning.
You create a few folders:
/screens
/components
/hooks
/utils
everything feels organized…
until the app grows.
You add:
authentication
realtime systems
notifications
caching
analytics
deep linking
background sync
offline support
100+ screens
multiple developers
and suddenly the codebase starts fighting you.
This is where architecture becomes more important than UI.
Apps like Instagram, WhatsApp, Uber and Netflix are not difficult because of screens.
They are difficult because of:
scale
synchronization
maintainability
performance
navigation complexity
realtime infrastructure
If these apps were built today using React Native + Expo Router, the architecture would look very different from typical tutorial apps.
This blog is about that architecture thinking.
Not UI cloning.
Why Simple Folder Structures Fail at Scale
Most beginner apps organize code like this:
/components
/screens
/apis
/hooks
This works for small projects.
But large apps slowly become impossible to manage because everything is grouped by “type” instead of “business feature”.
Example:
/components
eventually becomes:
/components
FeedCard.tsx
ChatBubble.tsx
DriverMarker.tsx
MovieCard.tsx
PaymentSheet.tsx
NotificationItem.tsx
Now nobody knows:
ownership
dependencies
feature boundaries
what can safely change
This is why production apps move toward feature-based architecture.
How Large Apps Think About Architecture
Small apps are built around screens.
Production apps are built around systems.
Instead of thinking:
"profile screen"
large teams think:
"user identity system"
Instead of:
"chat UI"
they think:
"realtime messaging infrastructure"
That mindset shift changes everything.
Why Expo Router Fits Modern React Native Apps
Before Expo Router, navigation usually became one giant file.
Something like:
navigation/
AppNavigator.tsx
AuthNavigator.tsx
HomeTabs.tsx
FeedStack.tsx
As the app grows:
navigation becomes deeply nested
imports explode
route ownership becomes unclear
feature teams collide constantly
Expo Router fixes this by making routing filesystem-based.
Example:
app/
(tabs)/
home.tsx
search.tsx
profile/
[username].tsx
The folder structure becomes the navigation structure.
This sounds simple…
but at scale it becomes extremely powerful.
Production-Grade Expo Router Structure
A scalable Expo Router app would look more like this:
app/
(public)/
login/
signup/
onboarding/
(protected)/
(tabs)/
home/
search/
activity/
profile/
chat/
rides/
media/
settings/
_layout.tsx
features/
auth/
feed/
messaging/
maps/
video/
payments/
services/
api/
realtime/
analytics/
storage/
store/
hooks/
types/
lib/
Notice something important:
/screens
barely exists anymore.
Features become the center of the architecture.
Feature-Based Architecture
This is one of the biggest differences between beginner apps and production apps.
Instead of:
/components
/hooks
/apis
large apps organize by domain:
/features/feed
/features/chat
/features/location
/features/payments
Each feature contains its own:
components/
hooks/
api/
types/
state/
utils/
Example:
/features/chat
/components
/hooks
/api
/store
/types
Benefits:
easier scaling
independent development
fewer merge conflicts
cleaner ownership
easier testing
This is how large engineering teams work without constantly breaking each other’s code.
Shared Layouts and Nested Routing
One underrated thing about Expo Router is layouts.
Example:
app/
(protected)/
_layout.tsx
(tabs)/
_layout.tsx
home.tsx
profile.tsx
chat/
_layout.tsx
[id].tsx
This allows:
protected route wrappers
shared headers
nested navigation stacks
feature-level navigation control
Instead of one massive navigator, each feature owns its own navigation logic.
That becomes extremely important at scale.
Authentication Architecture
Most tutorial apps handle auth like this:
if (loggedIn) {
return <Home />
}
Production apps are far more complex.
Because authentication is not just login.
It includes:
token refresh
deep linking
onboarding state
secure storage
session restoration
role permissions
splash coordination
A real startup flow looks closer to this:
Open App
↓
Restore Session
↓
Validate Token
↓
Fetch User Data
↓
Resolve Feature Flags
↓
Enter Protected Routes
Expo Router route groups make this clean:
(public)
(protected)
(admin)
State Management in Large Apps
One of the biggest mistakes in React Native apps:
putting everything into one global store.
At scale that becomes:
slow
difficult to debug
tightly coupled
rerender heavy
Large apps separate state into layers.
1. Server State
This is data coming from APIs.
Examples:
feed posts
chats
movies
rides
Usually handled with tools like:
TanStack Query
caching layers
background revalidation
2. UI State
Things like:
modal open
active tab
sheet visibility
Usually local state.
Not everything belongs globally.
3. Persistent App State
Things like:
auth
session
preferences
Usually stored with:
Zustand
Redux Toolkit
MMKV
How Instagram Would Be Built
Instagram is basically:
feeds + media delivery + engagement systems
The hardest problems are not UI.
They are:
infinite scrolling
image caching
upload systems
feed ranking
memory optimization
preloading
Feed Architecture
Large feeds are usually built like this:
Feed Engine
↓
Pagination Layer
↓
Cache Layer
↓
Virtualized Rendering
This is why apps use:
FlashList
aggressive caching
lazy image loading
pagination systems
Without optimization, React Native feeds become laggy very quickly.
The Real Bottleneck in Social Apps
Most people think JavaScript is the bottleneck.
Usually it’s actually:
images and rendering
Large apps spend huge effort optimizing:
image decoding
caching
prefetching
memory pressure
scroll performance
That’s why Instagram feels smooth even with massive media feeds.
How WhatsApp Would Be Built
WhatsApp is fundamentally a realtime synchronization system.
The chat UI itself is relatively easy.
The difficult part is:
websocket reliability
offline delivery
message ordering
retries
synchronization
media uploads
typing indicators
Realtime Messaging Flow
A production messaging system often works like this:
User Sends Message
↓
Optimistic UI Update
↓
Save Locally
↓
WebSocket Send
↓
Server ACK
↓
Message Status Update
The message appears instantly before the server confirms it.
That instant feeling matters.
Offline-First Messaging
Apps like WhatsApp assume networks will fail.
So messages are usually:
stored locally
queued offline
retried automatically
synced later
This is why production apps use:
SQLite
MMKV
background sync queues
Offline-first architecture is one of the biggest differences between serious apps and beginner apps.
How Uber Would Be Built
Uber is mostly a realtime location platform.
Core challenges:
live GPS tracking
websocket streams
route recalculation
map rendering
background location updates
Ride Tracking Architecture
A simplified version:
Driver GPS
↓
Realtime Transport Layer
↓
Location Processing
↓
Passenger Live Updates
This sounds simple…
until updates happen every few seconds for millions of users.
Why Maps Become Difficult
Maps are expensive.
At scale, apps optimize:
marker rendering
animation frequency
region updates
rerender control
battery usage
Without optimization:
FPS drops
battery drains
phones heat up
Realtime systems force you to think differently about rendering.
How Netflix Would Be Built
Netflix is heavily optimized around content delivery.
The app architecture focuses on:
startup speed
streaming reliability
offline downloads
recommendation loading
media caching
Netflix-Style Architecture
Apps like Netflix separate systems aggressively:
UI Layer
Playback Engine
Content Metadata
Download Engine
Recommendation Engine
Video playback is often isolated because streaming systems become highly specialized.
App Startup Optimization
Large apps obsess over startup speed.
Because slow startup kills retention.
Bad startup strategy:
load everything immediately
Better strategy:
load minimum shell
↓
restore session
↓
lazy load features
↓
prefetch likely screens
Production apps aggressively optimize:
lazy imports
route-level splitting
deferred rendering
background hydration
asset preloading
Offline-First Architecture
Production apps assume internet can fail anytime.
Typical flow:
User Action
↓
Local Update
↓
Sync Queue
↓
Background Retry
↓
Server Confirmation
This creates apps that feel instant even on bad networks.
That’s a huge architectural advantage.
Performance at Scale
Large React Native apps optimize constantly for:
memory usage
rerenders
JS thread blocking
navigation performance
image rendering
startup time
Common strategies:
FlashList
memoization
background prefetching
selective hydration
virtualization
Performance engineering becomes a full-time concern in large apps.
The Biggest Architecture Lesson
The most important shift is this:
Small apps are built around pages.
Large apps are built around systems.
feed systems
caching systems
realtime systems
synchronization systems
navigation systems
offline systems
The UI is only the surface layer.
That’s why apps like Instagram, WhatsApp, Uber and Netflix require much more than “React Native knowledge”.
They require architecture thinking.