Subscribe to a vanilla store outside React
Use a vanilla store when browser code outside React needs to read state, update it, or react to changes without rendering a React component. The page below shows two live values; clicking either button updates its value through a selected subscription.
Create the store and subscribe
-
Install
zustand:bashnpm install zustand -
Put this markup in the page that loads your TypeScript entry point. It gives the example a mounted UI with two controls and two value displays:
html<section> <p>X: <output id="x-value"></output></p> <button id="increment-x" type="button">Increment X</button> <p>Y: <output id="y-value"></output></p> <button id="increment-y" type="button">Increment Y</button> </section> -
In that entry point, create a store with
createStorefromzustand/vanilla. Wrap its state creator withsubscribeWithSelectorfromzustand/middleware, then subscribe to each coordinate:tsimport { subscribeWithSelector } from 'zustand/middleware' import { createStore } from 'zustand/vanilla' type Coordinates = { x: number; y: number } const store = createStore<Coordinates>()( subscribeWithSelector(() => ({ x: 0, y: 0 })), ) const xValue = document.getElementById('x-value')! const yValue = document.getElementById('y-value')! const incrementXButton = document.getElementById('increment-x')! const incrementYButton = document.getElementById('increment-y')! const initialState = store.getState() xValue.textContent = String(initialState.x) yValue.textContent = String(initialState.y) store.subscribe( (state) => state.x, (x, previousX) => { xValue.textContent = `${x} (was ${previousX})` }, { fireImmediately: true }, ) store.subscribe( (state) => state.y, (y, previousY) => { yValue.textContent = `${y} (was ${previousY})` }, { fireImmediately: true }, ) incrementXButton.addEventListener('click', () => { store.setState((state) => ({ x: state.x + 1 })) }) incrementYButton.addEventListener('click', () => { store.setState((state) => ({ y: state.y + 1 })) })
For the vanilla store's API surface, see React and vanilla stores. Here, subscription callbacks update ordinary DOM outputs directly, without a React component rendering the values.
Selector subscription options
The selector form accepts an optional third argument:
| Option | Type | Default | What it does |
|---|---|---|---|
equalityFn | (a: U, b: U) => boolean | Object.is | Compares the current and next selector results; the callback runs when they are not equal according to this function. |
fireImmediately | boolean | false | Calls the callback as soon as it subscribes, passing the current selected value as both the new and previous values. |
Use the default comparison for scalar selections such as state.x. If you select a newly created object or array, provide an equality function appropriate to that value so equivalent selections do not trigger the callback.
Pitfalls
- For the
subscribeand middleware API details, see Use a vanilla store in React; here, each DOM output tracks only its own coordinate. - When middleware changes the
setorgetfunctions supplied to a state creator, those changes do not carry over to the vanilla store'ssetStateandgetStateutilities. Use the store's actions when you need the middleware-wrapped behavior. - For the Next.js shared-store and React Server Component caveat, see Set up Zustand in Next.js.
Live demo
Try the Zustand live demo to see a running Zustand application.
Was this page helpful?