DEV Community

Cathy Lai
Cathy Lai

Posted on

Making My React Native App Work Offline [MyNextHome]

The Problem We're Solving

Let's be honest - mobile apps need to work offline. House hunters are going to be in basements, on subways, or in areas with terrible WiFi. They'll want to add house listings or take notes during open home viewings, and they shouldn't have to wait for a network connection.

So how do we build an app that works seamlessly offline, but also syncs everything back to the server when connectivity returns? That's what we're diving into in my new app "MyNextHome".

The Big Picture

Our house hunting app has three main pieces:

  1. HousesContext - The brain that manages all the houses and handles syncing
  2. Add/Edit Form - Where users create new listings
  3. House Details Screen - Where users take notes on each house

Everything is stored locally first (using AsyncStorage), then synced to the server when possible. Simple, right?

The Core Ideas

1. Save Locally, Always

No matter what, when a user saves something, it goes straight to local storage. No waiting, no "please check your connection" errors. The app just works.

2. Server is the Boss (Mostly)

The server has the definitive data - titles, locations, prices, all that good stuff. When we sync, we use server data as the base and merge in any local edits (like notes). This keeps everything consistent across devices.

3. Timestamps Tell the Story

Each house has two timestamps:

  • updatedAt - When you last changed it locally
  • syncedAt - When it was last successfully sent to the server

If updatedAt > syncedAt (or syncedAt is missing), that house needs to be synced. It's like a to-do list, but for data.

4. Auto-Sync When Network Returns

The app watches for network changes. Go from offline to online? Boom - automatic sync of everything that needs it.

Data Flow Diagrams

First Load: Online

┌─────────────┐
│   App Start │
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│ Load from AsyncStorage│  ← Instant UI (local data)
│ (if exists)          │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ Fetch from Server   │  ← Background fetch
│ GET /api/open-homes │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ Merge Data          │
│ - Server = base     │
│ - Local notes kept  │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ Save to AsyncStorage│
│ Update UI           │
└─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What happens: You get instant UI from local data, then fresh server data merges in the background. Your notes are preserved.

First Load: Offline

┌─────────────┐
│   App Start │
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│ Load from AsyncStorage│  ← All you get
│ (if exists)          │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ Try Fetch from Server│
│ ❌ Network Error    │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ Show Local Data     │  ← Still works!
│ (maybe show error)  │
└─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What happens: You only see local data, but the app still works. When you come back online, it'll fetch fresh data.

Updating Notes: Online

┌─────────────────┐
│ User edits notes│
│ Taps "Save"     │
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│ updateHouse()       │
│ - Update local state│
│ - Set updatedAt     │
│ - Save to AsyncStorage│
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Check Network       │
│ ✅ Online           │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ syncToServer()      │
│ - Find dirty houses │
│ - POST to server    │
│ - Update syncedAt   │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Navigate back       │
│ ✅ Notes saved & synced│
└─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What happens: Notes save locally immediately, then sync to server right away. Smooth and fast.

Updating Notes: Offline

┌─────────────────┐
│ User edits notes│
│ Taps "Save"     │
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│ updateHouse()       │
│ - Update local state│
│ - Set updatedAt     │
│ - Save to AsyncStorage│
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Check Network       │
│ ❌ Offline          │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Navigate back       │
│ ✅ Notes saved locally│
└────────┬────────────┘
         │
         ▼
         ...
         │
         ▼
┌─────────────────────┐
│ Network comes back  │
│ NetInfo detects     │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ syncToServer()      │
│ - updatedAt > syncedAt│
│ - POST to server    │
│ - Update syncedAt   │
└─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What happens: Notes save locally, no sync attempt. Later, when network returns, automatic sync kicks in and pushes your notes to the server.

Server Updates (New Houses)

┌─────────────────────┐
│ Server has new house│
│ (added by admin)    │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ App fetches on load │
│ or background refresh│
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Transform API data  │
│ to House format     │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Merge with local    │
│ - Server = base     │
│ - Keep local notes  │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ Save to AsyncStorage│
│ Update UI           │
└─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What happens: New houses from the server appear in your app. If you had local notes for that house ID, they're preserved. Server data is the source of truth for everything else.

Adding New House: Offline → Online

┌─────────────────┐
│ User adds house │
│ (offline)       │
└────────┬────────┘
         │
         ▼
┌─────────────────────┐
│ addHouse()          │
│ - Create with ID    │
│ - Set updatedAt     │
│ - No syncedAt       │
│ - Save locally      │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ House appears in UI │
│ ✅ Works offline!   │
└────────┬────────────┘
         │
         ... (user goes online)
         │
         ▼
┌─────────────────────┐
│ NetInfo detects     │
│ offline → online    │
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│ syncToServer()      │
│ - syncedAt missing  │
│ - POST to server    │
│ - Set syncedAt      │
└─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

What happens: You add a house offline, it appears immediately. When you come back online, it automatically syncs to the server.

The Magic: Timestamp Comparison

The sync logic is pretty simple:

// Does this house need syncing?
const needsSync = !house.syncedAt || 
                  new Date(house.updatedAt) > new Date(house.syncedAt);
Enter fullscreen mode Exit fullscreen mode

If syncedAt is missing, it's never been synced. If updatedAt is newer than syncedAt, you've made local changes since the last sync. Either way, it needs to go to the server.

Why This Works

  • Reliability - Your data is never lost. Even if sync fails, it's safe in local storage.
  • Speed - No waiting for network. Everything feels instant.
  • User Experience - The app just works, online or offline.
  • Consistency - Server is the source of truth, so everyone sees the same base data.
  • Simplicity - Timestamp comparison is easy to understand and debug.

Wrapping Up

This offline-first approach means your users can use the app anywhere - subway, basement viewing, terrible WiFi, you name it. Data saves locally first, syncs when it can, and the server keeps everything consistent.

The key is: local storage is your friend, timestamps are your guide, and the server is your source of truth.


Built with @react-native-async-storage/async-storage for local persistence and @react-native-community/netinfo for network detection.

Top comments (5)

Collapse
 
art_light profile image
Art light

This is super impressive — the way you’ve designed the offline-first flow is clean, practical, and really user-friendly.

Collapse
 
cathylai profile image
Cathy Lai

Thanks! I’ve been trying to share mobile patterns in a clean, practical way, especially the kinds of things I wish I had when working on real-world apps. I’m glad the offline-first flow came across clearly. Appreciate you taking the time to comment!

Collapse
 
art_light profile image
Art light

I will keep my fingers crossed for you!

Collapse
 
cloudhooks profile image
Cloudhooks

Congratulations on your work!
I wish you luck!

Collapse
 
cathylai profile image
Cathy Lai

If this helped you, I’m writing more practical React Native troubleshooting guides. Follow me here or ask questions — happy to help others avoid the pain points I experienced.