Don't use 'persist' middleware for Zustand in React Native

Published on 25 May 2025
Don't use 'persist' middleware for Zustand in React Native

Zustand is great for React Native apps if you need a simple but effective state management solution. It also has persist middleware that lets you persist app's state across reloads. That's pretty helpful if you want offline kind of app and want to load your initial state instantly from the persistent storage.

How to use?

Consider my use case :- I am building an offline music player that fetches tracks from the database (I am using Pocketbase), downloads all of them, and then store them locally. I was using persist middleware to handle those. Take a look at this example.

// store/library.ts

import { TrackWithPlaylist } from "@/helpers/types";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import { persistStorage, StorageKeys } from "./index";

interface LibraryState {
    tracks: TrackWithPlaylist[];
}

export const useLibraryStore = create<LibraryState>()(
    persist(
        (): LibraryState => ({
            tracks: [],
        }),
        {
            name: StorageKeys.Library,
            storage: createJSONStorage(() => persistStorage),
        }
    )
);

// Add to track list
export function addTracks(tracks: TrackWithPlaylist[]) {
    useLibraryStore.setState({ tracks });
}

And, this is the code for persistStorage

// store/index.ts

import AsyncStorage from "@react-native-async-storage/async-storage";

export const persistStorage = {
    getItem: (key: string) => AsyncStorage.getItem(key),
    setItem: (key: string, value: string) => AsyncStorage.setItem(key, value),
    removeItem: (key: string) => AsyncStorage.removeItem(key),
};

export enum StorageKeys {
    Library = "library-storage",
    Playlist = "playlist-storage",
    Queue = "queue-storage",
    RepeatEnabled = "repeat-enabled",
}

Here, I am using AsyncStorage for storing the 'stringified' value of the state which Zustand will try to save in the local storage. Pretty straight forward!

The problem

When I loaded 10 tracks, the app was running fine, and it also loaded the tracks from storage if you freshly opened the app. Everything worked great! But when I loaded more than 20 songs, everything started to crumble. The issue is, for each track I am loading, I am saving all the information about the track such as title, artist, album, year, etc., along with the artwork. Now, the artwork for a track is stored as a base64 string, which is a really long string. It's fine for a couple of tracks, but the whole track list becomes huge when you have more than 20 tracks. And of course, you will not save only 20 tracks for an offline music player, right?

What exact issue did I face?

The player was loading the tracks just fine, but nothing was being stored locally, and when you opened the app again, there were no tracks saved. And every now and then, when I selected a track (that's when I add tracks to the queue), the app was crashing. There were no warnings, no errors in the console, and I spent a few hours debugging it before figuring out what was really happening.

The fix?

Don't use persist if you know your state will be huge in any way. Now, I am still using Zustand, but only for loading the state when the app loads. And instead of the persist middleware, I am using good old SQL; specifically, Knex with expo-sqlite, and here is an example of how you integrate it in React Native.  

Thank you so much for reading!

Get in touch

If you happen to have a new brilliant idea and want an energetic intelligent companion to work on it, feel free to reach out to me.