Building Offline-First React Native Apps
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
- @react-native-community/netinfo: To detect if the device is online.
- @react-native-async-storage/async-storage: To save JSON data to the device's disk.
- 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.