Zustand is the minimalist state manager for React. No providers, no boilerplate, no context — just a hook and a store.
Create a Store in 5 Lines
import { create } from "zustand";
interface Store {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
const useStore = create<Store>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
// Use it — no Provider needed!
function Counter() {
const count = useStore((s) => s.count);
const increment = useStore((s) => s.increment);
return <button onClick={increment}>{count}</button>;
}
Slices Pattern: Scalable Stores
const createScraperSlice = (set) => ({
scrapers: [],
activeScraper: null,
addScraper: (scraper) => set((s) => ({ scrapers: [...s.scrapers, scraper] })),
setActive: (id) => set({ activeScraper: id }),
});
const createDataSlice = (set) => ({
results: [],
loading: false,
fetchResults: async (scraperId) => {
set({ loading: true });
const data = await fetch(`/api/results/${scraperId}`).then(r => r.json());
set({ results: data, loading: false });
},
});
const useStore = create((...a) => ({
...createScraperSlice(...a),
...createDataSlice(...a),
}));
Middleware: Persist, DevTools, Immer
import { create } from "zustand";
import { persist, devtools } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
const useStore = create(
devtools(
persist(
immer((set) => ({
items: [],
addItem: (item) => set((state) => {
state.items.push(item); // Immer — mutate freely!
}),
removeItem: (id) => set((state) => {
state.items = state.items.filter(i => i.id !== id);
}),
})),
{ name: "scraper-store" } // localStorage key
)
)
);
Subscribe Outside React
// Use store anywhere — not just components
const unsubscribe = useStore.subscribe(
(state) => state.results,
(results) => {
console.log("Results updated:", results.length);
analytics.track("results_changed", { count: results.length });
}
);
// Get state imperatively
const currentState = useStore.getState();
useStore.setState({ loading: true });
Async Actions
const useStore = create((set, get) => ({
data: null,
error: null,
loading: false,
fetchData: async (url) => {
if (get().loading) return; // Prevent double-fetch
set({ loading: true, error: null });
try {
const res = await fetch(url);
const data = await res.json();
set({ data, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
Selector Optimization
import { useShallow } from "zustand/react/shallow";
// Only re-render when these specific fields change
const { items, loading } = useStore(
useShallow((s) => ({ items: s.items, loading: s.loading }))
);
Managing scraped data state? My Apify tools deliver data ready for your Zustand stores.
Custom solution? Email spinov001@gmail.com
Top comments (0)