- One Tip a Week
- Posts
- One Tip a Week: Zustand Middleware & Storage
One Tip a Week: Zustand Middleware & Storage
This week’s tip of the week is about Zustand.
It comes from a PR I currently have open, pomerium/mcp-app-demo/pull/160. I originally was using the useLocalStorage
custom hook but opted for Zustand’s persist middleware.
Before: using
useLocalStorage
, state persisted but changes only appeared on the next renderAfter: with Zustand, updates apply instantly and state is automatically saved to storage and rehydrated on load
Here’s a simplified version of the background jobs store from that PR:
import { create } from "zustand";
import { persist } from "zustand/middleware";
import type { BackgroundJob } from "@/lib/schemas";
import { backgroundJobSchema } from "@/lib/schemas";
export const BACKGROUND_JOBS_STORAGE_KEY = "background-jobs";
interface BackgroundJobsState {
jobs: Record<string, BackgroundJob>;
addJob: (job: BackgroundJob) => void;
removeJob: (id: string) => void;
}
export const useBackgroundJobsStore = create<BackgroundJobsState>()(
persist(
(set) => ({
jobs: {},
addJob: (job) => {
const validated = backgroundJobSchema.parse(job);
set((state) => ({
jobs: { ...state.jobs, [validated.id]: validated },
}));
},
removeJob: (id) => {
set((state) => {
const { [id]: _, ...rest } = state.jobs;
return { jobs: rest };
});
},
}),
{
name: BACKGROUND_JOBS_STORAGE_KEY, // stored in localStorage by default
}
)
);
See the file in the PR for the full store with persist
in action.
Since no custom storage
is provided, Zustand automatically uses localStorage
. It also hydrates state on load, so your background jobs reappear as soon as the app initializes.
If you want a different backend, you can pass a custom storage option, for example sessionStorage
or IndexedDB via createJSONStorage
.
Zustand is awesome. Simple API, instant updates, and persistence that just works.
That’s it! Short and sweet. Until the next one!