Create a standalone store, then use a React component to display and update a selected value from it.
Use this pattern when non-React code updates state that a mounted React component displays; for how vanilla and hook-bound stores differ, see React and vanilla stores.
Create the store and mount its React consumer
Install zustand in your React project; see React and vanilla stores for the package entry-point distinctions, including zustand/vanilla.
bashnpm install zustand
In CounterApp.tsx, createStore owns the count and useStore selects it for the mounted component. Unlike a hook-bound counter, this example has a browser timer update the store outside the component.
tsximport { useEffect } from 'react'
import { createRoot } from 'react-dom/client'
import { useStore } from 'zustand'
import { createStore } from 'zustand/vanilla'
type CounterStore = {
count: number
}
const counterStore = createStore<CounterStore>()(() => ({ count: 0 }))
function Counter() {
const count = useStore(counterStore, (state) => state.count)
useEffect(() => {
const interval = window.setInterval(() => {
counterStore.setState((state) => ({ count: state.count + 1 }))
}, 1000)
return () => window.clearInterval(interval)
}, [])
return (
<section>
<output aria-live="polite">{count}</output>
</section>
)
}
function App() {
return <Counter />
}
const container = document.getElementById('root')!
createRoot(container).render(<App />)
Add <div id="root"></div> to the page and use this file as your browser entry point. In the browser, the mounted output starts at 0 and advances once per second as the timer updates the store. The timer callback runs independently of React rendering; the selected count causes the component to display each update.
Selector and store API
For the comparison of vanilla and hook-bound store APIs, see React and vanilla stores. This page focuses on selecting the value the mounted React view renders.
| Option | Type | Default | What it does |
|---|---|---|---|
useStore selector | (state: CounterStore) => U | Identity selector when omitted | Returns the selected value for the component. This example selects count. |
The initializer's set updates state immutably. For an object update such as { count: nextCount }, Zustand shallowly merges the returned fields into the current state; keep nested updates immutable as well. The vanilla store's subscribe method is available for non-React subscribers and returns an unsubscribe function.
Pitfalls
- If middleware changes
setorget, those changes do not apply to the vanilla store'ssetStateandgetStatemethods. See Subscribe outside React. - In Next.js, avoid sharing a global store across simultaneous server requests, and do not read or write it from React Server Components. See Set up Zustand in Next.js.
See it running
Try the Zustand live demo to see Zustand running in a browser.
Was this page helpful?