How to Access Zustand State Outside of React Components
You can access Zustand state outside React components by calling the static methods getState(), setState(), and subscribe() attached directly to the store hook, or by creating a vanilla store with createStore() from zustand/vanilla.
Accessing Zustand state outside of React components is a core feature of the pmndrs/zustand library. Whether you need to read state from a utility module, update state from an API response handler, or subscribe to changes in a Node.js script, the store exposes a vanilla JavaScript API that operates independently of React's component lifecycle.
The Store API Architecture
Zustand stores are created using either create (for React) or createStore (for vanilla JavaScript). Both functions return an API object containing four core utilities:
getState– returns the current state snapshotsetState– updates the state via shallow merge by defaultsubscribe– registers a listener for state changesgetInitialState– returns the state at store creation time
In src/vanilla.ts, these utilities are defined as the core store implementation (setState at lines 66-80, getState at line 83, and subscribe at lines 88-92). When you use create in src/react.ts, the hook is built and the API is mixed into it using Object.assign (lines 56-58), making these methods available directly on the exported hook.
Because the hook carries these static methods, you can call them anywhere—in plain JavaScript modules, utility functions, event listeners, or Node scripts—without importing React.
Reading and Writing State Outside Components
Retrieve Current State with getState()
To read the latest state snapshot non-reactively, call getState() directly on the store hook. This returns the current value without subscribing to future updates.
// counterStore.ts
import { create } from 'zustand';
export const useCounterStore = create<{
count: number;
inc: () => void;
}>()((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 })),
}));
// utility.ts - outside any React component
import { useCounterStore } from './counterStore';
// Get current count (non-reactive snapshot)
const currentCount = useCounterStore.getState().count;
console.log(currentCount); // → 0
Update State with setState()
Use setState() to update the store from outside React. You can pass a partial state object for shallow merging or an updater function that receives the current state.
import { useCounterStore } from './counterStore';
// Shallow merge update
useCounterStore.setState({ count: 10 });
// Functional update based on previous state
useCounterStore.setState((state) => ({ count: state.count + 5 }));
// Replace entire state (second argument = true)
useCounterStore.setState({ count: 0 }, true);
Subscribe to State Changes
The subscribe method listens to every state change and executes a callback. It returns an unsubscribe function that you must call to prevent memory leaks.
import { useCounterStore } from './counterStore';
// Subscribe to all changes
const unsubscribe = useCounterStore.subscribe((state) => {
console.log('Counter changed:', state.count);
});
// Later, cleanup the subscription
unsubscribe();
Using Vanilla Stores for Non-React Contexts
When you need a store that has no React dependencies—such as for Node.js scripts, shared logic across multiple React roots, or framework-agnostic modules—use createStore from zustand/vanilla. This exports the underlying vanilla implementation defined in src/vanilla.ts (lines 99-101) without the React binding.
// vanillaStore.ts
import { createStore } from 'zustand/vanilla';
export const counterStore = createStore<{
count: number;
inc: () => void;
}>((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 })),
}));
// Extract methods for convenience
export const { getState, setState, subscribe } = counterStore;
// usage.ts - Node.js script or vanilla JS module
import { getState, setState, subscribe } from './vanillaStore';
console.log('Initial:', getState().count); // 0
setState({ count: 42 });
console.log('Updated:', getState().count); // 42
const off = subscribe((state) => console.log('Changed:', state));
setState({ count: 100 }); // triggers listener
off(); // cleanup
Advanced Subscriptions with Selectors
For performance-critical scenarios outside React, you can subscribe to specific slices of state rather than every change using the subscribeWithSelector middleware from zustand/middleware. This allows you to provide a selector function and an equality function to control when the callback fires.
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';
export const useTodoStore = create(
subscribeWithSelector((set) => ({
todos: [] as string[],
add: (t: string) => set((s) => ({ todos: [...s.todos, t] })),
}))
);
// Subscribe to specific property changes outside React
const unsubscribe = useTodoStore.subscribe(
(state) => state.todos.length, // selector
(length, prevLength) => {
console.log(`Todo count changed from ${prevLength} to ${length}`);
},
{ equalityFn: (a, b) => a === b } // optional equality check
);
useTodoStore.getState().add('Buy milk'); // triggers callback
unsubscribe();
Summary
- Static methods on the hook: The
createfunction returns a hook withgetState,setState,subscribe, andgetInitialStateattached directly, enabling access outside React components. - Core implementation: These methods are defined in
src/vanilla.tsand attached to the React hook insrc/react.tsviaObject.assign. - Vanilla stores: Import
createStorefromzustand/vanillato create React-agnostic stores suitable for Node.js or shared logic. - Selective subscriptions: Use the
subscribeWithSelectormiddleware to listen to specific state slices rather than all changes. - Memory management: Always store and call the unsubscribe function returned by
subscribeto prevent memory leaks when components or modules unmount.
Frequently Asked Questions
Can I use Zustand without React at all?
Yes. Import createStore from zustand/vanilla to create a standalone store that has zero React dependencies. This is implemented in src/vanilla.ts and exports the same getState, setState, and subscribe utilities without the hook wrapper used in src/react.ts.
How do I get the initial state of a store outside a component?
Call getInitialState() on the store hook or vanilla store instance. This method returns the state as it existed when the store was first created, useful for resetting state or testing purposes.
Is getState() reactive like the useStore hook?
No. getState() returns a static snapshot of the current state at the moment of invocation. It does not re-execute when the state changes, nor does it trigger re-renders. To react to changes outside React, you must use subscribe() and manually handle updates.
Can I subscribe to a specific property of the store outside React?
Yes, by applying the subscribeWithSelector middleware from zustand/middleware. This extends the store's subscribe method to accept a selector function as the first argument, allowing you to listen only to changes in specific properties rather than every state mutation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →