Documentation Index
Fetch the complete documentation index at: https://mintlify.com/christianbaroni/stores/llms.txt
Use this file to discover all available pages before exploring further.
@storesjs/stores works in React Native with the same API you use on the web. The Metro bundler picks up the react-native package export condition automatically, so the native-tuned build is selected without any special imports or configuration. Persistence, sync, derived stores, and query stores all behave identically — MMKV replaces localStorage as the storage backend, and internal batching adapts to React Native’s scheduler.
Automatic Package Condition
Metro resolves the react-native condition from package.json exports, which points to the native-optimized build:
// Same import on every platform — Metro selects the right build automatically
import { createBaseStore, configureStores } from '@storesjs/stores';
You do not need to change your import paths, add aliases, or configure anything in metro.config.js.
Installing MMKV
react-native-mmkv is the default storage backend for persisted stores on React Native. It is listed as an optional peer dependency and must be installed separately:
npm install react-native-mmkv
After installation, pod install for iOS:
Once react-native-mmkv is available, @storesjs/stores loads it automatically when a persisted store is first accessed. No further setup is required.
react-native-mmkv is a peer dependency — it will not be installed automatically when you install @storesjs/stores. If it is missing and a persisted store is created, the framework throws a descriptive error at runtime pointing to the installation steps.
Persistence on React Native
Persistence works identically to web. Add storageKey to your store options and MMKV takes over transparently:
import { createBaseStore } from '@storesjs/stores';
type Settings = {
currency: string;
locale: string;
setCurrency: (currency: string) => void;
};
export const settingsStore = createBaseStore<Settings>(
set => ({
currency: 'USD',
locale: 'en-US',
setCurrency: currency => set({ currency }),
}),
{
storageKey: 'settings',
partialize: state => ({ currency: state.currency, locale: state.locale }),
}
);
MMKV is synchronous, so setState returns void (not Promise<void>), and there is no need to await persistence operations.
Batching
React Native uses unstable_batchedUpdates from react-native for batching store subscriber notifications. The native build imports this automatically, so multiple state updates within the same synchronous block are batched into a single render pass — no configuration needed.
Write Throttling
On mobile devices, storage I/O is more expensive than on desktop browsers. The default persistThrottleMs values reflect this:
| Platform | Default persistThrottleMs |
|---|
| iOS | 3 000 ms (3 seconds) |
| Android | 5 000 ms (5 seconds) |
| Web | 200 ms |
You can override this per-store. Decrease it for state that must be durable immediately; increase it for state that changes frequently and can tolerate a longer flush window:
export const positionStore = createBaseStore<PositionState>(
set => ({
lat: 0,
lng: 0,
setPosition: (lat, lng) => set({ lat, lng }),
}),
{
storageKey: 'map-position',
partialize: state => ({ lat: state.lat, lng: state.lng }),
persistThrottleMs: 1_000, // flush every second for location data
}
);
Custom MMKV Instance
If your app already uses a custom MMKV instance — for example one with encryption enabled — you can wrap it in a SyncStorageInterface and pass it to configureStores:
import { configureStores } from '@storesjs/stores';
import { MMKV } from 'react-native-mmkv';
import type { SyncStorageInterface } from '@storesjs/stores';
const mmkv = new MMKV({ id: 'myapp', encryptionKey: 'secret' });
const mmkvAdapter: SyncStorageInterface<string> = {
clearAll: () => mmkv.clearAll(),
contains: key => mmkv.contains(key),
delete: key => mmkv.delete(key),
get: key => mmkv.getString(key),
getAllKeys: () => mmkv.getAllKeys(),
set: (key, value) => mmkv.set(key, value),
};
configureStores({ storage: mmkvAdapter });
All stores will then use your custom MMKV instance instead of the auto-detected default.
Full Example
import { createBaseStore, createQueryStore, configureStores } from '@storesjs/stores';
// Configure once in your app entry file
configureStores({
storageKeyPrefix: 'myapp:',
});
// Base store with persistence
export const themeStore = createBaseStore(
set => ({
mode: 'light' as 'light' | 'dark',
setMode: (mode: 'light' | 'dark') => set({ mode }),
}),
{
storageKey: 'theme',
persistThrottleMs: 500,
}
);
// Query store — persistence and fetching work the same as on web
export const profileStore = createQueryStore(
{
fetcher: fetchProfile,
params: { userId: $ => $(authStore).userId },
staleTime: 5 * 60 * 1000,
},
{ storageKey: 'profile' }
);
import { View, Text } from 'react-native';
function ThemeToggle() {
const mode = themeStore(s => s.mode);
const setMode = themeStore(s => s.setMode);
return (
<View>
<Text onPress={() => setMode(mode === 'light' ? 'dark' : 'light')}>
Current theme: {mode}
</Text>
</View>
);
}
Call configureStores as early as possible in your app’s entry point — before any store module is imported. In a React Native app this is typically index.js or App.tsx, above the root component definition. The framework locks its configuration on the first store creation, so any configureStores call that comes after will throw in development.