React Native
data storage
iOS
Android
mobile app development

What are my options for storing data when using React Native? iOS and Android

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

React Native gives you several storage options, but the right one depends on what you are storing: tiny preferences, large offline datasets, sensitive credentials, or cached files. The most useful way to think about storage on iOS and Android is not “which package is best,” but “what data shape, security level, and query behavior does this app need.”

Key-Value Storage for Small App State

For lightweight non-sensitive data such as feature flags, onboarding state, or UI preferences, key-value storage is usually enough.

A familiar option is AsyncStorage:

javascript
1import AsyncStorage from '@react-native-async-storage/async-storage';
2
3await AsyncStorage.setItem('theme', 'dark');
4const theme = await AsyncStorage.getItem('theme');
5console.log(theme);

This is easy to use and cross-platform, but it is not meant for large relational datasets and it is not secure storage for secrets.

Some teams prefer faster key-value engines such as MMKV when app startup speed or frequent reads matter. The architectural point is the same: key-value storage is great for small local state, not for complex querying.

Secure Storage for Secrets

If the data includes access tokens, refresh tokens, or other credentials, do not store it in plain AsyncStorage.

Use a secure storage layer backed by iOS Keychain and Android Keystore. In React Native, this is commonly exposed through packages that wrap the platform secure storage APIs.

javascript
1import * as SecureStore from 'expo-secure-store';
2
3await SecureStore.setItemAsync('access_token', 'secret-value');
4const token = await SecureStore.getItemAsync('access_token');

The exact package depends on the app stack, but the design rule is stable: secrets belong in secure storage, not in general-purpose local storage.

SQLite and Database-Style Storage

If the app needs offline data with filtering, joins, or large tables, a database is usually a better fit than key-value storage.

SQLite is the usual local relational option.

javascript
1import * as SQLite from 'expo-sqlite';
2
3const db = SQLite.openDatabaseSync('app.db');
4db.execSync('CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, title TEXT)');
5db.execSync("INSERT INTO notes (title) VALUES ('Draft')");
6const rows = db.getAllSync('SELECT * FROM notes');
7console.log(rows);

This style of storage is better when you need structured queries or large local datasets. It is heavier than key-value storage, but much more capable.

Offline-First Object or Sync Libraries

Some apps need local storage plus a synchronization model. In those cases, libraries such as Realm or higher-level offline-first layers may be attractive.

These tools can be useful when:

  • the app has a complex local domain model
  • offline editing matters
  • synchronization rules are part of the product design

The tradeoff is extra framework complexity. For a simple app, they are often unnecessary. For a serious offline app, they can save substantial infrastructure work.

File Storage for Images and Documents

If the data is a photo, PDF, audio file, or other binary asset, a database or key-value store is often the wrong abstraction. Store the file in the filesystem and keep only metadata or references in structured storage.

javascript
1import * as FileSystem from 'expo-file-system';
2
3const path = FileSystem.documentDirectory + 'note.txt';
4await FileSystem.writeAsStringAsync(path, 'hello');
5const contents = await FileSystem.readAsStringAsync(path);
6console.log(contents);

This separation keeps binary files out of places designed for small records and improves maintainability.

Cloud Data Is a Different Layer

Some apps primarily store data in a backend such as Firebase, Supabase, or a custom API. In that case, the local device still usually needs one of the storage strategies above for:

  • session tokens
  • offline cache
  • queued writes
  • settings and feature flags

So “we use a backend” does not eliminate local storage design. It just changes how much of the source of truth lives on the device.

A Practical Decision Rule

A simple rule set works well:

  • small non-sensitive settings: key-value storage
  • secrets and credentials: secure storage
  • structured offline data: SQLite or similar database
  • binary assets: filesystem
  • rich offline sync model: specialized offline-first library

Most storage mistakes come from using one tool for every category just because it was the first package added to the project.

Common Pitfalls

  • Storing tokens or passwords in plain AsyncStorage.
  • Using key-value storage for data that really needs queries and indexing.
  • Putting large binary files directly into storage systems meant for small values.
  • Choosing a heavy offline database when the app only needs a few preferences.
  • Assuming backend storage removes the need for careful local storage design.

Summary

  • React Native storage choices should follow the type and sensitivity of the data.
  • Use key-value storage for simple non-sensitive local state.
  • Use secure storage for credentials and secrets.
  • Use SQLite or a similar local database for larger structured datasets.
  • Use the filesystem for files and media, and combine these tools instead of forcing one storage layer to do everything.

Course illustration
Course illustration

All Rights Reserved.