đŸ» Multiple Stores with Zustand, Persist and Typescript


I could not find a good example of using Zustand with multiple stores, persist and TypeScript. So I decided to create one. I hope it will be useful for someone.

First we'll create a simple store to keep track of the theme the user has selected which we'll persist in localStorage.

themeSlice.tsx
import { StoreSlice } from './store'; type themeType = 'light' | 'dark'; export type ThemeSlice = { theme: themeType; toggleTheme: () => void; }; export const themeSlice: StoreSlice<ThemeSlice> = (set) => ({ theme: 'light', toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })) });
themeSlice.tsx
import { StoreSlice } from './store'; type themeType = 'light' | 'dark'; export type ThemeSlice = { theme: themeType; toggleTheme: () => void; }; export const themeSlice: StoreSlice<ThemeSlice> = (set) => ({ theme: 'light', toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })) });

Next we'll create a store to toggle the sidebar open and closed which we won't persist in localStorage.

sidebarSlice.tsx
import { StoreSlice } from './store'; export type SidebarSlice = { sidebarOpen: boolean; toggleSidebar: () => void; }; export const sidebarSlice: StoreSlice<SidebarSlice> = (set) => ({ sidebarOpen: false, toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })) });
sidebarSlice.tsx
import { StoreSlice } from './store'; export type SidebarSlice = { sidebarOpen: boolean; toggleSidebar: () => void; }; export const sidebarSlice: StoreSlice<SidebarSlice> = (set) => ({ sidebarOpen: false, toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })) });

Now we'll create a store to put all of our slices together and persist the theme slice in localStorage.

store.tsx
import create from 'zustand'; import { persist, StoreApiWithPersist } from 'zustand/middleware'; import { ThemeSlice, themeSlice } from './themeSlice'; import { SidebarSlice, sidebarSlice } from './sidebarSlice'; // Create a type for our store export type Storestate = ThemeSlice & SidebarSlice; // Create a type for our store slices export type StoreSlice<T> = ( set: SetState<StoreState>, get: GetState<StoreState> ) => T; // Create our store with the slices we created export const useStore = create( persist< StoreState, SetState<StoreState>, GetState<StoreState>, StoreApiWithPersist<StoreState> >( (set, get) => ({ ...themeSlice(set, get), ...sidebarSlice(set, get) }), { name: 'our-local-storage-key', partialize: (state) => ({ // Only persist the theme slice theme: state.theme }) } ) );
store.tsx
import create from 'zustand'; import { persist, StoreApiWithPersist } from 'zustand/middleware'; import { ThemeSlice, themeSlice } from './themeSlice'; import { SidebarSlice, sidebarSlice } from './sidebarSlice'; // Create a type for our store export type Storestate = ThemeSlice & SidebarSlice; // Create a type for our store slices export type StoreSlice<T> = ( set: SetState<StoreState>, get: GetState<StoreState> ) => T; // Create our store with the slices we created export const useStore = create( persist< StoreState, SetState<StoreState>, GetState<StoreState>, StoreApiWithPersist<StoreState> >( (set, get) => ({ ...themeSlice(set, get), ...sidebarSlice(set, get) }), { name: 'our-local-storage-key', partialize: (state) => ({ // Only persist the theme slice theme: state.theme }) } ) );


I love the way Zustand simplifies state management in React, this small reciepe can be scaled up to create a large application with multiple stores and slices with type safety.

📖 Reference