⚡ A fast, cross-platform TurboModule for app preferences and key-value storage, using NSUserDefaults on iOS and SharedPreferences on Android. Built for React Native's New Architecture.
- 🚀 New Architecture Ready — Implemented as a TurboModule for maximum performance
- 🪝 React Hooks — Convenient hooks for reactive state management
- 📱 Cross-Platform — Same JS API for iOS + Android with native optimizations
- 📦 Lightweight — Wraps native APIs (NSUserDefaults, SharedPreferences) directly
- 🗂 Namespace Support — Switch between default store and named suite/file
- 🛠 Batch Operations — Set/get/remove multiple keys at once for efficiency
- 🧹 Full Control — Get all keys, clear store, check existence
- 🔒 Type Safe — Full TypeScript support with proper type definitions
- ⚡ Turbo Performance — Built for React Native's New Architecture
npm install react-native-turbo-preferences
yarn add react-native-turbo-preferences
For React Native (Bare):
npx pod-install
For Expo: This package works with EAS builds.
import Prefs from 'react-native-turbo-preferences';
// Basic usage
await Prefs.set('username', 'Hamza');
const username = await Prefs.get('username');
console.log(username); // "Hamza"
// Use a named store
await Prefs.setName('MyPrefs');
await Prefs.set('theme', 'dark');
import { usePreferenceString, usePreferenceNamespace } from 'react-native-turbo-preferences';
function UserProfile() {
const [username, setUsername, hasUsername, clearUsername] = usePreferenceString('username');
const [namespace, setNamespace, resetToDefault] = usePreferenceNamespace();
return (
<View>
<Text>Username: {username || 'Not set'}</Text>
<Button
title="Set Username"
onPress={() => setUsername('Hamza')}
/>
<Button
title="Clear Username"
onPress={clearUsername}
/>
</View>
);
}
Switches the storage namespace.
Parameters:
name
(string, optional) - Namespace name. Pass null/undefined to reset to default store.
Returns: Promise<void>
Example:
// iOS: uses UserDefaults(suiteName:)
// Android: uses getSharedPreferences(name, MODE_PRIVATE)
await Prefs.setName('group.com.your.app');
Retrieves a value for a key.
Parameters:
key
(string) - The key to retrieve
Returns: Promise<string | null>
- The value or null if missing
Example:
const value = await Prefs.get('username');
if (value) {
console.log('Username:', value);
}
Stores a string value.
Parameters:
key
(string) - The key to storevalue
(string) - The value to store
Returns: Promise<void>
Example:
await Prefs.set('theme', 'dark');
await Prefs.set('lastLogin', new Date().toISOString());
Deletes a key.
Parameters:
key
(string) - The key to delete
Returns: Promise<void>
Example:
await Prefs.clear('temporaryData');
Checks if a key exists.
Parameters:
key
(string) - The key to check
Returns: Promise<boolean>
- True if key exists, false otherwise
Example:
const hasTheme = await Prefs.contains('theme');
if (hasTheme) {
console.log('Theme is configured');
}
Sets multiple keys at once.
Parameters:
values
(array) - Array of objects withkey
andvalue
properties
Returns: Promise<void>
Example:
await Prefs.setMultiple([
{ key: 'theme', value: 'dark' },
{ key: 'lang', value: 'en' },
{ key: 'notifications', value: 'true' },
]);
Retrieves multiple keys at once.
Parameters:
keys
(string[]) - Array of keys to retrieve
Returns: Promise<{ [key: string]: string | null }>
- Object with key-value pairs
Example:
const values = await Prefs.getMultiple(['theme', 'lang', 'notifications']);
console.log(values);
// { theme: 'dark', lang: 'en', notifications: 'true' }
Removes multiple keys at once.
Parameters:
keys
(string[]) - Array of keys to remove
Returns: Promise<void>
Example:
await Prefs.clearMultiple(['temp1', 'temp2', 'temp3']);
Returns all keys/values in the current store.
Returns: Promise<{ [key: string]: string }>
- Object with all key-value pairs
Example:
const allPrefs = await Prefs.getAll();
console.log('All preferences:', allPrefs);
Clears the current store.
Returns: Promise<void>
Example:
await Prefs.clearAll(); // ⚠️ Use with caution!
The library provides convenient React hooks for reactive state management with automatic updates and type safety.
Hook for managing string preferences with reactive updates.
Parameters:
key
(string) - The preference key
Returns: [value, setValue, contains, clear]
value
(string | null) - Current valuesetValue
(function) -(value: string) => Promise<void>
contains
(boolean) - Whether the key existsclear
(function) -() => Promise<void>
Example:
import { usePreferenceString } from 'react-native-turbo-preferences';
function UserSettings() {
const [username, setUsername, hasUsername, clearUsername] = usePreferenceString('username');
return (
<View>
<Text>Username: {username || 'Not set'}</Text>
<Text>Has username: {hasUsername ? 'Yes' : 'No'}</Text>
<Button title="Set" onPress={() => setUsername('John')} />
<Button title="Clear" onPress={clearUsername} />
</View>
);
}
Hook for managing numeric preferences with automatic type conversion.
Parameters:
key
(string) - The preference key
Returns: [value, setValue, contains, clear]
value
(number | null) - Current numeric valuesetValue
(function) -(value: number) => Promise<void>
contains
(boolean) - Whether the key existsclear
(function) -() => Promise<void>
Example:
import { usePreferenceNumber } from 'react-native-turbo-preferences';
function CounterSettings() {
const [count, setCount, hasCount, clearCount] = usePreferenceNumber('count');
return (
<View>
<Text>Count: {count ?? 0}</Text>
<Button title="Increment" onPress={() => setCount((count ?? 0) + 1)} />
<Button title="Reset" onPress={clearCount} />
</View>
);
}
Hook for managing boolean preferences with automatic type conversion.
Parameters:
key
(string) - The preference key
Returns: [value, setValue, contains, clear]
value
(boolean | null) - Current boolean valuesetValue
(function) -(value: boolean) => Promise<void>
contains
(boolean) - Whether the key existsclear
(function) -() => Promise<void>
Example:
import { usePreferenceBoolean } from 'react-native-turbo-preferences';
function NotificationSettings() {
const [notifications, setNotifications, hasNotifications, clearNotifications] =
usePreferenceBoolean('notifications');
return (
<View>
<Text>Notifications: {notifications ? 'Enabled' : 'Disabled'}</Text>
<Switch
value={notifications ?? false}
onValueChange={setNotifications}
/>
<Button title="Reset" onPress={clearNotifications} />
</View>
);
}
Hook for managing object preferences with automatic JSON serialization.
Parameters:
key
(string) - The preference keyT
(generic) - TypeScript type for the object
Returns: [value, setValue, contains, clear]
value
(T | null) - Current object valuesetValue
(function) -(value: T) => Promise<void>
contains
(boolean) - Whether the key existsclear
(function) -() => Promise<void>
Example:
import { usePreferenceObject } from 'react-native-turbo-preferences';
interface UserProfile {
name: string;
age: number;
email: string;
}
function ProfileSettings() {
const [profile, setProfile, hasProfile, clearProfile] =
usePreferenceObject<UserProfile>('userProfile');
const updateProfile = () => {
setProfile({
name: 'John Doe',
age: 30,
email: '[email protected]'
});
};
return (
<View>
<Text>Name: {profile?.name || 'Not set'}</Text>
<Text>Age: {profile?.age || 'Not set'}</Text>
<Text>Email: {profile?.email || 'Not set'}</Text>
<Button title="Update Profile" onPress={updateProfile} />
<Button title="Clear Profile" onPress={clearProfile} />
</View>
);
}
Hook for managing preference namespaces with reactive updates.
Returns: [currentNamespace, setNamespace, resetToDefault]
currentNamespace
(string) - Current namespace namesetNamespace
(function) -(namespace: string) => Promise<void>
resetToDefault
(function) -() => Promise<void>
Example:
import { usePreferenceNamespace } from 'react-native-turbo-preferences';
function NamespaceManager() {
const [namespace, setNamespace, resetToDefault] = usePreferenceNamespace();
return (
<View>
<Text>Current namespace: {namespace || 'Default'}</Text>
<Button title="User Settings" onPress={() => setNamespace('user_settings')} />
<Button title="App Config" onPress={() => setNamespace('app_config')} />
<Button title="Reset to Default" onPress={resetToDefault} />
</View>
);
}
- 🔄 Reactive Updates - Values automatically update when changed
- ⚡ Automatic Loading - Initial values loaded on mount
- 🎯 Type Safety - Full TypeScript support with proper types
- 🛡️ Error Handling - Built-in error handling with console warnings
- 🔧 Simple API - Consistent
[value, setValue, contains, clear]
pattern
import React from 'react';
import { View, Text, Switch, Button, TextInput } from 'react-native';
import {
usePreferenceString,
usePreferenceBoolean,
usePreferenceObject,
usePreferenceNamespace
} from 'react-native-turbo-preferences';
interface UserSettings {
theme: 'light' | 'dark';
fontSize: number;
language: string;
}
function SettingsScreen() {
// Namespace management
const [namespace, setNamespace, resetToDefault] = usePreferenceNamespace();
// Basic preferences
const [username, setUsername, hasUsername, clearUsername] = usePreferenceString('username');
const [notifications, setNotifications, , clearNotifications] = usePreferenceBoolean('notifications');
// Complex object preferences
const [settings, setSettings, hasSettings, clearSettings] =
usePreferenceObject<UserSettings>('userSettings');
const updateSettings = (newSettings: Partial<UserSettings>) => {
setSettings({ ...settings, ...newSettings });
};
return (
<View style={{ padding: 20 }}>
{/* Namespace Control */}
<Text>Current namespace: {namespace || 'Default'}</Text>
<Button title="User Prefs" onPress={() => setNamespace('user')} />
<Button title="App Prefs" onPress={() => setNamespace('app')} />
<Button title="Reset Namespace" onPress={resetToDefault} />
{/* String Preference */}
<Text>Username: {username || 'Not set'}</Text>
<TextInput
value={username || ''}
onChangeText={setUsername}
placeholder="Enter username"
/>
{/* Boolean Preference */}
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text>Notifications: </Text>
<Switch
value={notifications ?? false}
onValueChange={setNotifications}
/>
</View>
{/* Object Preference */}
<Text>Theme: {settings?.theme || 'Not set'}</Text>
<Button
title="Dark Theme"
onPress={() => updateSettings({ theme: 'dark' })}
/>
<Button
title="Light Theme"
onPress={() => updateSettings({ theme: 'light' })}
/>
{/* Clear actions */}
<Button title="Clear All" onPress={() => {
clearUsername();
clearNotifications();
clearSettings();
}} />
</View>
);
}
import Prefs from 'react-native-turbo-preferences';
class UserSettings {
static async saveUserPreferences(userId: string, preferences: any) {
const namespace = `user_${userId}`;
await Prefs.setName(namespace);
await Prefs.setMultiple([
{ key: 'theme', value: preferences.theme },
{ key: 'language', value: preferences.language },
{ key: 'notifications', value: String(preferences.notifications) },
]);
}
static async getUserPreferences(userId: string) {
const namespace = `user_${userId}`;
await Prefs.setName(namespace);
const values = await Prefs.getMultiple([
'theme',
'language',
'notifications',
]);
return {
theme: values.theme || 'light',
language: values.language || 'en',
notifications: values.notifications === 'true',
};
}
}
import Prefs from 'react-native-turbo-preferences';
class AppConfig {
static async initialize() {
// Check if first run
const isFirstRun = !(await Prefs.contains('appInitialized'));
if (isFirstRun) {
await Prefs.setMultiple([
{ key: 'appInitialized', value: 'true' },
{ key: 'version', value: '1.0.0' },
{ key: 'defaultTheme', value: 'system' },
]);
}
}
static async getConfig() {
const config = await Prefs.getMultiple([
'version',
'defaultTheme',
'lastUpdateCheck',
]);
return {
version: config.version || '1.0.0',
theme: config.defaultTheme || 'system',
lastUpdate: config.lastUpdateCheck
? new Date(config.lastUpdateCheck)
: null,
};
}
}
// Use default store
await Prefs.setName('');
// Use app group (iOS) or named file (Android)
await Prefs.setName('group.com.your.app');
// Use custom namespace
await Prefs.setName('UserSettings');
try {
await Prefs.set('key', 'value');
} catch (error) {
console.error('Failed to save preference:', error);
// Handle error appropriately
}
Method | Description | Parameters | Returns |
---|---|---|---|
setName(name) |
Switch namespace | name: string | null |
Promise<void> |
get(key) |
Retrieve value | key: string |
Promise<string | null> |
set(key, value) |
Store value | key: string, value: string |
Promise<void> |
clear(key) |
Delete key | key: string |
Promise<void> |
contains(key) |
Check existence | key: string |
Promise<boolean> |
setMultiple(values) |
Store multiple | values: Array<{key, value}> |
Promise<void> |
getMultiple(keys) |
Retrieve multiple | keys: string[] |
Promise<Record<string, string | null>> |
clearMultiple(keys) |
Delete multiple | keys: string[] |
Promise<void> |
getAll() |
Get all keys | None | Promise<Record<string, string>> |
clearAll() |
Clear store | None | Promise<void> |
Hook | Description | Parameters | Returns |
---|---|---|---|
usePreferenceString(key) |
String preference hook | key: string |
[value, setValue, contains, clear] |
usePreferenceNumber(key) |
Number preference hook | key: string |
[value, setValue, contains, clear] |
usePreferenceBoolean(key) |
Boolean preference hook | key: string |
[value, setValue, contains, clear] |
usePreferenceObject(key) |
Object preference hook | key: string |
[value, setValue, contains, clear] |
usePreferenceNamespace() |
Namespace management | None | [namespace, setNamespace, resetToDefault] |
⚠️ Important Security Notice
This library stores values in NSUserDefaults (iOS) and SharedPreferences (Android), which are NOT secure.
Do NOT store sensitive data:
- ❌ Passwords
- ❌ API tokens
- ❌ Credit card information
- ❌ Personal identification data
For secure storage, use:
- iOS: Keychain (
react-native-keychain
,expo-secure-store
) - Android: EncryptedSharedPreferences (
react-native-encrypted-storage
)
Best Practices:
- Only store non-sensitive app preferences
- Use namespaces to separate different data sets
- Implement proper data validation
- Consider encryption for sensitive data
Platform | Support | Notes |
---|---|---|
iOS | ✅ | iOS 11.0+ (NSUserDefaults) |
Android | ✅ | API Level 21+ (SharedPreferences) |
React Native | ✅ | 0.75+ with New Architecture enabled |
Expo | ✅ | Development builds & EAS builds |
Try the interactive demo in the example/
folder:
# Navigate to example
cd example
# Install dependencies
yarn install
# Start the demo
yarn start
The example app demonstrates:
- ✅ All API methods
- ✅ React Hooks usage
- ✅ Namespace switching
- ✅ Batch operations
- ✅ Error handling
- ✅ Real-time updates
- ✅ Tab navigation (Normal API, Hooks, Benchmarks)
Run the test suite:
# Run all tests
yarn test
# Run tests in watch mode
yarn test --watch
# Run tests with coverage
yarn test --coverage
Operation | iOS | Android | iOS Ops/sec | Android Ops/sec |
---|---|---|---|---|
Single Set (100) | 32ms | 232ms | 3,117 | 431 |
Single Get (100) | 78ms | 100ms | 1,277 | 995 |
Batch Set (100) | ~0.1ms | 9ms | 331,950 | 11,700 |
Batch Get (100) | 85ms | 6ms | 1,172 | 18,000 |
Namespace Switch | 2ms | 77ms | 33,123 | 646 |
Memory overhead | ~4B | ~12KB | 0.04B per operation | 0.12 KB per operation |
Note: All benchmarks from real device testing. iOS shows superior performance in most operations with ultra-low memory footprint.
Android Memory Testing Results (Real Device):
Test Type | Operations | Memory Used | Memory per Operation | Notes |
---|---|---|---|---|
Regular Test | 100 | 12 KB | 0.12 KB | Efficient memory usage |
Stress Test | 1,000 | 96 KB | 0.096 KB | Scales linearly, excellent efficiency |
iOS Memory Testing Results (Real Device - iPhone SE):
Test Type | Operations | Memory Used | Memory per Operation | Notes |
---|---|---|---|---|
Regular Test | 100 | 4B | 0.04B | Ultra-efficient memory usage |
Stress Test | 1,000 | 28B | 0.028B | Exceptional scalability |
Key Findings:
Android Memory Performance:
- Ultra-low memory overhead: Only 0.12 KB per operation (100 ops = 12 KB total)
- Excellent scalability: 0.096 KB per operation at scale (1000 ops = 96 KB total)
- Linear memory scaling: Memory usage grows predictably: 12 KB → 96 KB (8x operations = 8x memory)
- Production efficiency: 96 KB for 1000 operations - suitable for high-frequency apps
- Memory consistency: 13 KB baseline overhead maintained across all operations
iOS Memory Performance:
- Exceptional memory efficiency: Only 0.04B per operation (100 ops = 4B total)
- Outstanding scalability: 0.028B per operation at scale (1000 ops = 28B total)
- Ultra-low baseline: 4B baseline overhead maintained across all operations
- Memory advantage: 3000x more memory efficient than Android
Cross-Platform Insights:
- iOS dominance: Superior performance in most operations with ultra-low memory footprint
- Android reliability: Solid performance with excellent memory scaling
- Production ready: Both platforms show excellent efficiency for high-frequency apps
Memory testing performed on Samsung SM-A525F (Android 14) and iPhone SE (iOS 18) with real device benchmarks.
- Node.js 18 or higher
- React Native 0.75+
- iOS: Xcode 12+, iOS 11.0+
- Android: Android Studio, API Level 21+
# Clone the repository
git clone https://github.com/hamzamekk/react-native-turbo-preferences.git
cd react-native-turbo-preferences
# Install dependencies
yarn install
# Build the project
yarn prepare
# Run tests
yarn test
# Type checking
yarn typecheck
yarn prepare # Build for production
yarn test # Run tests
yarn typecheck # TypeScript checking
yarn lint # Lint code
yarn example # Run example app
- ✅ Basic key-value operations
- ✅ Cross-platform support
- ✅ New Architecture (TurboModule)
- ✅ Batch operations
- ✅ Namespace support
- ✅ TypeScript definitions
- ✅ Performance monitoring & benchmarking (iOS + Android)
- ✅ Memory footprint analysis (iOS + Android)
- ✅ React hooks (usePreferenceString, usePreferenceNumber, usePreferenceBoolean, usePreferenceObject, usePreferenceNamespace)
Contributions are welcome! Please read our contributing guidelines.
- Fork the repository
- Create a new branch:
git checkout -b feature/amazing-feature
- Make your changes
- Test your changes:
yarn test
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- Open a Pull Request
We use ESLint and Prettier. Run:
yarn lint
yarn lint:fix
How do I handle errors in hooks?
Hooks handle errors internally and log warnings to console. For custom error handling:
const [value, setValue] = usePreferenceString('key');
const handleSave = async () => {
try {
await setValue('new value');
console.log('Saved successfully!');
} catch (error) {
console.error('Save failed:', error);
// Show user feedback
}
};
Do hooks automatically sync between components?
No, hooks don't automatically sync. Each hook instance manages its own state. If you need real-time sync between components, consider using a state management library like Redux or Zustand with the imperative API.
What's the difference between namespaces and keys?
- Namespace: Different storage "files" (like
user_settings
,app_config
) - Keys: Individual preferences within a namespace (like
username
,theme
)
// Switch to user namespace
await Prefs.setName('user_settings');
await Prefs.set('username', 'John'); // Stored in user_settings
// Switch to app namespace
await Prefs.setName('app_config');
await Prefs.set('username', 'Admin'); // Different storage!
Can I store complex objects?
Yes! Use usePreferenceObject
or store JSON strings manually:
// With hook (recommended)
const [user, setUser] = usePreferenceObject<{ name: string; age: number }>(
'user'
);
// Manual approach
await Prefs.set('user', JSON.stringify({ name: 'John', age: 30 }));
const userStr = await Prefs.get('user');
const user = userStr ? JSON.parse(userStr) : null;
Is data encrypted or secure?
No! This library uses NSUserDefaults (iOS) and SharedPreferences (Android), which store data in plain text.
Never store sensitive data like:
- Passwords, tokens, credit cards
- Personal identification numbers
- Any confidential information
For secure storage, use:
react-native-keychain
(iOS Keychain)react-native-encrypted-storage
(Android EncryptedSharedPreferences)
This project is licensed under the MIT License - see the LICENSE file for details.
- Thanks to the React Native team for the New Architecture
- Inspired by the need for better performance in React Native apps
- Built with modern TypeScript and React Native best practices
- 🐛 Issues: GitHub Issues
- 💡 Request a Feature: Feature Requests
- 📖 Documentation: Full API Docs
- 🌟 Star this repo if you found it helpful!
- 💬 Discussions: GitHub Discussions
Made with ❤️ by hamzamekk