Scaling React Native with Zustand: The Slices Pattern
In 2025, the debate isn't about which state library to use—it's about how to structure the one you chose. At Insyllium, we moved our entire React Native fleet to Zustand not just for its size, but for its unopinionated flexibility.
The "Client vs. Server" Split
The biggest mistake I see in modern apps is storing server data in global stores. If it comes from an API, it belongs in TanStack Query. Zustand should be reserved strictly for client ownership:
- UI State (Modals, Sidebar status)
- Session State (Auth tokens, User preferences)
- Complex Multi-step Form Data
The Slices Pattern
As our app grew, a single store file became unmanageable. We adopted the Slices Pattern to keep domains isolated while sharing a single store instance.
// store/createAuthSlice.ts
export const createAuthSlice = (set) => ({
user: null,
login: (user) => set({ user }),
})
// store/index.ts
export const useBoundStore = create((...a) => ({
...createAuthSlice(...a),
...createCartSlice(...a),
}))
Persistence with MMKV
For offline persistence, AsyncStorage is too slow for large state trees. We use Zustand's persist middleware coupled with react-native-mmkv. It’s synchronous, roughly 30x faster, and ensures the store is hydrated before the first render frame.