Modern TurboModule wrapper for iOS NSUserDefaults and Android SharedPreferences.
npm install react-native-turbo-preferences




> โก 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
``bash`
npm install react-native-turbo-preferences
`bash`
yarn add react-native-turbo-preferences
For React Native (Bare):
`bash`
npx pod-install
For Expo:
This package works with EAS builds.
`typescript
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');
`
`typescript
import { usePreferenceString, usePreferenceNamespace } from 'react-native-turbo-preferences';
function UserProfile() {
const [username, setUsername, hasUsername, clearUsername] = usePreferenceString('username');
const [namespace, setNamespace, resetToDefault] = usePreferenceNamespace();
return (
title="Set Username"
onPress={() => setUsername('Hamza')}
/>
title="Clear Username"
onPress={clearUsername}
/>
);
}
`
#### setName(name?: string | null): Promise
Switches the storage namespace.
Parameters:
- name (string, optional) - Namespace name. Pass null/undefined to reset to default store.
Returns: Promise
Example:
`typescript`
// iOS: uses UserDefaults(suiteName:)
// Android: uses getSharedPreferences(name, MODE_PRIVATE)
await Prefs.setName('group.com.your.app');
#### get(key: string): Promise
Retrieves a value for a key.
Parameters:
- key (string) - The key to retrieve
Returns: Promise - The value or null if missing
Example:
`typescript`
const value = await Prefs.get('username');
if (value) {
console.log('Username:', value);
}
#### set(key: string, value: string): Promise
Stores a string value.
Parameters:
- key (string) - The key to storevalue
- (string) - The value to store
Returns: Promise
Example:
`typescript`
await Prefs.set('theme', 'dark');
await Prefs.set('lastLogin', new Date().toISOString());
#### clear(key: string): Promise
Deletes a key.
Parameters:
- key (string) - The key to delete
Returns: Promise
Example:
`typescript`
await Prefs.clear('temporaryData');
#### contains(key: string): Promise
Checks if a key exists.
Parameters:
- key (string) - The key to check
Returns: Promise - True if key exists, false otherwise
Example:
`typescript`
const hasTheme = await Prefs.contains('theme');
if (hasTheme) {
console.log('Theme is configured');
}
#### setMultiple(values: { key: string; value: string }[]): Promise
Sets multiple keys at once.
Parameters:
- values (array) - Array of objects with key and value properties
Returns: Promise
Example:
`typescript`
await Prefs.setMultiple([
{ key: 'theme', value: 'dark' },
{ key: 'lang', value: 'en' },
{ key: 'notifications', value: 'true' },
]);
#### getMultiple(keys: string[]): Promise<{ [key: string]: string | null }>
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:
`typescript`
const values = await Prefs.getMultiple(['theme', 'lang', 'notifications']);
console.log(values);
// { theme: 'dark', lang: 'en', notifications: 'true' }
#### clearMultiple(keys: string[]): Promise
Removes multiple keys at once.
Parameters:
- keys (string[]) - Array of keys to remove
Returns: Promise
Example:
`typescript`
await Prefs.clearMultiple(['temp1', 'temp2', 'temp3']);
#### getAll(): Promise<{ [key: string]: string }>
Returns all keys/values in the current store.
Returns: Promise<{ [key: string]: string }> - Object with all key-value pairs
Example:
`typescript`
const allPrefs = await Prefs.getAll();
console.log('All preferences:', allPrefs);
#### clearAll(): Promise
Clears the current store.
Returns: Promise
Example:
`typescript`
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) => Promisecontains
- (boolean) - Whether the key existsclear
- (function) - () => Promise
Example:
`typescript
import { usePreferenceString } from 'react-native-turbo-preferences';
function UserSettings() {
const [username, setUsername, hasUsername, clearUsername] = usePreferenceString('username');
return (
);
}
`
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) => Promisecontains
- (boolean) - Whether the key existsclear
- (function) - () => Promise
Example:
`typescript
import { usePreferenceNumber } from 'react-native-turbo-preferences';
function CounterSettings() {
const [count, setCount, hasCount, clearCount] = usePreferenceNumber('count');
return (
);
}
`
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) => Promisecontains
- (boolean) - Whether the key existsclear
- (function) - () => Promise
Example:
`typescript
import { usePreferenceBoolean } from 'react-native-turbo-preferences';
function NotificationSettings() {
const [notifications, setNotifications, hasNotifications, clearNotifications] =
usePreferenceBoolean('notifications');
return (
onValueChange={setNotifications}
/>
);
}
`
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) => Promisecontains
- (boolean) - Whether the key existsclear
- (function) - () => Promise
Example:
`typescript
import { usePreferenceObject } from 'react-native-turbo-preferences';
interface UserProfile {
name: string;
age: number;
email: string;
}
function ProfileSettings() {
const [profile, setProfile, hasProfile, clearProfile] =
usePreferenceObject
const updateProfile = () => {
setProfile({
name: 'John Doe',
age: 30,
email: 'john@example.com'
});
};
return (
);
}
`
Hook for managing preference namespaces with reactive updates.
Returns: [currentNamespace, setNamespace, resetToDefault]
- currentNamespace (string) - Current namespace namesetNamespace
- (function) - (namespace: string) => PromiseresetToDefault
- (function) - () => Promise
Example:
`typescript
import { usePreferenceNamespace } from 'react-native-turbo-preferences';
function NamespaceManager() {
const [namespace, setNamespace, resetToDefault] = usePreferenceNamespace();
return (
);
}
`
- ๐ 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
`typescript
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
const updateSettings = (newSettings: Partial
setSettings({ ...settings, ...newSettings });
};
return (
{/ Namespace Control /}
{/ String Preference /}
onChangeText={setUsername}
placeholder="Enter username"
/>
{/ Boolean Preference /}
onValueChange={setNotifications}
/>
{/ Object Preference /}
onPress={() => updateSettings({ theme: 'dark' })}
/>
onPress={() => updateSettings({ theme: 'light' })}
/>
{/ Clear actions /}
clearUsername();
clearNotifications();
clearSettings();
}} />
);
}
`
`typescript
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',
};
}
}
`
`typescript
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,
};
}
}
`
`typescript
// 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');
`
`typescript`
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 |get(key)
| | Retrieve value | key: string | Promise |set(key, value)
| | Store value | key: string, value: string | Promise |clear(key)
| | Delete key | key: string | Promise |contains(key)
| | Check existence | key: string | Promise |setMultiple(values)
| | Store multiple | values: Array<{key, value}> | Promise |getMultiple(keys)
| | Retrieve multiple | keys: string[] | Promise |clearMultiple(keys)
| | Delete multiple | keys: string[] | Promise |getAll()
| | Get all keys | None | Promise |clearAll()
| | Clear store | None | Promise |
| 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)react-native-encrypted-storage
- Android: EncryptedSharedPreferences ()
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:
`bashNavigate to example
cd example
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)
๐งช Testing
Run the test suite:
`bash
Run all tests
yarn testRun tests in watch mode
yarn test --watchRun tests with coverage
yarn test --coverage
`๐ Performance
| 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.
$3
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.
๐ Development
$3
- Node.js 18 or higher
- React Native 0.75+
- iOS: Xcode 12+, iOS 11.0+
- Android: Android Studio, API Level 21+
$3
`bash
Clone the repository
git clone https://github.com/hamzamekk/react-native-turbo-preferences.git
cd react-native-turbo-preferencesInstall dependencies
yarn installBuild the project
yarn prepareRun tests
yarn testType checking
yarn typecheck
`$3
`bash
yarn prepare # Build for production
yarn test # Run tests
yarn typecheck # TypeScript checking
yarn lint # Lint code
yarn example # Run example app
`๐บ Roadmap
- [x] โ
Basic key-value operations
- [x] โ
Cross-platform support
- [x] โ
New Architecture (TurboModule)
- [x] โ
Batch operations
- [x] โ
Namespace support
- [x] โ
TypeScript definitions
- [x] โ
Performance monitoring & benchmarking (iOS + Android)
- [x] โ
Memory footprint analysis (iOS + Android)
- [x] โ
React hooks (usePreferenceString, usePreferenceNumber, usePreferenceBoolean, usePreferenceObject, usePreferenceNamespace)
๐ค Contributing
Contributions are welcome! Please read our contributing guidelines.
$3
1. Fork the repository
2. Create a new branch:
git checkout -b feature/amazing-feature
3. Make your changes
4. Test your changes: yarn test
5. Commit your changes: git commit -m 'Add amazing feature'
6. Push to the branch: git push origin feature/amazing-feature
7. Open a Pull Request$3
We use ESLint and Prettier. Run:
`bash
yarn lint
yarn lint:fix
`โ FAQ
How do I handle errors in hooks?
Hooks handle errors internally and log warnings to console. For custom error handling:
`typescript
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)`typescript
// 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:`typescript
// 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