/blog/building-offline-first-react-native-apps/ - zsh
user@portfolio ~ $

cat building-offline-first-react-native-apps.md

Building Offline-First React Native Apps

Author: Aslany Rahim Published: December 04, 2025
Mobile users lose connectivity constantly (elevators, subways, flights). Learn how to cache data using Async Storage and NetInfo so your app doesn't crash when the internet dies.

A web app can get away with showing a "No Internet" dinosaur when connectivity is lost. A mobile app cannot. Users expect to be able to open your app, read previously loaded content, and navigate UI even in Airplane mode.

This strategy is called Offline-First.

The Tools

  1. @react-native-community/netinfo: To detect if the device is online.
  2. @react-native-async-storage/async-storage: To save JSON data to the device's disk.
  3. TanStack Query (React Query): To handle the logic of serving cached data vs. fetching new data.

Detecting Connection State

First, we need to know the status of the network.

import NetInfo from "@react-native-community/netinfo";
import { useEffect, useState } from "react";

const useConnectivity = () => {
  const [isConnected, setIsConnected] = useState(true);

  useEffect(() => {
    const unsubscribe = NetInfo.addEventListener(state => {
      setIsConnected(state.isConnected);
    });
    return () => unsubscribe();
  }, []);

  return isConnected;
};

The Manual Caching Approach

If you aren't using a library like React Query, you have to implement a "Stale-While-Revalidate" pattern manually.

import AsyncStorage from '@react-native-async-storage/async-storage';

const getPosts = async () => {
  try {
    // 1. Try to fetch from Network
    const response = await fetch('https://api.example.com/posts');
    const data = await response.json();

    // 2. If successful, save to storage
    await AsyncStorage.setItem('posts_cache', JSON.stringify(data));
    return data;

  } catch (error) {
    // 3. If network fails, load from storage
    console.log("Network failed, loading cache...");
    const cached = await AsyncStorage.getItem('posts_cache');
    if (cached) {
      return JSON.parse(cached);
    }
    throw error;
  }
}

The Modern Approach: React Query + Async Storage

React Query has offline support built-in using persistQueryClient. It automatically saves your API responses to Async Storage and rehydrates them when the app opens.

import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'
import AsyncStorage from '@react-native-async-storage/async-storage'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // Keep cache for 24 hours
    },
  },
})

const asyncStoragePersister = createAsyncStoragePersister({
  storage: AsyncStorage,
})

// Wrap your app
export default function App() {
  return (
    <PersistQueryClientProvider
      client={queryClient}
      persistOptions={{ persister: asyncStoragePersister }}
    >
      <YourApp />
    </PersistQueryClientProvider>
  )
}

Now, if you call useQuery(['posts'], fetchPosts), React Query will serve the data from disk immediately while it tries to fetch fresh data in the background.

Conclusion

Offline support is what separates "Websites wrapped in an app container" from Native Quality apps. Using caching strategies ensures your users have a seamless experience regardless of their 4G signal strength.

38 views
0 comments

Comments (0)

Leave a Comment

No comments yet. Be the first to comment!

Related Posts

React Native Navigation: Stack vs. Tab vs. Drawer

Unlike the web, mobile apps don't have a URL bar. We explore how to structure your mobile app using React …

November 28, 2025

React Native CLI vs Expo: Which Should You Choose in 2025?

Starting a mobile project? The choice between "Bare" React Native and Expo can make or break your timeline. We break …

November 23, 2025

user@portfolio ~ $ _