Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,39 @@ on:
branches: [main]
workflow_dispatch:

# Cancel previous run (see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency)
permissions:
contents: read
pages: write
id-token: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
group: pages
cancel-in-progress: true

env:
GITHUB_PAGES: true

jobs:
build:
uses: pmndrs/docs/.github/workflows/build.yml@v3
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep deploying to the current website.
So, please do not modify workflows/docs.yml and make it work.

Create a new workflows/website.yml for rspress.

Copy link
Copy Markdown
Collaborator Author

@dbritto-dev dbritto-dev Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dai-shi can't be separated because both use gh pages to publish the website. I could try to publish both under different paths but that would be tricky to implement

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I wasn't aware that gh pages is conflicting. Hmm.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with:
mdx: 'docs'
libname: 'Zustand'
home_redirect: '/learn/getting-started/introduction'
icon: '/favicon.ico'
logo: '/bear.jpg'
github: 'https://github.com/pmndrs/zustand'
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: 20
cache: pnpm
- run: pnpm --dir website install
- run: pnpm --dir website build
- uses: actions/upload-pages-artifact@v4
with:
path: website/doc_build

deploy:
needs: build
runs-on: ubuntu-latest

# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source

# Deploy to the github-pages environment
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="./docs/bear.jpg" />
<img src="./docs/public/bear.jpg" />
</p>

[![Build Status](https://img.shields.io/github/actions/workflow/status/pmndrs/zustand/test.yml?branch=main&style=flat&colorA=000000&colorB=000000)](https://github.com/pmndrs/zustand/actions?query=workflow%3ATest)
Expand Down Expand Up @@ -311,7 +311,7 @@ const useFishStore = create(
)
```

[See the full documentation for this middleware.](./docs/reference/integrations/persisting-store-data.md)
[See the full documentation for this middleware.](./docs/learn/integrations/persisting-store-data.md)

## Immer middleware

Expand Down Expand Up @@ -505,7 +505,7 @@ A more detailed TypeScript guide is [here](docs/learn/guides/beginner-typescript

## Third-Party Libraries

Some users may want to extend Zustand's feature set which can be done using third-party libraries made by the community. For information regarding third-party libraries with Zustand, visit [the doc](./docs/reference/integrations/third-party-libraries.md).
Some users may want to extend Zustand's feature set which can be done using third-party libraries made by the community. For information regarding third-party libraries with Zustand, visit [the doc](./docs/learn/integrations/third-party-libraries.md).

## Comparison with other libraries

Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ hero:
actions:
- theme: brand
text: Introduction
link: ./learn/getting-started/introduction.md
link: ./learn/getting-started/introduction
- theme: alt
text: Quick Start
link: ./learn/index.md
link: ./learn
features:
- title: Minimal API, fast adoption
details: Create a store with a single hook, subscribe with selectors, and avoid boilerplate or providers.
Expand Down
2 changes: 1 addition & 1 deletion docs/learn/getting-started/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ nav: 1
---

<div class="flex justify-center mb-4">
<img src="../../bear.jpg" alt="Logo Zustand" />
<img src="../../public/bear.jpg" alt="Logo Zustand" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we keep the original position? If it's a hard requirement of rspress, I may consider a symbolic link as a last resort.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dai-shi hard requirement, should be under docs/public

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dbritto-dev Can we copy ./docs/bear.jpg ./docs/public/bear.jpg in the GitHub Action?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dai-shi sure, we can do that

</div>

A small, fast, and scalable bearbones state management solution.
Expand Down
2 changes: 1 addition & 1 deletion docs/learn/guides/nextjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Setup with Next.js
nav: 15
---

> [!NOTE]
> [!INFO]
> We will be updating this guide soon based on our discussion in https://github.com/pmndrs/zustand/discussions/2740.

[Next.js](https://nextjs.org) is a popular server-side rendering framework for React that presents
Expand Down
2 changes: 1 addition & 1 deletion docs/learn/guides/slices-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const useBoundStore = create((...a) => ({

Adding middlewares to a combined store is the same as with other normal stores.

Adding [`persist` middleware](../../reference/integrations/persisting-store-data.md) to our `useBoundStore`:
Adding [`persist` middleware](../integrations/persisting-store-data.md) to our `useBoundStore`:

```js
import { create } from 'zustand'
Expand Down
4 changes: 2 additions & 2 deletions docs/learn/guides/tutorial-tic-tac-toe.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ parent `Board` component instead of in each `Square` component. The `Board` comp
`Square` component what to display by passing a prop, like you did when you passed a number to each
`Square` component.

> [!IMPORTANT]
> [!NOTE]
> To collect data from multiple children, or to have two or more child components
> communicate with each other, declare the shared state in their parent component instead. The
> parent component can pass that state back down to the children via props. This keeps the child
Expand Down Expand Up @@ -423,7 +423,7 @@ export default function Board() {
}
```

> [!IMPORTANT]
> [!NOTE]
> Note how in `handleClick` function, you call `.slice()` to create a copy of the squares array
> instead of modifying the existing array.

Expand Down
2 changes: 1 addition & 1 deletion docs/learn/guides/updating-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ take a look at an example:
set(produce((state: State) => { ++state.deep.nested.obj.count })),
```

What a reduction! Please take note of the [gotchas listed here](../../reference/integrations/immer-middleware.md).
What a reduction! Please take note of the [gotchas listed here](../integrations/immer-middleware.md).

### With optics-ts

Expand Down
8 changes: 8 additions & 0 deletions docs/learn/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ Using Zustand in server-rendered and framework-specific environments.
- [SSR and hydration](./guides/ssr-and-hydration.md) — Avoid hydration mismatches when rendering on the server.
- [Initialize state with props](./guides/initialize-state-with-props.md) — Seed a store's initial state from React component props.

## Integrations

In-depth guides for combining Zustand with middleware and ecosystem tools.

- [Persisting store data](./integrations/persisting-store-data.md) — Persist and rehydrate state using storage adapters.
- [Immer middleware](./integrations/immer-middleware.md) — Write immutable updates with concise mutable syntax.
- [Third-party libraries](./integrations/third-party-libraries.md) — Explore community packages that extend Zustand.

## Testing and quality

Best practices for writing reliable, maintainable code with Zustand.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ npm install immer

## Usage

(Notice the extra parentheses after the type parameter as mentioned in the [Advanced Typescript Guide](../../learn/guides/advanced-typescript.md)).
(Notice the extra parentheses after the type parameter as mentioned in the [Advanced Typescript Guide](../guides/advanced-typescript.md)).

Updating simple states

Expand Down
File renamed without changes
File renamed without changes.
8 changes: 4 additions & 4 deletions docs/reference/apis/create-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const someStore = createStore(stateCreatorFn)
- [Updating Arrays in State](#updating-arrays-in-state)
- [Subscribing to state updates](#subscribing-to-state-updates)
- [Troubleshooting](#troubleshooting)
- [I’ve updated the state, but the screen doesn’t update](#i’ve-updated-the-state,-but-the-screen-doesn’t-update)
- [I’ve updated the state, but the screen doesn’t update](#ive-updated-the-state-but-the-screen-doesnt-update)

## Types

Expand Down Expand Up @@ -120,7 +120,7 @@ State can hold any kind of JavaScript value. When you want to update built-in pr
numbers, strings, booleans, etc. you should directly assign new values to ensure updates are applied
correctly, and avoid unexpected behaviors.

> [!NOTE]
> [!INFO]
> By default, `set` function performs a shallow merge. If you need to completely replace
> the state with a new one, use the `replace` parameter set to `true`

Expand Down Expand Up @@ -231,7 +231,7 @@ By default, `set` function performs a shallow merge. To update array values we s
values to ensure updates are applied correctly, and avoid unexpected behaviors. To completely
replace the state with a new one, use the `replace` parameter set to `true`.

> [!IMPORTANT]
> [!TIP]
> We should prefer immutable operations like: `[...array]`, `concat(...)`, `filter(...)`,
> `slice(...)`, `map(...)`, `toSpliced(...)`, `toSorted(...)`, and `toReversed(...)`, and avoid
> mutable operations like `array[arrayIndex] = ...`, `push(...)`, `unshift(...)`, `pop(...)`,
Expand Down Expand Up @@ -440,7 +440,7 @@ personStore.getState().setPerson({
})
```

> [!NOTE]
> [!TIP]
> We don’t need to copy every property separately due to `set` function performing shallow merge by
> default.

Expand Down
12 changes: 6 additions & 6 deletions docs/reference/apis/create-with-equality-fn.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ nav: 23
However, it offers a way to define a custom equality check. This allows for more granular control
over when components re-render, improving performance and responsiveness.

> [!IMPORTANT]
> [!INFO]
> In order to use `createWithEqualityFn` from `zustand/traditional` you need to install
> `use-sync-external-store` library due to `zustand/traditional` relies on `useSyncExternalStoreWithSelector`.

Expand All @@ -28,7 +28,7 @@ const useSomeStore = createWithEqualityFn(stateCreatorFn, defaultEqualityFn)
- [Updating state with no store actions](#updating-state-with-no-store-actions)
- [Subscribing to state updates](#subscribing-to-state-updates)
- [Troubleshooting](#troubleshooting)
- [I’ve updated the state, but the screen doesn’t update](#i’ve-updated-the-state,-but-the-screen-doesn’t-update)
- [I’ve updated the state, but the screen doesn’t update](#ive-updated-the-state-but-the-screen-doesnt-update)

## Types

Expand Down Expand Up @@ -131,7 +131,7 @@ State can hold any kind of JavaScript value. When you want to update built-in pr
numbers, strings, booleans, etc. you should directly assign new values to ensure updates are applied
correctly, and avoid unexpected behaviors.

> [!NOTE]
> [!INFO]
> By default, `set` function performs a shallow merge. If you need to completely replace
> the state with a new one, use the `replace` parameter set to `true`

Expand Down Expand Up @@ -254,7 +254,7 @@ By default, `set` function performs a shallow merge. To update array values we s
values to ensure updates are applied correctly, and avoid unexpected behaviors. To completely
replace the state with a new one, use the `replace` parameter set to `true`.

> [!IMPORTANT]
> [!TIP]
> We should prefer immutable operations like: `[...array]`, `concat(...)`, `filter(...)`,
> `slice(...)`, `map(...)`, `toSpliced(...)`, `toSorted(...)`, and `toReversed(...)`, and avoid
> mutable operations like `array[arrayIndex] = ...`, `push(...)`, `unshift(...)`, `pop(...)`,
Expand Down Expand Up @@ -311,7 +311,7 @@ export default function MovingDot() {
Defining actions at module level, external to the store have a few advantages like: it doesn't
require a hook to call an action, and it facilitates code splitting.

> [!NOTE]
> [!TIP]
> The recommended way is to colocate actions and states within the store (let your actions be
> located together with your state).

Expand Down Expand Up @@ -532,7 +532,7 @@ fields has changed:
setPerson({ ...person, firstName: e.target.value }) // New first name from the input
```

> [!NOTE]
> [!TIP]
> We don’t need to copy every property separately due to `set` function performing shallow merge by
> default.

Expand Down
10 changes: 5 additions & 5 deletions docs/reference/apis/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const useSomeStore = create(stateCreatorFn)
- [Updating state with no store actions](#updating-state-with-no-store-actions)
- [Subscribing to state updates](#subscribing-to-state-updates)
- [Troubleshooting](#troubleshooting)
- [I’ve updated the state, but the screen doesn’t update](#i’ve-updated-the-state,-but-the-screen-doesn’t-update)
- [I’ve updated the state, but the screen doesn’t update](#ive-updated-the-state-but-the-screen-doesnt-update)

## Types

Expand Down Expand Up @@ -118,7 +118,7 @@ State can hold any kind of JavaScript value. When you want to update built-in pr
numbers, strings, booleans, etc. you should directly assign new values to ensure updates are applied
correctly, and avoid unexpected behaviors.

> [!NOTE]
> [!INFO]
> By default, `set` function performs a shallow merge. If you need to completely replace the state
> with a new one, use the `replace` parameter set to `true`

Expand Down Expand Up @@ -236,7 +236,7 @@ By default, `set` function performs a shallow merge. To update array values we s
values to ensure updates are applied correctly, and avoid unexpected behaviors. To completely
replace the state with a new one, use the `replace` parameter set to `true`.

> [!IMPORTANT]
> [!TIP]
> We should prefer immutable operations like: `[...array]`, `concat(...)`, `filter(...)`,
> `slice(...)`, `map(...)`, `toSpliced(...)`, `toSorted(...)`, and `toReversed(...)`, and avoid
> mutable operations like `array[arrayIndex] = ...`, `push(...)`, `unshift(...)`, `pop(...)`,
Expand Down Expand Up @@ -289,7 +289,7 @@ export default function MovingDot() {
Defining actions at module level, external to the store have a few advantages like: it doesn't
require a hook to call an action, and it facilitates code splitting.

> [!NOTE]
> [!TIP]
> The recommended way is to colocate actions and states within the store (let your actions be
> located together with your state).

Expand Down Expand Up @@ -501,7 +501,7 @@ fields has changed:
setPerson({ ...person, firstName: e.target.value }) // New first name from the input
```

> [!NOTE]
> [!TIP]
> We don’t need to copy every property separately due to `set` function performing shallow merge by
> default.

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/apis/shallow.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ nav: 24
**top-level** properties when you're working with data structures that don't have nested objects or
arrays within them.

> [!NOTE]
> [!WARNING]
> Shallow lets you perform quick comparisons, but keep its limitations in mind.

```js
Expand Down Expand Up @@ -235,7 +235,7 @@ are referentially different, shallow will return `false`. This comparison is don
Object.getPrototypeOf(a) === Object.getPrototypeOf(b)
```

> [!IMPORTANT]
> [!NOTE]
> Objects created with the object initializer (`{}`) or with `new Object()` inherit from
> `Object.prototype` by default. However, objects created with `Object.create(proto)` inherit from
> the proto you pass in—which may not be `Object.prototype.`
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/hooks/use-store-with-equality-fn.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ nav: 26
`useStore`. However, it offers a way to define a custom equality check. This allows for more
granular control over when components re-render, improving performance and responsiveness.

> [!IMPORTANT]
> [!INFO]
> In order to use `useStoreWithEqualityFn` from `zustand/traditional` you need to install
> `use-sync-external-store` library due to `zustand/traditional` relies on `useSyncExternalStoreWithSelector`.

Expand All @@ -23,8 +23,8 @@ const someState = useStoreWithEqualityFn(store, selectorFn, equalityFn)
- [Usage](#usage)
- [Using a global vanilla store in React](#using-a-global-vanilla-store-in-react)
- [Using dynamic vanilla stores in React](#using-dynamic-global-vanilla-stores-in-react)
- [Using scoped (non-global) vanilla store in React](<#using-scoped-(non-global)-vanilla-store-in-react>)
- [Using dynamic scoped (non-global) vanilla stores in React](<#using-dynamic-scoped-(non-global)-vanilla-stores-in-react>)
- [Using scoped (non-global) vanilla store in React](#using-scoped-non-global-vanilla-store-in-react)
- [Using dynamic scoped (non-global) vanilla stores in React](#using-dynamic-scoped-non-global-vanilla-stores-in-react)
- [Troubleshooting](#troubleshooting)

## Types
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/hooks/use-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const someState = useStore(store, selectorFn)
- [Usage](#usage)
- [Use a vanilla store in React](#using-a-global-vanilla-store-in-react)
- [Using dynamic vanilla stores in React](#using-dynamic-global-vanilla-stores-in-react)
- [Using scoped (non-global) vanilla store in React](<#using-scoped-(non-global)-vanilla-store-in-react>)
- [Using dynamic scoped (non-global) vanilla stores in React](<#using-dynamic-scoped-(non-global)-vanilla-stores-in-react>)
- [Using scoped (non-global) vanilla store in React](#using-scoped-non-global-vanilla-store-in-react)
- [Using dynamic scoped (non-global) vanilla stores in React](#using-dynamic-scoped-non-global-vanilla-stores-in-react)
- [Troubleshooting](#troubleshooting)

## Types
Expand Down
11 changes: 5 additions & 6 deletions docs/reference/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Reference
description: API-first reference for stores, hooks, middlewares, and integrations.
description: API-first reference for stores, hooks, and middlewares.
---

## APIs
Expand Down Expand Up @@ -31,13 +31,12 @@ Composable middleware functions for extending store behavior.
- [`combine`](./middlewares/combine.md) — Combine separate state slices into a single store with inferred types.
- [`subscribeWithSelector`](./middlewares/subscribe-with-selector.md) — Subscribe to a slice of state with selector and equality support.

## Integrations
## Learn guides

In-depth guides for using Zustand alongside third-party libraries.
Practical guides and integration-focused tutorials now live in the Learn section.

- [Persisting store data](./integrations/persisting-store-data.md) — Detailed guide to the `persist` middleware and storage adapters.
- [Immer middleware](./integrations/immer-middleware.md) — Detailed guide to the `immer` middleware.
- [Third-party libraries](./integrations/third-party-libraries.md) — Using Zustand with other libraries in the ecosystem.
- [Integrations](../learn/index.md#integrations)
- [State management patterns](../learn/index.md#core-concepts)

## Migrations

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/middlewares/combine.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ creator function that adds new state slices and actions. This is really helpful
infers types, so there’s no need for explicit type definitions.

> [!TIP]
> This makes state management more straightforward and efficient by making curried version of
> `create` and `createStore` not necessary for middleware usage.
> This makes state management more straightforward and efficient by making curried version
> of `create` and `createStore` not necessary for middleware usage.

```js
const nextStateCreatorFn = combine(initialState, additionalStateCreatorFn)
Expand Down
Loading
Loading