Skip to main content

NativeStorage Class

The main storage class that provides all storage operations.
export class NativeStorage {
  // Async Methods
  async setItem(key: string, value: string): Promise<void>;
  async getItem(key: string): Promise<string | null>;
  async removeItem(key: string): Promise<void>;
  async clear(): Promise<void>;
  async setObject<T extends Record<string, unknown>>(key: string, value: T): Promise<void>;
  async getObject<T extends Record<string, unknown> = Record<string, unknown>>(key: string): Promise<T | null>;

  // Sync Methods
  setItemSync(key: string, value: string): void;
  getItemSync(key: string): string | null;
  removeItemSync(key: string): void;
  clearSync(): void;
  setObjectSync<T extends Record<string, unknown>>(key: string, value: T): void;
  getObjectSync<T extends Record<string, unknown> = Record<string, unknown>>(key: string): T | null;

  // Multi Operations
  async multiGet(keys: string[]): Promise<Record<string, string | null>>;
  async multiSet(items: Record<string, string>): Promise<void>;
}

Usage

import { NativeStorage } from 'expo-native-storage';

// Create your own instance
const storage = new NativeStorage();
await storage.setItem('key', 'value');

// Or use the default instance
import Storage from 'expo-native-storage';
await Storage.setItem('key', 'value');

ExpoNativeStorageModule Interface

The native module interface that provides the underlying platform-specific storage operations.
export type ExpoNativeStorageModule = {
  // Async Methods
  setItem(key: string, value: string): Promise<void>;
  getItem(key: string): Promise<string | null>;
  removeItem(key: string): Promise<void>;
  clear(): Promise<void>;

  // Sync Methods
  setItemSync(key: string, value: string): void;
  getItemSync(key: string): string | null;
  removeItemSync(key: string): void;
  clearSync(): void;
};

Platform Implementation

This interface is implemented natively on each platform:
  • iOS: Uses UserDefaults for synchronous key-value storage
  • Android: Uses SharedPreferences for synchronous key-value storage
  • Web: Uses localStorage for synchronous key-value storage

Internal Usage

The native module is loaded using Expo’s requireNativeModule:
import { requireNativeModule } from 'expo-modules-core';

const ExpoNativeStorageModule = requireNativeModule<ExpoNativeStorageModule>(
  'ExpoNativeStorage'
);
The ExpoNativeStorageModule is used internally by the NativeStorage class. You typically don’t need to access it directly.

Generic Type Parameters

Several methods support TypeScript generics for type-safe object storage.

Object Methods Type Parameter

Both setObject and getObject methods (async and sync) use a generic type parameter T:
T extends Record<string, unknown>
This constraint ensures that:
  • The type must be an object (not a primitive)
  • The object can have any string keys
  • The values can be of any type
  • The object must be JSON-serializable

Example: Type-Safe Object Storage

interface User {
  id: number;
  name: string;
  email: string;
  premium: boolean;
}

// TypeScript knows the type
const user: User = {
  id: 1,
  name: 'John Doe',
  email: '[email protected]',
  premium: true
};

// Type-safe storage
await Storage.setObject<User>('user', user);

// Type-safe retrieval
const storedUser = await Storage.getObject<User>('user');
if (storedUser) {
  // TypeScript knows all properties
  console.log(storedUser.name); // string
  console.log(storedUser.premium); // boolean
}

Default Generic Type

If no type parameter is provided, getObject defaults to Record<string, unknown>:
// Without type parameter
const data = await Storage.getObject('config');
// Type: Record<string, unknown> | null

// Access properties (no type safety)
if (data) {
  console.log(data.someProperty); // unknown
}

Export Structure

The library provides both named and default exports:
// Named export: NativeStorage class
export class NativeStorage { /* ... */ }

// Default export: Pre-instantiated Storage object
const Storage = new NativeStorage();
export default Storage;

Import Options

// Option 1: Use the default instance (recommended)
import Storage from 'expo-native-storage';
await Storage.setItem('key', 'value');

// Option 2: Import the class and create your own instance
import { NativeStorage } from 'expo-native-storage';
const myStorage = new NativeStorage();
await myStorage.setItem('key', 'value');

// Option 3: Import both
import Storage, { NativeStorage } from 'expo-native-storage';
Most applications should use the default Storage instance for simplicity and consistency.

Type Examples

Basic String Storage

// Simple key-value storage
const key: string = 'username';
const value: string = 'john_doe';

await Storage.setItem(key, value);
const result: string | null = await Storage.getItem(key);

Object Storage with Interface

interface AppSettings {
  theme: 'light' | 'dark';
  language: string;
  notifications: boolean;
  fontSize: number;
}

const settings: AppSettings = {
  theme: 'dark',
  language: 'en',
  notifications: true,
  fontSize: 16
};

await Storage.setObject<AppSettings>('settings', settings);
const loaded: AppSettings | null = await Storage.getObject<AppSettings>('settings');

Multi Operations with Types

// multiGet with known keys
const keys: string[] = ['username', 'email', 'theme'];
const result: Record<string, string | null> = await Storage.multiGet(keys);

// multiSet with type-safe object
const items: Record<string, string> = {
  username: 'john_doe',
  email: '[email protected]',
  theme: 'dark'
};
await Storage.multiSet(items);

Sync Methods with Types

// Sync string operations
Storage.setItemSync('key', 'value');
const value: string | null = Storage.getItemSync('key');

// Sync object operations with interface
interface Config {
  apiUrl: string;
  timeout: number;
}

const config: Config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
};

Storage.setObjectSync<Config>('config', config);
const loaded: Config | null = Storage.getObjectSync<Config>('config');

Build docs developers (and LLMs) love