[
  {
    "path": ".codesandbox/ci.json",
    "content": "{\n  \"packages\": [\"dist\"],\n  \"sandboxes\": [\n    \"new\",\n    \"react-typescript-react-ts\",\n    \"simple-react-browserify-x9yni\",\n    \"simple-snowpack-react-o1gmx\",\n    \"react-parcel-onewf\",\n    \"next-js-uo1h0\",\n    \"pavlobu-zustand-demo-frutec\"\n  ],\n  \"node\": \"18\"\n}\n"
  },
  {
    "path": ".github/DISCUSSION_TEMPLATE/bug-report.yml",
    "content": "labels: ['bug']\nbody:\n  - type: markdown\n    attributes:\n      value: If you don't have a reproduction link, please choose a different category.\n  - type: textarea\n    attributes:\n      label: Bug Description\n      description: Describe the bug you encountered\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Reproduction Link\n      description: A link to a [TypeScript Playground](https://www.typescriptlang.org/play), a [StackBlitz Project](https://stackblitz.com/) or something else with a minimal reproduction.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [dai-shi] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: [\n    'https://daishi.gumroad.com/l/uaxms',\n    'https://daishi.gumroad.com/l/learn-zustand-v4',\n  ] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Assigned issue\nabout: This is to create a new issue that already has an assignee. Please open a new discussion otherwise.\ntitle: ''\nlabels: ''\nassignees: ''\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Bug Reports\n    url: https://github.com/pmndrs/zustand/discussions/new?category=bug-report\n    about: Please post bug reports here.\n  - name: Questions\n    url: https://github.com/pmndrs/zustand/discussions/new?category=q-a\n    about: Please post questions here.\n  - name: Other Discussions\n    url: https://github.com/pmndrs/zustand/discussions/new/choose\n    about: Please post ideas and general discussions here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: 'npm'\n    directory: '/'\n    schedule:\n      interval: 'daily'\n    ignore:\n      - dependency-name: '*'\n        update-types:\n          - 'version-update:semver-patch'\n          - 'version-update:semver-minor'\n\n  - package-ecosystem: 'github-actions'\n    directory: '/'\n    schedule:\n      interval: 'weekly'\n    ignore:\n      - dependency-name: '*'\n        update-types:\n          - 'version-update:semver-patch'\n          - 'version-update:semver-minor'\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "## Related Bug Reports or Discussions\n\nFixes #\n\n## Summary\n\n## Check List\n\n- [ ] `pnpm run fix` for formatting and linting code and docs\n"
  },
  {
    "path": ".github/workflows/compressed-size.yml",
    "content": "name: Compressed Size\n\non: [pull_request]\n\njobs:\n  compressed_size:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - uses: preactjs/compressed-size-action@49c7ff02f46adc39a83c24e91f6110ba8138a19d # v3\n        with:\n          pattern: './dist/**/*.{js,mjs}'\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Build documentation and deploy to GitHub Pages\n\non:\n  push:\n    branches: [main]\n  workflow_dispatch:\n\n# Cancel previous run (see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency)\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build:\n    uses: pmndrs/docs/.github/workflows/build.yml@v3\n    with:\n      mdx: 'docs'\n      libname: 'Zustand'\n      home_redirect: '/learn/getting-started/introduction'\n      icon: '/favicon.ico'\n      logo: '/bear.jpg'\n      github: 'https://github.com/pmndrs/zustand'\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n\n    # Grant GITHUB_TOKEN the permissions required to make a Pages deployment\n    permissions:\n      pages: write # to deploy to Pages\n      id-token: write # to verify the deployment originates from an appropriate source\n\n    # Deploy to the github-pages environment\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n\n    steps:\n      - id: deployment\n        uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5\n"
  },
  {
    "path": ".github/workflows/preview-release.yml",
    "content": "name: Preview Release\n\non: [push, pull_request]\n\njobs:\n  preview_release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - run: pnpm dlx pkg-pr-new publish './dist' --compact --template './examples/*'\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  release:\n    types: [published]\n\npermissions:\n  id-token: write\n  contents: read\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          registry-url: 'https://registry.npmjs.org'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - run: npm publish\n        working-directory: dist\n"
  },
  {
    "path": ".github/workflows/test-multiple-builds.yml",
    "content": "name: Test Multiple Builds\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test_multiple_builds:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        build: [cjs, esm]\n        env: [development] # [development, production]\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - name: Patch for DEV-ONLY\n        if: ${{ matrix.env == 'development' }}\n        run: |\n          sed -i~ \"s/it[.a-zA-Z]*('\\[DEV-ONLY\\]/it('/\" tests/*.tsx\n          sed -i~ \"s/it[.a-zA-Z]*('\\[PRD-ONLY\\]/it.skip('/\" tests/*.tsx\n      - name: Patch for PRD-ONLY\n        if: ${{ matrix.env == 'production' }}\n        run: |\n          sed -i~ \"s/it[.a-zA-Z]*('\\[PRD-ONLY\\]/it('/\" tests/*.tsx\n          sed -i~ \"s/it[.a-zA-Z]*('\\[DEV-ONLY\\]/it.skip('/\" tests/*.tsx\n      - name: Patch for CJS\n        if: ${{ matrix.build == 'cjs' }}\n        run: |\n          sed -i~ \"s/resolve('\\.\\/src\\(.*\\)\\.ts')/resolve('\\.\\/dist\\1.js')/\" vitest.config.mts\n      - name: Patch for ESM\n        if: ${{ matrix.build == 'esm' }}\n        run: |\n          sed -i~ \"s/resolve('\\.\\/src\\(.*\\)\\.ts')/resolve('\\.\\/dist\\/esm\\1.mjs')/\" vitest.config.mts\n          sed -i~ \"1s/^/import.meta.env.MODE='${NODE_ENV}';/\" tests/*.tsx\n        env:\n          NODE_ENV: ${{ matrix.env }}\n      - name: Test ${{ matrix.build }} ${{ matrix.env }}\n        run: |\n          pnpm run test:spec\n        env:\n          NODE_ENV: ${{ matrix.env }}\n"
  },
  {
    "path": ".github/workflows/test-multiple-versions.yml",
    "content": "name: Test Multiple Versions\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test_multiple_versions:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        react:\n          - 18.0.0\n          - 18.1.0\n          - 18.2.0\n          - 18.3.1\n          - 19.0.0\n          - 19.1.0\n          - 19.2.0\n          - 19.3.0-canary-da641178-20260129\n          - 0.0.0-experimental-da641178-20260129\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - name: Test ${{ matrix.react }} ${{ matrix.devtools-skip }}\n        run: |\n          pnpm add -D react@${{ matrix.react }} react-dom@${{ matrix.react }}\n          pnpm run test:spec\n"
  },
  {
    "path": ".github/workflows/test-old-typescript.yml",
    "content": "name: Test Old TypeScript\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test_old_typescript:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        typescript:\n          - 5.9.3\n          - 5.8.3\n          - 5.7.3\n          - 5.6.3\n          - 5.5.4\n          - 5.4.5\n          - 5.3.3\n          - 5.2.2\n          - 5.1.6\n          - 5.0.4\n          - 4.9.5\n          - 4.8.4\n          - 4.7.4\n          - 4.6.4\n          - 4.5.5\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - name: Patch for all TS\n        run: |\n          sed -i~ 's/\"isolatedDeclarations\": true,//' tsconfig.json\n      - name: Patch for v4/v3 TS\n        if: ${{ startsWith(matrix.typescript, '4.') || startsWith(matrix.typescript, '3.') }}\n        run: |\n          sed -i~ 's/\"verbatimModuleSyntax\": true,//' tsconfig.json\n      - name: Patch for Old TS\n        if: ${{ matrix.typescript == '5.3.3' || matrix.typescript == '5.2.2' || matrix.typescript == '5.1.6' || matrix.typescript == '5.0.4' || matrix.typescript == '4.9.5' || matrix.typescript == '4.8.4' || matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' }}\n        run: |\n          sed -i~ 's/\"moduleResolution\": \"bundler\",/\"moduleResolution\": \"node\",/' tsconfig.json\n          sed -i~ 's/\"allowImportingTsExtensions\": true,//' tsconfig.json\n          sed -i~ 's/\"zustand\": \\[\"\\.\\/src\\/index\\.ts\"\\],/\"zustand\": [\".\\/dist\\/index.d.ts\"],/' tsconfig.json\n          sed -i~ 's/\"zustand\\/\\*\": \\[\"\\.\\/src\\/\\*\\.ts\"\\]/\"zustand\\/*\": [\".\\/dist\\/*.d.ts\"]/' tsconfig.json\n          sed -i~ 's/\"include\": .*/\"include\": [\"src\\/types.d.ts\", \"dist\\/**\\/*\", \"tests\\/**\\/*\"],/' tsconfig.json\n      - name: Patch for Older TS\n        if: ${{ matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' }}\n        run: |\n          pnpm json -I -f package.json -e \"this.resolutions={}; this.resolutions['@types/node']='18.13.0';\"\n          pnpm add -D @types/node@18.13.0\n          pnpm add -D vitest@3.2.4 @vitest/coverage-v8@3.2.4 @vitest/ui@3.2.4\n      - name: Install old TypeScript\n        run: pnpm add -D typescript@${{ matrix.typescript }}\n      - name: Test ${{ matrix.typescript }}\n        run: pnpm run test:types\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run test:format\n      - run: pnpm run test:types\n      - run: pnpm run test:lint\n      - run: pnpm run test:spec\n      - run: pnpm run build # we don't have any other workflows to test build\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\ndist/\nThumbs.db\nehthumbs.db\nDesktop.ini\n$RECYCLE.BIN/\n.DS_Store\n.vscode\n.docz/\ncoverage/\n.rpt2_cache/\n.idea\nexamples/**/*/package-lock.json\nexamples/**/*/yarn.lock\nexamples/**/*/pnpm-lock.yaml\nexamples/**/*/bun.lockb\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist\npnpm-lock.yaml\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## General Guideline\n\n### Reporting Issues\n\nIf you have found what you think is a bug, please [start a discussion](https://github.com/pmndrs/zustand/discussions/new?category=bug-report).\n\nFor any usage questions, please [start a discussion](https://github.com/pmndrs/zustand/discussions/new?category=q-a).\n\n### Suggesting New Features\n\nIf you are here to suggest a feature, first [start a discussion](https://github.com/pmndrs/zustand/discussions/new?category=ideas) if it does not already exist. From there, we will discuss use-cases for the feature and then finally discuss how it could be implemented.\n\n### Committing\n\nWe are applying [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/) here. In short, that means a commit has to be one of the following types:\n\nYour commit type must be one of the following:\n\n- **feat**: A new feature.\n- **fix**: A bug fix.\n- **refactor**: A code change that neither fixes a bug nor adds a feature.\n- **chore**: Changes to the build process, configuration, dependencies, CI/CD pipelines, or other auxiliary tools and libraries.\n- **docs**: Documentation-only changes.\n- **test**: Adding missing or correcting existing tests.\n\nIf you are unfamiliar with the usage of conventional commits,\nthe short version is to simply specify the type as a first word,\nand follow it with a colon and a space, then start your message\nfrom a lowercase letter, like this:\n\n```\nfeat: add a 'foo' type support\n```\n\nYou can also specify the scope of the commit in the parentheses after a type:\n\n```\nfix(react): change the 'bar' parameter type\n```\n\n### Development\n\nIf you would like to contribute by fixing an open issue or developing a new feature you can use this suggested workflow:\n\n#### General\n\n1. Fork this repository.\n2. Create a new feature branch based off the `main` branch.\n3. Follow the [Core](#Core) and/or the [Documentation](#Documentation) guide below and come back to this once done.\n4. Run `pnpm run fix:format` to format the code.\n5. Git stage your required changes and commit (review the commit guidelines below).\n6. Submit the PR for review.\n\n##### Core\n\n1. Run `pnpm install` to install dependencies.\n2. Create failing tests for your fix or new feature in the [`tests`](./tests/) folder.\n3. Implement your changes.\n4. Run `pnpm run build` to build the library. _(Pro-tip: `pnpm run build-watch` runs the build in watch mode)_\n5. Run the tests by running `pnpm run test` and ensure that they pass.\n6. You can use `pnpm link` to sym-link this package and test it locally on your own project. Alternatively, you may use CodeSandbox CI's canary releases to test the changes in your own project. (requires a PR to be created first)\n7. Follow step 4 and onwards from the [General](#General) guide above to bring it to the finish line.\n\n### Pull Requests\n\nPlease try to keep your pull request focused in scope and avoid including unrelated commits.\n\nAfter you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or request improvements, therefore, please check ✅ [\"Allow edits from maintainers\"](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) on your PR.\n\n## Zustand-specific Guideline\n\n##### Documentation\n\nOur [docs](https://zustand.docs.pmnd.rs) are based on [`pmndrs/docs`](https://github.com/pmndrs/docs).\n\n1. Separately, clone the `pmndrs/docs`. (you don't need to fork it).\n2. Inside the `pmndrs/docs` directory:\n   1. Create a `.env` file in the root directory with the next environment variables: `MDX=docs/zustand/docs` and `HOME_REDIRECT=/getting-started/introduction`.\n   2. Run `npm install` to install dependencies.\n   3. Run `npm run dev` to start the dev server.\n   4. Navigate to [`http://localhost:3000`](http://localhost:3000) to view the documents.\n3. Go Back to the forked repository:\n   1. Run `pnpm install` to install dependencies.\n   2. Navigate to the [`docs`](./docs/) folder and make necessary changes to the documents.\n   3. Add your changes to the documents and see them live reloaded in the browser. (if you don't see changes, try `control + c`, then run `npm run dev` in the cloned `pmndrs/docs` repository)\n4. Follow step 4 and onwards from the [General](#General) guide above to bring it to the finish line.\n\nThank you for contributing! :heart:\n"
  },
  {
    "path": "FUNDING.json",
    "content": "{\n  \"drips\": {\n    \"ethereum\": {\n      \"ownedBy\": \"0xBA918e34bed77Ba7a9fCF53be0A81FA538d56FA7\"\n    }\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Paul Henschel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"./docs/bear.jpg\" />\n</p>\n\n[![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)\n[![Build Size](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdeno.bundlejs.com%2F%3Fq%3Dzustand&query=%24.size.uncompressedSize&style=flat&label=bundle%20size&colorA=000000&colorB=000000)](https://bundlejs.com/?q=zustand)\n[![Version](https://img.shields.io/npm/v/zustand?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/zustand)\n[![Downloads](https://img.shields.io/npm/dt/zustand.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/zustand)\n[![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/poimandres)\n\n<a href=\"https://dai-shi.github.io/zustand-banner-sponsorship/sponsors/\" target=\"_blank\" rel=\"noopener\">\n  <p align=\"center\">\n    <img src=\"https://dai-shi.github.io/zustand-banner-sponsorship/api/banner.png\" />\n  </p>\n</a>\n\nA small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated.\n\nDon't disregard it because it's cute. It has quite the claws, lots of time was spent dealing with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [react concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right.\n\nYou can try a live [demo](https://zustand-demo.pmnd.rs/) and read the [docs](https://zustand.docs.pmnd.rs/).\n\n```bash\nnpm install zustand\n```\n\n:warning: This readme is written for JavaScript users. If you are a TypeScript user, be sure to check out our [TypeScript Usage section](#typescript-usage).\n\n## First create a store\n\nYour store is a hook! You can put anything in it: primitives, objects, functions. State has to be updated immutably and the `set` function [merges state](./docs/guides/immutable-state-and-merging.md) to help it.\n\n```jsx\nimport { create } from 'zustand'\n\nconst useBearStore = create((set) => ({\n  bears: 0,\n  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),\n  removeAllBears: () => set({ bears: 0 }),\n}))\n```\n\n## Then bind your components, and that's it!\n\nUse the hook anywhere, no providers are needed. Select your state and the component will re-render on changes.\n\n```jsx\nfunction BearCounter() {\n  const bears = useBearStore((state) => state.bears)\n  return <h1>{bears} around here ...</h1>\n}\n\nfunction Controls() {\n  const increasePopulation = useBearStore((state) => state.increasePopulation)\n  return <button onClick={increasePopulation}>one up</button>\n}\n```\n\n### Why zustand over redux?\n\n- Simple and un-opinionated\n- Makes hooks the primary means of consuming state\n- Doesn't wrap your app in context providers\n- [Can inform components transiently (without causing render)](#transient-updates-for-often-occurring-state-changes)\n\n### Why zustand over context?\n\n- Less boilerplate\n- Renders components only on changes\n- Centralized, action-based state management\n\n---\n\n# Recipes\n\n## Fetching everything\n\nYou can, but bear in mind that it will cause the component to update on every state change!\n\n```jsx\nconst state = useBearStore()\n```\n\n## Selecting multiple state slices\n\nIt detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks.\n\n```jsx\nconst nuts = useBearStore((state) => state.nuts)\nconst honey = useBearStore((state) => state.honey)\n```\n\nIf you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can use [useShallow](./docs/guides/prevent-rerenders-with-use-shallow.md) to prevent unnecessary rerenders when the selector output does not change according to shallow equal.\n\n```jsx\nimport { create } from 'zustand'\nimport { useShallow } from 'zustand/react/shallow'\n\nconst useBearStore = create((set) => ({\n  nuts: 0,\n  honey: 0,\n  treats: {},\n  // ...\n}))\n\n// Object pick, re-renders the component when either state.nuts or state.honey change\nconst { nuts, honey } = useBearStore(\n  useShallow((state) => ({ nuts: state.nuts, honey: state.honey })),\n)\n\n// Array pick, re-renders the component when either state.nuts or state.honey change\nconst [nuts, honey] = useBearStore(\n  useShallow((state) => [state.nuts, state.honey]),\n)\n\n// Mapped picks, re-renders the component when state.treats changes in order, count or keys\nconst treats = useBearStore(useShallow((state) => Object.keys(state.treats)))\n```\n\nFor more control over re-rendering, you may provide any custom equality function (this example requires the use of [`createWithEqualityFn`](./docs/migrations/migrating-to-v5.md#using-custom-equality-functions-such-as-shallow)).\n\n```jsx\nconst treats = useBearStore(\n  (state) => state.treats,\n  (oldTreats, newTreats) => compare(oldTreats, newTreats),\n)\n```\n\n## Overwriting state\n\nThe `set` function has a second argument, `false` by default. Instead of merging, it will replace the state model. Be careful not to wipe out parts you rely on, like actions.\n\n```jsx\nconst useFishStore = create((set) => ({\n  salmon: 1,\n  tuna: 2,\n  deleteEverything: () => set({}, true), // clears the entire store, actions included\n  deleteTuna: () => set(({ tuna, ...rest }) => rest, true),\n}))\n```\n\n## Async actions\n\nJust call `set` when you're ready, zustand doesn't care if your actions are async or not.\n\n```jsx\nconst useFishStore = create((set) => ({\n  fishies: {},\n  fetch: async (pond) => {\n    const response = await fetch(pond)\n    set({ fishies: await response.json() })\n  },\n}))\n```\n\n## Read from state in actions\n\n`set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`.\n\n```jsx\nconst useSoundStore = create((set, get) => ({\n  sound: 'grunt',\n  action: () => {\n    const sound = get().sound\n    ...\n```\n\n## Reading/writing state and reacting to changes outside of components\n\nSometimes you need to access state in a non-reactive way or act upon the store. For these cases, the resulting hook has utility functions attached to its prototype.\n\n:warning: This technique is not recommended for adding state in [React Server Components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md) (typically in Next.js 13 and above). It can lead to unexpected bugs and privacy issues for your users. For more details, see [#2200](https://github.com/pmndrs/zustand/discussions/2200).\n\n```jsx\nconst useDogStore = create(() => ({ paw: true, snout: true, fur: true }))\n\n// Getting non-reactive fresh state\nconst paw = useDogStore.getState().paw\n// Listening to all changes, fires synchronously on every change\nconst unsub1 = useDogStore.subscribe(console.log)\n// Updating state, will trigger listeners\nuseDogStore.setState({ paw: false })\n// Unsubscribe listeners\nunsub1()\n\n// You can of course use the hook as you always would\nfunction Component() {\n  const paw = useDogStore((state) => state.paw)\n  ...\n```\n\n### Using subscribe with selector\n\nIf you need to subscribe with a selector,\n`subscribeWithSelector` middleware will help.\n\nWith this middleware `subscribe` accepts an additional signature:\n\n```ts\nsubscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubscribe\n```\n\n```js\nimport { subscribeWithSelector } from 'zustand/middleware'\nconst useDogStore = create(\n  subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })),\n)\n\n// Listening to selected changes, in this case when \"paw\" changes\nconst unsub2 = useDogStore.subscribe((state) => state.paw, console.log)\n// Subscribe also exposes the previous value\nconst unsub3 = useDogStore.subscribe(\n  (state) => state.paw,\n  (paw, previousPaw) => console.log(paw, previousPaw),\n)\n// Subscribe also supports an optional equality function\nconst unsub4 = useDogStore.subscribe(\n  (state) => [state.paw, state.fur],\n  console.log,\n  { equalityFn: shallow },\n)\n// Subscribe and fire immediately\nconst unsub5 = useDogStore.subscribe((state) => state.paw, console.log, {\n  fireImmediately: true,\n})\n```\n\n## Using zustand without React\n\nZustand core can be imported and used without the React dependency. The only difference is that the create function does not return a hook, but the API utilities.\n\n```jsx\nimport { createStore } from 'zustand/vanilla'\n\nconst store = createStore((set) => ...)\nconst { getState, setState, subscribe, getInitialState } = store\n\nexport default store\n```\n\nYou can use a vanilla store with `useStore` hook available since v4.\n\n```jsx\nimport { useStore } from 'zustand'\nimport { vanillaStore } from './vanillaStore'\n\nconst useBoundStore = (selector) => useStore(vanillaStore, selector)\n```\n\n:warning: Note that middlewares that modify `set` or `get` are not applied to `getState` and `setState`.\n\n## Transient updates (for often occurring state-changes)\n\nThe subscribe function allows components to bind to a state-portion without forcing re-render on changes. Best combine it with useEffect for automatic unsubscribe on unmount. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance impact when you are allowed to mutate the view directly.\n\n```jsx\nconst useScratchStore = create((set) => ({ scratches: 0, ... }))\n\nconst Component = () => {\n  // Fetch initial state\n  const scratchRef = useRef(useScratchStore.getState().scratches)\n  // Connect to the store on mount, disconnect on unmount, catch state-changes in a reference\n  useEffect(() => useScratchStore.subscribe(\n    state => (scratchRef.current = state.scratches)\n  ), [])\n  ...\n```\n\n## Sick of reducers and changing nested states? Use Immer!\n\nReducing nested structures is tiresome. Have you tried [immer](https://github.com/mweststrate/immer)?\n\n```jsx\nimport { produce } from 'immer'\n\nconst useLushStore = create((set) => ({\n  lush: { forest: { contains: { a: 'bear' } } },\n  clearForest: () =>\n    set(\n      produce((state) => {\n        state.lush.forest.contains = null\n      }),\n    ),\n}))\n\nconst clearForest = useLushStore((state) => state.clearForest)\nclearForest()\n```\n\n[Alternatively, there are some other solutions.](./docs/guides/updating-state.md#with-immer)\n\n## Persist middleware\n\nYou can persist your store's data using any kind of storage.\n\n```jsx\nimport { create } from 'zustand'\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\nconst useFishStore = create(\n  persist(\n    (set, get) => ({\n      fishes: 0,\n      addAFish: () => set({ fishes: get().fishes + 1 }),\n    }),\n    {\n      name: 'food-storage', // name of the item in the storage (must be unique)\n      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used\n    },\n  ),\n)\n```\n\n[See the full documentation for this middleware.](./docs/reference/integrations/persisting-store-data.md)\n\n## Immer middleware\n\nImmer is available as middleware too.\n\n```jsx\nimport { create } from 'zustand'\nimport { immer } from 'zustand/middleware/immer'\n\nconst useBeeStore = create(\n  immer((set) => ({\n    bees: 0,\n    addBees: (by) =>\n      set((state) => {\n        state.bees += by\n      }),\n  })),\n)\n```\n\n## Can't live without redux-like reducers and action types?\n\n```jsx\nconst types = { increase: 'INCREASE', decrease: 'DECREASE' }\n\nconst reducer = (state, { type, by = 1 }) => {\n  switch (type) {\n    case types.increase:\n      return { grumpiness: state.grumpiness + by }\n    case types.decrease:\n      return { grumpiness: state.grumpiness - by }\n  }\n}\n\nconst useGrumpyStore = create((set) => ({\n  grumpiness: 0,\n  dispatch: (args) => set((state) => reducer(state, args)),\n}))\n\nconst dispatch = useGrumpyStore((state) => state.dispatch)\ndispatch({ type: types.increase, by: 2 })\n```\n\nOr, just use our redux-middleware. It wires up your main-reducer, sets the initial state, and adds a dispatch function to the state itself and the vanilla API.\n\n```jsx\nimport { redux } from 'zustand/middleware'\n\nconst useGrumpyStore = create(redux(reducer, initialState))\n```\n\n## Redux devtools\n\nInstall the [Redux DevTools Chrome extension](https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) to use the devtools middleware.\n\n```jsx\nimport { devtools } from 'zustand/middleware'\n\n// Usage with a plain action store, it will log actions as \"setState\"\nconst usePlainStore = create(devtools((set) => ...))\n// Usage with a redux store, it will log full action types\nconst useReduxStore = create(devtools(redux(reducer, initialState)))\n```\n\nOne redux devtools connection for multiple stores\n\n```jsx\nimport { devtools } from 'zustand/middleware'\n\n// Usage with a plain action store, it will log actions as \"setState\"\nconst usePlainStore1 = create(devtools((set) => ..., { name, store: storeName1 }))\nconst usePlainStore2 = create(devtools((set) => ..., { name, store: storeName2 }))\n// Usage with a redux store, it will log full action types\nconst useReduxStore1 = create(devtools(redux(reducer, initialState)), { name, store: storeName3 })\nconst useReduxStore2 = create(devtools(redux(reducer, initialState)), { name, store: storeName4 })\n```\n\nAssigning different connection names will separate stores in redux devtools. This also helps group different stores into separate redux devtools connections.\n\ndevtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument.\n\nName store: `devtools(..., {name: \"MyStore\"})`, which will create a separate instance named \"MyStore\" in the devtools.\n\nSerialize options: `devtools(..., { serialize: { options: true } })`.\n\n#### Logging Actions\n\ndevtools will only log actions from each separated store unlike in a typical _combined reducers_ redux store. See an approach to combining stores https://github.com/pmndrs/zustand/issues/163\n\nYou can log a specific action type for each `set` function by passing a third parameter:\n\n```jsx\nconst useBearStore = create(devtools((set) => ({\n  ...\n  eatFish: () => set(\n    (prev) => ({ fishes: prev.fishes > 1 ? prev.fishes - 1 : 0 }),\n    undefined,\n    'bear/eatFish'\n  ),\n  ...\n```\n\nYou can also log the action's type along with its payload:\n\n```jsx\n  ...\n  addFishes: (count) => set(\n    (prev) => ({ fishes: prev.fishes + count }),\n    undefined,\n    { type: 'bear/addFishes', count, }\n  ),\n  ...\n```\n\nIf an action type is not provided, it is defaulted to \"anonymous\". You can customize this default value by providing an `anonymousActionType` parameter:\n\n```jsx\ndevtools(..., { anonymousActionType: 'unknown', ... })\n```\n\nIf you wish to disable devtools (on production for instance). You can customize this setting by providing the `enabled` parameter:\n\n```jsx\ndevtools(..., { enabled: false, ... })\n```\n\n## React context\n\nThe store created with `create` doesn't require context providers. In some cases, you may want to use contexts for dependency injection or if you want to initialize your store with props from a component. Because the normal store is a hook, passing it as a normal context value may violate the rules of hooks.\n\nThe recommended method available since v4 is to use the vanilla store.\n\n```jsx\nimport { createContext, useContext } from 'react'\nimport { createStore, useStore } from 'zustand'\n\nconst store = createStore(...) // vanilla store without hooks\n\nconst StoreContext = createContext()\n\nconst App = () => (\n  <StoreContext.Provider value={store}>\n    ...\n  </StoreContext.Provider>\n)\n\nconst Component = () => {\n  const store = useContext(StoreContext)\n  const slice = useStore(store, selector)\n  ...\n```\n\n## TypeScript Usage\n\nBasic typescript usage doesn't require anything special except for writing `create<State>()(...)` instead of `create(...)`...\n\n```ts\nimport { create } from 'zustand'\nimport { devtools, persist } from 'zustand/middleware'\nimport type {} from '@redux-devtools/extension' // required for devtools typing\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst useBearStore = create<BearState>()(\n  devtools(\n    persist(\n      (set) => ({\n        bears: 0,\n        increase: (by) => set((state) => ({ bears: state.bears + by })),\n      }),\n      {\n        name: 'bear-storage',\n      },\n    ),\n  ),\n)\n```\n\nA more detailed TypeScript guide is [here](docs/guides/beginner-typescript.md) and [there](docs/guides/advanced-typescript.md).\n\n## Best practices\n\n- You may wonder how to organize your code for better maintenance: [Splitting the store into separate slices](./docs/learn/guides/slices-pattern.md).\n- Recommended usage for this unopinionated library: [Flux inspired practice](./docs/learn/guides/flux-inspired-practice.md).\n- [Calling actions outside a React event handler in pre-React 18](./docs/learn/guides/event-handler-in-pre-react-18.md).\n- [Testing](./docs/learn/guides/testing.md)\n- For more, have a look [in the docs folder](./docs/index.md)\n\n## Third-Party Libraries\n\nSome 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).\n\n## Comparison with other libraries\n\n- [Difference between zustand and other state management libraries for React](https://zustand.docs.pmnd.rs/learn/getting-started/comparison)\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\npageType: home\n\nhero:\n  text: Bear necessities for React state\n  tagline: A tiny, predictable store with hooks-first ergonomics and escape hatches that stay out of your way.\n  actions:\n    - theme: brand\n      text: Introduction\n      link: ./learn/getting-started/introduction.md\n    - theme: alt\n      text: Quick Start\n      link: ./learn/index.md\nfeatures:\n  - title: Minimal API, fast adoption\n    details: Create a store with a single hook, subscribe with selectors, and avoid boilerplate or providers.\n  - title: Safe under React concurrency\n    details: Built to avoid zombie children and tearing issues while keeping renders predictable.\n  - title: Works across React and vanilla\n    details: Share stores between React, React Native, and non-React environments with the same API surface.\n  - title: Batteries included\n    details: Opt into devtools, persistence, Redux-style middleware, and Immer without changing your mental model.\n  - title: TypeScript-first ergonomics\n    details: Strongly typed helpers and patterns so your state and actions stay inferred.\n  - title: Small footprint\n    details: Tiny bundle size with zero config and performance that keeps pace in production.\n---\n"
  },
  {
    "path": "docs/learn/getting-started/comparison.md",
    "content": "---\ntitle: Comparison\ndescription: How Zustand stacks up against similar libraries\nnav: 2\n---\n\nZustand is one of many state management libraries for React.\nOn this page we will discuss Zustand\nin comparison to some of these libraries,\nincluding Redux, Valtio, Jotai, and Recoil.\n\nEach library has its own strengths and weaknesses,\nand we will compare key differences and similarities between each.\n\n## Redux\n\n### State Model (vs Redux)\n\nConceptually, Zustand and Redux are quite similar,\nboth are based on an immutable state model.\nHowever, Redux requires your app to be wrapped\nin context providers; Zustand does not.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  increment: (qty: number) => void\n  decrement: (qty: number) => void\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  increment: (qty: number) => set((state) => ({ count: state.count + qty })),\n  decrement: (qty: number) => set((state) => ({ count: state.count - qty })),\n}))\n```\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Action = {\n  type: 'increment' | 'decrement'\n  qty: number\n}\n\ntype Actions = {\n  dispatch: (action: Action) => void\n}\n\nconst countReducer = (state: State, action: Action) => {\n  switch (action.type) {\n    case 'increment':\n      return { count: state.count + action.qty }\n    case 'decrement':\n      return { count: state.count - action.qty }\n    default:\n      return state\n  }\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  dispatch: (action: Action) => set((state) => countReducer(state, action)),\n}))\n```\n\n**Redux**\n\n```ts\nimport { createStore } from 'redux'\nimport { useSelector, useDispatch } from 'react-redux'\n\ntype State = {\n  count: number\n}\n\ntype Action = {\n  type: 'increment' | 'decrement'\n  qty: number\n}\n\nconst countReducer = (state: State, action: Action) => {\n  switch (action.type) {\n    case 'increment':\n      return { count: state.count + action.qty }\n    case 'decrement':\n      return { count: state.count - action.qty }\n    default:\n      return state\n  }\n}\n\nconst countStore = createStore(countReducer)\n```\n\n```ts\nimport { createSlice, configureStore } from '@reduxjs/toolkit'\n\nconst countSlice = createSlice({\n  name: 'count',\n  initialState: { value: 0 },\n  reducers: {\n    incremented: (state, qty: number) => {\n      // Redux Toolkit does not mutate the state, it uses the Immer library\n      // behind scenes, allowing us to have something called \"draft state\".\n      state.value += qty\n    },\n    decremented: (state, qty: number) => {\n      state.value -= qty\n    },\n  },\n})\n\nconst countStore = configureStore({ reducer: countSlice.reducer })\n```\n\n### Render Optimization (vs Redux)\n\nWhen it comes to render optimizations within your app,\nthere are no major differences in approach between Zustand and Redux.\nIn both libraries it is recommended\nthat you manually apply render optimizations by using selectors.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  increment: (qty: number) => void\n  decrement: (qty: number) => void\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  increment: (qty: number) => set((state) => ({ count: state.count + qty })),\n  decrement: (qty: number) => set((state) => ({ count: state.count - qty })),\n}))\n\nconst Component = () => {\n  const count = useCountStore((state) => state.count)\n  const increment = useCountStore((state) => state.increment)\n  const decrement = useCountStore((state) => state.decrement)\n  // ...\n}\n```\n\n**Redux**\n\n```ts\nimport { createStore } from 'redux'\nimport { useSelector, useDispatch } from 'react-redux'\n\ntype State = {\n  count: number\n}\n\ntype Action = {\n  type: 'increment' | 'decrement'\n  qty: number\n}\n\nconst countReducer = (state: State, action: Action) => {\n  switch (action.type) {\n    case 'increment':\n      return { count: state.count + action.qty }\n    case 'decrement':\n      return { count: state.count - action.qty }\n    default:\n      return state\n  }\n}\n\nconst countStore = createStore(countReducer)\n\nconst Component = () => {\n  const count = useSelector((state) => state.count)\n  const dispatch = useDispatch()\n  // ...\n}\n```\n\n```ts\nimport { useSelector } from 'react-redux'\nimport type { TypedUseSelectorHook } from 'react-redux'\nimport { createSlice, configureStore } from '@reduxjs/toolkit'\n\nconst countSlice = createSlice({\n  name: 'count',\n  initialState: { value: 0 },\n  reducers: {\n    incremented: (state, qty: number) => {\n      // Redux Toolkit does not mutate the state, it uses the Immer library\n      // behind scenes, allowing us to have something called \"draft state\".\n      state.value += qty\n    },\n    decremented: (state, qty: number) => {\n      state.value -= qty\n    },\n  },\n})\n\nconst countStore = configureStore({ reducer: countSlice.reducer })\n\nconst useAppSelector: TypedUseSelectorHook<typeof countStore.getState> =\n  useSelector\n\nconst useAppDispatch: () => typeof countStore.dispatch = useDispatch\n\nconst Component = () => {\n  const count = useAppSelector((state) => state.count.value)\n  const dispatch = useAppDispatch()\n  // ...\n}\n```\n\n## Valtio\n\n### State Model (vs Valtio)\n\nZustand and Valtio approach state management\nin a fundamentally different way.\nZustand is based on the **immutable** state model,\nwhile Valtio is based on the **mutable** state model.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  obj: { count: number }\n}\n\nconst store = create<State>(() => ({ obj: { count: 0 } }))\n\nstore.setState((prev) => ({ obj: { count: prev.obj.count + 1 } }))\n```\n\n**Valtio**\n\n```ts\nimport { proxy } from 'valtio'\n\nconst state = proxy({ obj: { count: 0 } })\n\nstate.obj.count += 1\n```\n\n### Render Optimization (vs Valtio)\n\nThe other difference between Zustand and Valtio\nis Valtio makes render optimizations through property access.\nHowever, with Zustand, it is recommended that\nyou manually apply render optimizations by using selectors.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\nconst useCountStore = create<State>(() => ({\n  count: 0,\n}))\n\nconst Component = () => {\n  const count = useCountStore((state) => state.count)\n  // ...\n}\n```\n\n**Valtio**\n\n```ts\nimport { proxy, useSnapshot } from 'valtio'\n\nconst state = proxy({\n  count: 0,\n})\n\nconst Component = () => {\n  const { count } = useSnapshot(state)\n  // ...\n}\n```\n\n## Jotai\n\n### State Model (vs Jotai)\n\nThere is one major difference between Zustand and Jotai.\nZustand is a single store,\nwhile Jotai consists of primitive atoms that can be composed together.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  updateCount: (\n    countCallback: (count: State['count']) => State['count'],\n  ) => void\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  updateCount: (countCallback) =>\n    set((state) => ({ count: countCallback(state.count) })),\n}))\n```\n\n**Jotai**\n\n```ts\nimport { atom } from 'jotai'\n\nconst countAtom = atom<number>(0)\n```\n\n### Render Optimization (vs Jotai)\n\nJotai achieves render optimizations through atom dependency.\nHowever, with Zustand it is recommended that\nyou manually apply render optimizations by using selectors.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  updateCount: (\n    countCallback: (count: State['count']) => State['count'],\n  ) => void\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  updateCount: (countCallback) =>\n    set((state) => ({ count: countCallback(state.count) })),\n}))\n\nconst Component = () => {\n  const count = useCountStore((state) => state.count)\n  const updateCount = useCountStore((state) => state.updateCount)\n  // ...\n}\n```\n\n**Jotai**\n\n```ts\nimport { atom, useAtom } from 'jotai'\n\nconst countAtom = atom<number>(0)\n\nconst Component = () => {\n  const [count, updateCount] = useAtom(countAtom)\n  // ...\n}\n```\n\n## Recoil\n\n### State Model (vs Recoil)\n\nThe difference between Zustand and Recoil\nis similar to that between Zustand and Jotai.\nRecoil depends on atom string keys\ninstead of atom object referential identities.\nAdditionally, Recoil needs to wrap your app in a context provider.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  setCount: (countCallback: (count: State['count']) => State['count']) => void\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  setCount: (countCallback) =>\n    set((state) => ({ count: countCallback(state.count) })),\n}))\n```\n\n**Recoil**\n\n```ts\nimport { atom } from 'recoil'\n\nconst count = atom({\n  key: 'count',\n  default: 0,\n})\n```\n\n### Render Optimization (vs Recoil)\n\nSimilar to previous optimization comparisons,\nRecoil makes render optimizations through atom dependency.\nWhereas with Zustand, it is recommended that\nyou manually apply render optimizations by using selectors.\n\n**Zustand**\n\n```ts\nimport { create } from 'zustand'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  setCount: (countCallback: (count: State['count']) => State['count']) => void\n}\n\nconst useCountStore = create<State & Actions>((set) => ({\n  count: 0,\n  setCount: (countCallback) =>\n    set((state) => ({ count: countCallback(state.count) })),\n}))\n\nconst Component = () => {\n  const count = useCountStore((state) => state.count)\n  const setCount = useCountStore((state) => state.setCount)\n  // ...\n}\n```\n\n**Recoil**\n\n```ts\nimport { atom, useRecoilState } from 'recoil'\n\nconst countAtom = atom({\n  key: 'count',\n  default: 0,\n})\n\nconst Component = () => {\n  const [count, setCount] = useRecoilState(countAtom)\n  // ...\n}\n```\n\n## Npm Downloads Trend\n\n- [Npm Downloads Trend of State Management Libraries for React](https://npm-stat.com/charts.html?package=zustand&package=jotai&package=valtio&package=%40reduxjs%2Ftoolkit&package=recoil)\n"
  },
  {
    "path": "docs/learn/getting-started/introduction.md",
    "content": "---\ntitle: Introduction\ndescription: How to use Zustand\nnav: 1\n---\n\n<div class=\"flex justify-center mb-4\">\n<img src=\"../../bear.jpg\" alt=\"Logo Zustand\" />\n</div>\n\nA small, fast, and scalable bearbones state management solution.\nZustand has a comfy API based on hooks.\nIt isn't boilerplatey or opinionated,\nbut has enough convention to be explicit and flux-like.\n\nDon't disregard it because it's cute, it has claws!\nLots of time was spent to deal with common pitfalls,\nlike the dreaded [zombie child problem],\n[React concurrency], and [context loss]\nbetween mixed renderers.\nIt may be the one state manager in the React space that gets all of these right.\n\nYou can try a live demo [here](https://codesandbox.io/s/dazzling-moon-itop4).\n\n[zombie child problem]: https://react-redux.js.org/api/hooks#stale-props-and-zombie-children\n[react concurrency]: https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md\n[context loss]: https://github.com/facebook/react/issues/13332\n\n## Installation\n\nZustand is available as a package on NPM for use:\n\n```bash\n# NPM\nnpm install zustand\n# Or, use any package manager of your choice.\n```\n\n## First create a store\n\nYour store is a hook!\nYou can put anything in it: primitives, objects, functions.\nThe `set` function _merges_ state.\n\n```js\nimport { create } from 'zustand'\n\nconst useBear = create((set) => ({\n  bears: 0,\n  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),\n  removeAllBears: () => set({ bears: 0 }),\n  updateBears: (newBears) => set({ bears: newBears }),\n}))\n```\n\n## Then bind your components, and that's it!\n\nYou can use the hook anywhere, without the need of providers.\nSelect your state and the consuming component\nwill re-render when that state changes.\n\n```jsx\nfunction BearCounter() {\n  const bears = useBear((state) => state.bears)\n  return <h1>{bears} bears around here...</h1>\n}\n\nfunction Controls() {\n  const increasePopulation = useBear((state) => state.increasePopulation)\n  return <button onClick={increasePopulation}>one up</button>\n}\n```\n"
  },
  {
    "path": "docs/learn/guides/advanced-typescript.md",
    "content": "---\ntitle: Advanced TypeScript Guide\nnav: 13\n---\n\n## Basic usage\n\nThe difference when using TypeScript is that instead of writing `create(...)`, you have to write `create<T>()(...)` (notice the extra parentheses `()` too along with the type parameter) where `T` is the type of the state to annotate it. For example:\n\n```ts\nimport { create } from 'zustand'\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst useBearStore = create<BearState>()((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n}))\n```\n\n<details>\n  <summary>Why can't we simply infer the type from the initial state?</summary>\n\n  <br/>\n\n**TLDR**: Because state generic `T` is invariant.\n\nConsider this minimal version `create`:\n\n```ts\ndeclare const create: <T>(f: (get: () => T) => T) => T\n\nconst x = create((get) => ({\n  foo: 0,\n  bar: () => get(),\n}))\n// `x` is inferred as `unknown` instead of\n// interface X {\n//   foo: number,\n//   bar: () => X\n// }\n```\n\nHere, if you look at the type of `f` in `create`, i.e. `(get: () => T) => T`, it \"gives\" `T` via return (making it covariant), but it also \"takes\" `T` via `get` (making it contravariant). \"So where does `T` come from?\" TypeScript wonders. It's like that chicken or egg problem. At the end TypeScript, gives up and infers `T` as `unknown`.\n\nSo, as long as the generic to be inferred is invariant (i.e. both covariant and contravariant), TypeScript will be unable to infer it. Another simple example would be this:\n\n```ts\nconst createFoo = {} as <T>(f: (t: T) => T) => T\nconst x = createFoo((_) => 'hello')\n```\n\nHere again, `x` is `unknown` instead of `string`.\n\n  <details>\n    <summary>More about the inference (just for the people curious and interested in TypeScript)</summary>\n\nIn some sense this inference failure is not a problem because a value of type `<T>(f: (t: T) => T) => T` cannot be written. That is to say you can't write the real runtime implementation of `createFoo`. Let's try it:\n\n```js\nconst createFoo = (f) => f(/* ? */)\n```\n\n`createFoo` needs to return the return value of `f`. And to do that we first have to call `f`. And to call it we have to pass a value of type `T`. And to pass a value of type `T` we first have to produce it. But how can we produce a value of type `T` when we don't even know what `T` is? The only way to produce a value of type `T` is to call `f`, but then to call `f` itself we need a value of type `T`. So you see it's impossible to actually write `createFoo`.\n\nSo what we're saying is, the inference failure in case of `createFoo` is not really a problem because it's impossible to implement `createFoo`. But what about the inference failure in case of `create`? That also is not really a problem because it's impossible to implement `create` too. Wait a minute, if it's impossible to implement `create` then how does Zustand implement it? The answer is, it doesn't.\n\nZustand lies that it implemented `create`'s type, it implemented only the most part of it. Here's a simple proof by showing unsoundness. Consider the following code:\n\n```ts\nimport { create } from 'zustand'\n\nconst useBoundStore = create<{ foo: number }>()((_, get) => ({\n  foo: get().foo,\n}))\n```\n\nThis code compiles. But if we run it, we'll get an exception: \"Uncaught TypeError: Cannot read properties of undefined (reading 'foo')\". This is because `get` would return `undefined` before the initial state is created (hence you shouldn't call `get` when creating the initial state). The types promise that `get` will never return `undefined` but it does initially, which means Zustand failed to implement it.\n\nAnd of course Zustand failed because it's impossible to implement `create` the way types promise (in the same way it's impossible to implement `createFoo`). In other words we don't have a type to express the actual `create` we have implemented. We can't type `get` as `() => T | undefined` because it would cause inconvenience and it still won't be correct as `get` is indeed `() => T` eventually, just if called synchronously it would be `() => undefined`. What we need is some kind of TypeScript feature that allows us to type `get` as `(() => T) & WhenSync<() => undefined>`, which of course is extremely far-fetched.\n\nSo we have two problems: lack of inference and unsoundness. Lack of inference can be solved if TypeScript can improve its inference for invariants. And unsoundness can be solved if TypeScript introduces something like `WhenSync`. To work around lack of inference we manually annotate the state type. And we can't work around unsoundness, but it's not a big deal because it's not much, calling `get` synchronously anyway doesn't make sense.\n\n</details>\n\n</details>\n\n<details>\n  <summary>Why the currying `()(...)`?</summary>\n\n  <br/>\n\n**TLDR**: It is a workaround for [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571).\n\nImagine you have a scenario like this:\n\n```ts\ndeclare const withError: <T, E>(\n  p: Promise<T>,\n) => Promise<[error: undefined, value: T] | [error: E, value: undefined]>\ndeclare const doSomething: () => Promise<string>\n\nconst main = async () => {\n  let [error, value] = await withError(doSomething())\n}\n```\n\nHere, `T` is inferred to be a `string` and `E` is inferred to be `unknown`. You might want to annotate `E` as `Foo`, because you are certain of the shape of error `doSomething()` would throw. However, you can't do that. You can either pass all generics or none. Along with annotating `E` as `Foo`, you will also have to annotate `T` as `string` even though it gets inferred anyway. The solution is to make a curried version of `withError` that does nothing at runtime. Its purpose is to just allow you annotate `E`.\n\n```ts\ndeclare const withError: {\n  <E>(): <T>(\n    p: Promise<T>,\n  ) => Promise<[error: undefined, value: T] | [error: E, value: undefined]>\n  <T, E>(\n    p: Promise<T>,\n  ): Promise<[error: undefined, value: T] | [error: E, value: undefined]>\n}\ndeclare const doSomething: () => Promise<string>\ninterface Foo {\n  bar: string\n}\n\nconst main = async () => {\n  let [error, value] = await withError<Foo>()(doSomething())\n}\n```\n\nThis way, `T` gets inferred and you get to annotate `E`. Zustand has the same use case when we want to annotate the state (the first type parameter) but allow other parameters to get inferred.\n\n</details>\n\nAlternatively, you can also use `combine`, which infers the state so that you do not need to type it.\n\n```ts\nimport { create } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\nconst useBearStore = create(\n  combine({ bears: 0 }, (set) => ({\n    increase: (by: number) => set((state) => ({ bears: state.bears + by })),\n  })),\n)\n```\n\n<details>\n  <summary>Be a little careful</summary>\n\n  <br/>\n\nWe achieve the inference by lying a little in the types of `set`, `get`, and `store` that you receive as parameters. The lie is that they're typed as if the state is the first parameter, when in fact the state is the shallow-merge (`{ ...a, ...b }`) of both first parameter and the second parameter's return. For example, `get` from the second parameter has type `() => { bears: number }` and that is a lie as it should be `() => { bears: number, increase: (by: number) => void }`. And `useBearStore` still has the correct type; for example, `useBearStore.getState` is typed as `() => { bears: number, increase: (by: number) => void }`.\n\nIt isn't really a lie because `{ bears: number }` is still a subtype of `{ bears: number, increase: (by: number) => void }`. Therefore, there will be no problem in most cases. You should just be careful while using replace. For example, `set({ bears: 0 }, true)` would compile but will be unsound as it will delete the `increase` function. Another instance where you should be careful is if you use `Object.keys`. `Object.keys(get())` will return `[\"bears\", \"increase\"]` and not `[\"bears\"]`. The return type of `get` can make you fall for these mistakes.\n\n`combine` trades off a little type-safety for the convenience of not having to write a type for state. Hence, you should use `combine` accordingly. It is fine in most cases and you can use it conveniently.\n\n</details>\n\nNote that we don't use the curried version when using `combine` because `combine` \"creates\" the state. When using a middleware that creates the state, it isn't necessary to use the curried version because the state now can be inferred. Another middleware that creates state is `redux`. So when using `combine`, `redux`, or any other custom middleware that creates the state, we don't recommend using the curried version.\n\nIf you want to infer state type also outside of state declaration, you can use the `ExtractState` type helper:\n\n```ts\nimport { create, ExtractState } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\ntype BearState = ExtractState<typeof useBearStore>\n\nconst useBearStore = create(\n  combine({ bears: 0 }, (set) => ({\n    increase: (by: number) => set((state) => ({ bears: state.bears + by })),\n  })),\n)\n```\n\n## Using middlewares\n\nYou do not have to do anything special to use middlewares in TypeScript.\n\n```ts\nimport { create } from 'zustand'\nimport { devtools, persist } from 'zustand/middleware'\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst useBearStore = create<BearState>()(\n  devtools(\n    persist(\n      (set) => ({\n        bears: 0,\n        increase: (by) => set((state) => ({ bears: state.bears + by })),\n      }),\n      { name: 'bearStore' },\n    ),\n  ),\n)\n```\n\nJust make sure you are using them immediately inside `create` so as to make the contextual inference work. Doing something even remotely fancy like the following `myMiddlewares` would require more advanced types.\n\n```ts\nimport { create } from 'zustand'\nimport { devtools, persist } from 'zustand/middleware'\n\nconst myMiddlewares = (f) => devtools(persist(f, { name: 'bearStore' }))\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst useBearStore = create<BearState>()(\n  myMiddlewares((set) => ({\n    bears: 0,\n    increase: (by) => set((state) => ({ bears: state.bears + by })),\n  })),\n)\n```\n\nAlso, we recommend using `devtools` middleware as last as possible. For example, when you use it with `immer` as a middleware, it should be `devtools(immer(...))` and not `immer(devtools(...))`. This is because`devtools` mutates the `setState` and adds a type parameter on it, which could get lost if other middlewares (like `immer`) also mutate `setState` before `devtools`. Hence using `devtools` at the end makes sure that no middlewares mutate `setState` before it.\n\n## Authoring middlewares and advanced usage\n\nImagine you had to write this hypothetical middleware.\n\n```ts\nimport { create } from 'zustand'\n\nconst foo = (f, bar) => (set, get, store) => {\n  store.foo = bar\n  return f(set, get, store)\n}\n\nconst useBearStore = create(foo(() => ({ bears: 0 }), 'hello'))\nconsole.log(useBearStore.foo.toUpperCase())\n```\n\nZustand middlewares can mutate the store. But how could we possibly encode the mutation on the type-level? That is to say how could we type `foo` so that this code compiles?\n\nFor a usual statically typed language, this is impossible. But thanks to TypeScript, Zustand has something called a \"higher-kinded mutator\" that makes this possible. If you are dealing with complex type problems, like typing a middleware or using the `StateCreator` type, you will have to understand this implementation detail. For this, you can [check out #710](https://github.com/pmndrs/zustand/issues/710).\n\nIf you are eager to know what the answer is to this particular problem then you can [see it here](#middleware-that-changes-the-store-type).\n\n### Handling Dynamic `replace` Flag\n\nIf the value of the `replace` flag is not known at compile time and is determined dynamically, you might face issues. To handle this, you can use a workaround by annotating the `replace` parameter with the parameters of the `setState` function:\n\n```ts\nconst replaceFlag = Math.random() > 0.5\nconst args = [{ bears: 5 }, replaceFlag] as Parameters<\n  typeof useBearStore.setState\n>\nstore.setState(...args)\n```\n\n#### Example with `as Parameters` Workaround\n\n```ts\nimport { create } from 'zustand'\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst useBearStore = create<BearState>()((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n}))\n\nconst replaceFlag = Math.random() > 0.5\nconst args = [{ bears: 5 }, replaceFlag] as Parameters<\n  typeof useBearStore.setState\n>\nuseBearStore.setState(...args) // Using the workaround\n```\n\nBy following this approach, you can ensure that your code handles dynamic `replace` flags without encountering type issues.\n\n## Common recipes\n\n### Middleware that doesn't change the store type\n\n```ts\nimport { create, StateCreator, StoreMutatorIdentifier } from 'zustand'\n\ntype Logger = <\n  T,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  f: StateCreator<T, Mps, Mcs>,\n  name?: string,\n) => StateCreator<T, Mps, Mcs>\n\ntype LoggerImpl = <T>(\n  f: StateCreator<T, [], []>,\n  name?: string,\n) => StateCreator<T, [], []>\n\nconst loggerImpl: LoggerImpl = (f, name) => (set, get, store) => {\n  const loggedSet: typeof set = (...a) => {\n    set(...(a as Parameters<typeof set>))\n    console.log(...(name ? [`${name}:`] : []), get())\n  }\n  const setState = store.setState\n  store.setState = (...a) => {\n    setState(...(a as Parameters<typeof setState>))\n    console.log(...(name ? [`${name}:`] : []), store.getState())\n  }\n\n  return f(loggedSet, get, store)\n}\n\nexport const logger = loggerImpl as unknown as Logger\n\n// ---\n\nconst useBearStore = create<BearState>()(\n  logger(\n    (set) => ({\n      bears: 0,\n      increase: (by) => set((state) => ({ bears: state.bears + by })),\n    }),\n    'bear-store',\n  ),\n)\n```\n\n### Middleware that changes the store type\n\n```ts\nimport {\n  create,\n  StateCreator,\n  StoreMutatorIdentifier,\n  Mutate,\n  StoreApi,\n} from 'zustand'\n\ntype Foo = <\n  T,\n  A,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  f: StateCreator<T, [...Mps, ['foo', A]], Mcs>,\n  bar: A,\n) => StateCreator<T, Mps, [['foo', A], ...Mcs]>\n\ndeclare module 'zustand' {\n  interface StoreMutators<S, A> {\n    foo: Write<Cast<S, object>, { foo: A }>\n  }\n}\n\ntype FooImpl = <T, A>(\n  f: StateCreator<T, [], []>,\n  bar: A,\n) => StateCreator<T, [], []>\n\nconst fooImpl: FooImpl = (f, bar) => (set, get, _store) => {\n  type T = ReturnType<typeof f>\n  type A = typeof bar\n\n  const store = _store as Mutate<StoreApi<T>, [['foo', A]]>\n  store.foo = bar\n  return f(set, get, _store)\n}\n\nexport const foo = fooImpl as unknown as Foo\n\ntype Write<T extends object, U extends object> = Omit<T, keyof U> & U\n\ntype Cast<T, U> = T extends U ? T : U\n\n// ---\n\nconst useBearStore = create(foo(() => ({ bears: 0 }), 'hello'))\nconsole.log(useBearStore.foo.toUpperCase())\n```\n\n### `create` without curried workaround\n\nThe recommended way to use `create` is using the curried workaround like so: `create<T>()(...)`. This is because it enables you to infer the store type. But if for some reason you do not want to use the workaround, you can pass the type parameters like the following. Note that in some cases, this acts as an assertion instead of annotation, so we don't recommend it.\n\n```ts\nimport { create } from \"zustand\"\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst useBearStore = create<\n  BearState,\n  [\n    ['zustand/persist', BearState],\n    ['zustand/devtools', never]\n  ]\n>(devtools(persist((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n}), { name: 'bearStore' }))\n```\n\n### Slices pattern\n\n```ts\nimport { create, StateCreator } from 'zustand'\n\ninterface BearSlice {\n  bears: number\n  addBear: () => void\n  eatFish: () => void\n}\n\ninterface FishSlice {\n  fishes: number\n  addFish: () => void\n}\n\ninterface SharedSlice {\n  addBoth: () => void\n  getBoth: () => number\n}\n\nconst createBearSlice: StateCreator<\n  BearSlice & FishSlice,\n  [],\n  [],\n  BearSlice\n> = (set) => ({\n  bears: 0,\n  addBear: () => set((state) => ({ bears: state.bears + 1 })),\n  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),\n})\n\nconst createFishSlice: StateCreator<\n  BearSlice & FishSlice,\n  [],\n  [],\n  FishSlice\n> = (set) => ({\n  fishes: 0,\n  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),\n})\n\nconst createSharedSlice: StateCreator<\n  BearSlice & FishSlice,\n  [],\n  [],\n  SharedSlice\n> = (set, get) => ({\n  addBoth: () => {\n    // you can reuse previous methods\n    get().addBear()\n    get().addFish()\n    // or do them from scratch\n    // set((state) => ({ bears: state.bears + 1, fishes: state.fishes + 1 })\n  },\n  getBoth: () => get().bears + get().fishes,\n})\n\nconst useBoundStore = create<BearSlice & FishSlice & SharedSlice>()((...a) => ({\n  ...createBearSlice(...a),\n  ...createFishSlice(...a),\n  ...createSharedSlice(...a),\n}))\n```\n\nA detailed explanation on the slices pattern can be found [here](./slices-pattern.md).\n\nIf you have some middlewares then replace `StateCreator<MyState, [], [], MySlice>` with `StateCreator<MyState, Mutators, [], MySlice>`. For example, if you are using `devtools` then it will be `StateCreator<MyState, [[\"zustand/devtools\", never]], [], MySlice>`. See the [\"Middlewares and their mutators reference\"](#middlewares-and-their-mutators-reference) section for a list of all mutators.\n\n### Bounded `useStore` hook for vanilla stores\n\n```ts\nimport { useStore } from 'zustand'\nimport { createStore } from 'zustand/vanilla'\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst bearStore = createStore<BearState>()((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n}))\n\nfunction useBearStore(): BearState\nfunction useBearStore<T>(selector: (state: BearState) => T): T\nfunction useBearStore<T>(selector?: (state: BearState) => T) {\n  return useStore(bearStore, selector!)\n}\n```\n\nYou can also make an abstract `createBoundedUseStore` function if you need to create bounded `useStore` hooks often and want to DRY things up...\n\n```ts\nimport { useStore, StoreApi } from 'zustand'\nimport { createStore } from 'zustand/vanilla'\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n}\n\nconst bearStore = createStore<BearState>()((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n}))\n\nconst createBoundedUseStore = ((store) => (selector) =>\n  useStore(store, selector)) as <S extends StoreApi<unknown>>(\n  store: S,\n) => {\n  (): ExtractState<S>\n  <T>(selector: (state: ExtractState<S>) => T): T\n}\n\ntype ExtractState<S> = S extends { getState: () => infer X } ? X : never\n\nconst useBearStore = createBoundedUseStore(bearStore)\n```\n\n## Middlewares and their mutators reference\n\n- `devtools` — `[\"zustand/devtools\", never]`\n- `persist` — `[\"zustand/persist\", YourPersistedState]`<br/>\n  `YourPersistedState` is the type of state you are going to persist, ie the return type of `options.partialize`, if you're not passing `partialize` options the `YourPersistedState` becomes `Partial<YourState>`. Also [sometimes](https://github.com/pmndrs/zustand/issues/980#issuecomment-1162289836) passing actual `PersistedState` won't work. In those cases, try passing `unknown`.\n- `immer` — `[\"zustand/immer\", never]`\n- `subscribeWithSelector` — `[\"zustand/subscribeWithSelector\", never]`\n- `redux` — `[\"zustand/redux\", YourAction]`\n- `combine` — no mutator as `combine` does not mutate the store\n"
  },
  {
    "path": "docs/learn/guides/auto-generating-selectors.md",
    "content": "---\ntitle: Auto Generating Selectors\nnav: 14\n---\n\nWe recommend using selectors when using either the properties or actions from the store. You can access values from the store like so:\n\n```typescript\nconst bears = useBearStore((state) => state.bears)\n```\n\nHowever, writing these could be tedious. If that is the case for you, you can auto-generate your selectors.\n\n## Create the following function: `createSelectors`\n\n```typescript\nimport { StoreApi, UseBoundStore } from 'zustand'\n\ntype WithSelectors<S> = S extends { getState: () => infer T }\n  ? S & { use: { [K in keyof T]: () => T[K] } }\n  : never\n\nconst createSelectors = <S extends UseBoundStore<StoreApi<object>>>(\n  _store: S,\n) => {\n  const store = _store as WithSelectors<typeof _store>\n  store.use = {}\n  for (const k of Object.keys(store.getState())) {\n    ;(store.use as any)[k] = () => store((s) => s[k as keyof typeof s])\n  }\n\n  return store\n}\n```\n\nIf you have a store like this:\n\n```typescript\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n  increment: () => void\n}\n\nconst useBearStoreBase = create<BearState>()((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n  increment: () => set((state) => ({ bears: state.bears + 1 })),\n}))\n```\n\nApply that function to your store:\n\n```typescript\nconst useBearStore = createSelectors(useBearStoreBase)\n```\n\nNow the selectors are auto generated and you can access them directly:\n\n```typescript\n// get the property\nconst bears = useBearStore.use.bears()\n\n// get the action\nconst increment = useBearStore.use.increment()\n```\n\n## Vanilla Store\n\nIf you are using a vanilla store, use the following `createSelectors` function:\n\n```typescript\nimport { StoreApi, useStore } from 'zustand'\n\ntype WithSelectors<S> = S extends { getState: () => infer T }\n  ? S & { use: { [K in keyof T]: () => T[K] } }\n  : never\n\nconst createSelectors = <S extends StoreApi<object>>(_store: S) => {\n  const store = _store as WithSelectors<typeof _store>\n  store.use = {}\n  for (const k of Object.keys(store.getState())) {\n    ;(store.use as any)[k] = () =>\n      useStore(_store, (s) => s[k as keyof typeof s])\n  }\n\n  return store\n}\n```\n\nThe usage is the same as a React store. If you have a store like this:\n\n```typescript\nimport { createStore } from 'zustand'\n\ninterface BearState {\n  bears: number\n  increase: (by: number) => void\n  increment: () => void\n}\n\nconst store = createStore<BearState>()((set) => ({\n  bears: 0,\n  increase: (by) => set((state) => ({ bears: state.bears + by })),\n  increment: () => set((state) => ({ bears: state.bears + 1 })),\n}))\n```\n\nApply that function to your store:\n\n```typescript\nconst useBearStore = createSelectors(store)\n```\n\nNow the selectors are auto generated and you can access them directly:\n\n```typescript\n// get the property\nconst bears = useBearStore.use.bears()\n\n// get the action\nconst increment = useBearStore.use.increment()\n```\n\n## Live Demo\n\nFor a working example of this, see the [Code Sandbox](https://codesandbox.io/s/zustand-auto-generate-selectors-forked-rl8v5e?file=/src/selectors.ts).\n\n## Third-party Libraries\n\n- [auto-zustand-selectors-hook](https://github.com/Albert-Gao/auto-zustand-selectors-hook)\n- [react-hooks-global-state](https://github.com/dai-shi/react-hooks-global-state)\n- [zustood](https://github.com/udecode/zustood)\n- [@davstack/store](https://github.com/DawidWraga/davstack)\n"
  },
  {
    "path": "docs/learn/guides/beginner-typescript.md",
    "content": "---\ntitle: Beginner TypeScript Guide\nnav: 12\n---\n\nZustand is a lightweight state manager, particularly used with React. Zustand avoids reducers, context, and boilerplate.\nPaired with TypeScript, you get a strongly typed store-state, actions, and selectors-with autocomplete and compile-time safety.\n\nIn this basic guide we’ll cover:\n\n- Creating a typed store (state + actions)\n- Using the store in React components with type safety\n- Resetting the store safely with types\n- Extracting and reusing Store type (for props, tests, and utilities)\n- Composing multiple selectors and building derived state (with type inference and without extra re-renders)\n- Middlewares with TypeScript support (`combine`, `devtools`, `persist`)\n- Async actions with typed API responses\n- Working with `createWithEqualityFn` (enhanced `create` store function)\n- Structuring and coordinating multiple stores\n\n### Creating a Store with State & Actions\n\nHere we describe state and actions using an Typescript interface. The `<BearState>` generic forces the store to match this shape.\nThis means if you forget a field or use the wrong type, TypeScript will complain. Unlike plain JS, this guarantees type-safe state management.\nThe `create` function uses the curried form, which results in a store of type `UseBoundStore<StoreApi<BearState>>`.\n\n```ts\n// store.ts\nimport { create } from 'zustand'\n\n// Define types for state & actions\ninterface BearState {\n  bears: number\n  food: string\n  feed: (food: string) => void\n}\n\n// Create store using the curried form of `create`\nexport const useBearStore = create<BearState>()((set) => ({\n  bears: 2,\n  food: 'honey',\n  feed: (food) => set(() => ({ food })),\n}))\n```\n\n### Using the Store in Components\n\nInside components, you can read state and call actions. Selectors `(s) => s.bears` subscribe to only what you need.\nThis reduces re-renders and improves performance. JS can do this too, but with TS your IDE autocompletes state fields.\n\n```tsx\nimport { useBearStore } from './store'\n\nfunction BearCounter() {\n  // Select only 'bears' to avoid unnecessary re-renders\n  const bears = useBearStore((s) => s.bears)\n  return <h1>{bears} bears around</h1>\n}\n```\n\n### Resetting the Store\n\nResetting is useful after logout or “clear session”. We use `typeof initialState` to avoid repeating property types.\nTypeScript updates automatically if `initialState` changes. This is safer and cleaner compared to JS.\n\n```tsx\nimport { create } from 'zustand'\n\nconst initialState = { bears: 0, food: 'honey' }\n\n// Reuse state type dynamically\ntype BearState = typeof initialState & {\n  increase: (by: number) => void\n  reset: () => void\n}\n\nconst useBearStore = create<BearState>()((set) => ({\n  ...initialState,\n  increase: (by) => set((s) => ({ bears: s.bears + by })),\n  reset: () => set(initialState),\n}))\n\nfunction ResetZoo() {\n  const { bears, increase, reset } = useBearStore()\n\n  return (\n    <div>\n      <div>{bears}</div>\n      <button onClick={() => increase(5)}>Increase by 5</button>\n      <button onClick={reset}>Reset</button>\n    </div>\n  )\n}\n```\n\n### Extracting Types\n\nZustand provides a built-in helper called `ExtractState`. This is useful for tests, utility functions, or component props.\nIt returns the full type of your store’s state and actions without having to manually redefine them. Extracting the Store type:\n\n```ts\n// store.ts\nimport { create, type ExtractState } from 'zustand'\n\nexport const useBearStore = create((set) => ({\n  bears: 3,\n  food: 'honey',\n  increase: (by: number) => set((s) => ({ bears: s.bears + by })),\n}))\n\n// Extract the type of the whole store state\nexport type BearState = ExtractState<typeof useBearStore>\n```\n\nUsing extracted type in tests:\n\n```ts\n// test.cy.ts\nimport { BearState } from './store.ts'\n\ntest('should reset store', () => {\n  const snapshot: BearState = useBearStore.getState()\n  expect(snapshot.bears).toBeGreaterThanOrEqual(0)\n})\n```\n\nand in utility function:\n\n```ts\n// util.ts\nimport { BearState } from './store.ts'\n\nfunction logBearState(state: BearState) {\n  console.log(`We have ${state.bears} bears eating ${state.food}`)\n}\n\nlogBearState(useBearStore.getState())\n```\n\n### Selectors\n\n#### Multiple Selectors\n\nSometimes you need more than one property. Returning an object from the selector lets you access multiple fields at once.\nHowever, directly destructuring properties from that object can cause unnecessary re-renders.\nTo avoid this, it’s recommended to wrap the selector with `useShallow`, which prevents re-renders when the selected values remain shallowly equal.\nThis is more efficient than subscribing to the whole store. TypeScript ensures you can’t accidentally misspell `bears` or `food`.\nSee the [API documentation](../../reference/hooks/use-shallow.md) for more details on `useShallow`.\n\n```tsx\nimport { create } from 'zustand'\nimport { useShallow } from 'zustand/react/shallow'\n\n// Bear store with explicit types\ninterface BearState {\n  bears: number\n  food: number\n}\n\nconst useBearStore = create<BearState>()(() => ({\n  bears: 2,\n  food: 10,\n}))\n\n// In components, you can use both stores safely\nfunction MultipleSelectors() {\n  const { bears, food } = useBearStore(\n    useShallow((state) => ({ bears: state.bears, food: state.food })),\n  )\n\n  return (\n    <div>\n      We have {food} units of food for {bears} bears\n    </div>\n  )\n}\n```\n\n#### Derived State with Selectors\n\nNot all values need to be stored directly - some can be computed from existing state. You can derive values using selectors.\nThis avoids duplication and keeps the store minimal. TypeScript ensures `bears` is a number, so math is safe.\n\n```tsx\nimport { create } from 'zustand'\n\ninterface BearState {\n  bears: number\n  foodPerBear: number\n}\n\nconst useBearStore = create<BearState>()(() => ({\n  bears: 3,\n  foodPerBear: 2,\n}))\n\nfunction TotalFood() {\n  // Derived value: required amount food for all bears\n  const totalFood = useBearStore((s) => s.bears * s.foodPerBear) // don't need to have extra property `{ totalFood: 6 }` in your Store\n\n  return <div>We need ${totalFood} jars of honey</div>\n}\n```\n\n### Middlewares\n\n#### `combine` middleware\n\nThis middleware separates initial state and actions, making the code cleaner.\nTS automatically infers types from the state and actions, no interface needed.\nThis is different from JS, where type safety is missing. It’s a very popular style in TypeScript projects.\nSee the [API documentation](../../reference/middlewares/combine.md) for more details.\n\n```ts\nimport { create } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\ninterface BearState {\n  bears: number\n  increase: () => void\n}\n\n// State + actions are separated\nexport const useBearStore = create<BearState>()(\n  combine({ bears: 0 }, (set) => ({\n    increase: () => set((s) => ({ bears: s.bears + 1 })),\n  })),\n)\n```\n\n#### `devtools` middleware\n\nThis middleware connects Zustand to Redux DevTools. You can inspect changes, time-travel, and debug state.\nIt’s extremely useful in development. TS ensures your actions and state remain type-checked even here.\nSee the [API documentation](../../reference/middlewares/devtools.md) for more details.\n\n```ts\nimport { create } from 'zustand'\nimport { devtools } from 'zustand/middleware'\n\ninterface BearState {\n  bears: number\n  increase: () => void\n}\n\nexport const useBearStore = create<BearState>()(\n  devtools((set) => ({\n    bears: 0,\n    increase: () => set((s) => ({ bears: s.bears + 1 })),\n  })),\n)\n```\n\n#### `persist` middleware\n\nThis middleware keeps your store in `localStorage` (or another storage). This means your bears survive a page refresh.\nGreat for apps where persistence matters. In TS, the state type stays consistent, so no runtime surprises.\nSee the [API documentation](../../reference/middlewares/persist.md) for more details.\n\n```ts\nimport { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\n\ninterface BearState {\n  bears: number\n  increase: () => void\n}\n\nexport const useBearStore = create<BearState>()(\n  persist(\n    (set) => ({\n      bears: 0,\n      increase: () => set((s) => ({ bears: s.bears + 1 })),\n    }),\n    { name: 'bear-storage' }, // localStorage key\n  ),\n)\n```\n\n### Async Actions\n\nActions can be async to fetch remote data. Here we fetch bears count and update state.\nTS enforces correct API response type (`BearData`). In JS you might misspell `count` - TS prevents that.\n\n```ts\nimport { create } from 'zustand'\n\ninterface BearData {\n  count: number\n}\n\ninterface BearState {\n  bears: number\n  fetchBears: () => Promise<void>\n}\n\nexport const useBearStore = create<BearState>()((set) => ({\n  bears: 0,\n  fetchBears: async () => {\n    const res = await fetch('/api/bears')\n    const data: BearData = await res.json()\n\n    set({ bears: data.count })\n  },\n}))\n```\n\n### `createWithEqualityFn`\n\nVariant of `create` with equality built-in. Useful if you always want custom equality checks.\nNot common, but shows Zustand’s flexibility. TS still keeps full type inference.\nSee the [API documentation](../../reference/apis/create-with-equality-fn.md) for more details.\n\n```ts\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/shallow'\n\nconst useBearStore = createWithEqualityFn(() => ({\n  bears: 0,\n}))\n\nconst bears = useBearStore((s) => s.bears, Object.is)\n// or\nconst bears = useBearStore((s) => ({ bears: s.bears }), shallow)\n```\n\n### Multiple Stores\n\nYou can create more than one store for different domains. For example, `BearStore` manages bears and `FishStore` manages fish.\nThis keeps state isolated and easier to maintain in larger apps. With TypeScript, each store has its own strict type - you can’t accidentally mix bears and fish.\n\n```tsx\nimport { create } from 'zustand'\n\n// Bear store with explicit types\ninterface BearState {\n  bears: number\n  addBear: () => void\n}\n\nconst useBearStore = create<BearState>()((set) => ({\n  bears: 2,\n  addBear: () => set((s) => ({ bears: s.bears + 1 })),\n}))\n\n// Fish store with explicit types\ninterface FishState {\n  fish: number\n  addFish: () => void\n}\n\nconst useFishStore = create<FishState>()((set) => ({\n  fish: 5,\n  addFish: () => set((s) => ({ fish: s.fish + 1 })),\n}))\n\n// In components, you can use both stores safely\nfunction Zoo() {\n  const { bears, addBear } = useBearStore()\n  const { fish, addFish } = useFishStore()\n\n  return (\n    <div>\n      <div>\n        {bears} bears and {fish} fish\n      </div>\n      <button onClick={addBear}>Add bear</button>\n      <button onClick={addFish}>Add fish</button>\n    </div>\n  )\n}\n```\n\n### Conclusion\n\nZustand together with TypeScript provides a balance: you keep the simplicity of small, minimalistic stores, while gaining the safety of strong typing.\nYou don’t need boilerplate or complex patterns - state and actions live side by side, fully typed, and ready to use.\nStart with a basic store to learn the pattern, then expand gradually: use `combine` for cleaner inference, `persist` for storage, and `devtools` for debugging.\n"
  },
  {
    "path": "docs/learn/guides/connect-to-state-with-url-hash.md",
    "content": "---\ntitle: Connect to state with URL\nnav: 10\n---\n\n## Connect State with URL Hash\n\nIf you want to connect state of a store to URL hash, you can create your own hash storage.\n\n```ts\nimport { create } from 'zustand'\nimport { persist, StateStorage, createJSONStorage } from 'zustand/middleware'\n\nconst hashStorage: StateStorage = {\n  getItem: (key): string => {\n    const searchParams = new URLSearchParams(location.hash.slice(1))\n    const storedValue = searchParams.get(key) ?? ''\n    return JSON.parse(storedValue)\n  },\n  setItem: (key, newValue): void => {\n    const searchParams = new URLSearchParams(location.hash.slice(1))\n    searchParams.set(key, JSON.stringify(newValue))\n    location.hash = searchParams.toString()\n  },\n  removeItem: (key): void => {\n    const searchParams = new URLSearchParams(location.hash.slice(1))\n    searchParams.delete(key)\n    location.hash = searchParams.toString()\n  },\n}\n\nexport const useBoundStore = create()(\n  persist(\n    (set, get) => ({\n      fishes: 0,\n      addAFish: () => set({ fishes: get().fishes + 1 }),\n    }),\n    {\n      name: 'food-storage', // unique name\n      storage: createJSONStorage(() => hashStorage),\n    },\n  ),\n)\n```\n\n## Persist and Connect State with URL Parameters (Example: URL Query Parameters)\n\nThere are times when you want to conditionally connect the state to the URL.\nThis example depicts usage of the URL query parameters\nwhile keeping it synced with another persistence implementation, like `localstorage`.\n\nIf you want the URL params to always populate, the conditional check on `getUrlSearch()` can be removed.\n\nThe implementation below will update the URL in place, without refresh, as the relevant states change.\n\n```ts\nimport { create } from 'zustand'\nimport { persist, StateStorage, createJSONStorage } from 'zustand/middleware'\n\nconst getUrlSearch = () => {\n  return window.location.search.slice(1)\n}\n\nconst persistentStorage: StateStorage = {\n  getItem: (key): string => {\n    // Check URL first\n    if (getUrlSearch()) {\n      const searchParams = new URLSearchParams(getUrlSearch())\n      const storedValue = searchParams.get(key)\n      return JSON.parse(storedValue as string)\n    } else {\n      // Otherwise, we should load from localstorage or alternative storage\n      return JSON.parse(localStorage.getItem(key) as string)\n    }\n  },\n  setItem: (key, newValue): void => {\n    // Check if query params exist at all, can remove check if always want to set URL\n    if (getUrlSearch()) {\n      const searchParams = new URLSearchParams(getUrlSearch())\n      searchParams.set(key, JSON.stringify(newValue))\n      window.history.replaceState(null, '', `?${searchParams.toString()}`)\n    }\n\n    localStorage.setItem(key, JSON.stringify(newValue))\n  },\n  removeItem: (key): void => {\n    const searchParams = new URLSearchParams(getUrlSearch())\n    searchParams.delete(key)\n    window.location.search = searchParams.toString()\n  },\n}\n\ntype LocalAndUrlStore = {\n  typesOfFish: string[]\n  addTypeOfFish: (fishType: string) => void\n  numberOfBears: number\n  setNumberOfBears: (newNumber: number) => void\n}\n\nconst storageOptions = {\n  name: 'fishAndBearsStore',\n  storage: createJSONStorage<LocalAndUrlStore>(() => persistentStorage),\n}\n\nconst useLocalAndUrlStore = create()(\n  persist<LocalAndUrlStore>(\n    (set) => ({\n      typesOfFish: [],\n      addTypeOfFish: (fishType) =>\n        set((state) => ({ typesOfFish: [...state.typesOfFish, fishType] })),\n\n      numberOfBears: 0,\n      setNumberOfBears: (numberOfBears) => set(() => ({ numberOfBears })),\n    }),\n    storageOptions,\n  ),\n)\n\nexport default useLocalAndUrlStore\n```\n\nWhen generating the URL from a component, you can call buildShareableUrl:\n\n```ts\nconst buildURLSuffix = (params, version = 0) => {\n  const searchParams = new URLSearchParams()\n\n  const zustandStoreParams = {\n    state: {\n      typesOfFish: params.typesOfFish,\n      numberOfBears: params.numberOfBears,\n    },\n    version: version, // version is here because that is included with how Zustand sets the state\n  }\n\n  // The URL param key should match the name of the store, as specified as in storageOptions above\n  searchParams.set('fishAndBearsStore', JSON.stringify(zustandStoreParams))\n  return searchParams.toString()\n}\n\nexport const buildShareableUrl = (params, version) => {\n  return `${window.location.origin}?${buildURLSuffix(params, version)}`\n}\n```\n\nThe generated URL would look like (here without any encoding, for readability):\n\n`https://localhost/search?fishAndBearsStore={\"state\":{\"typesOfFish\":[\"tilapia\",\"salmon\"],\"numberOfBears\":15},\"version\":0}}`\n\n### Demo\n\n- Hash: https://stackblitz.com/edit/vitejs-vite-9vg24prg\n- Query: https://stackblitz.com/edit/vitejs-vite-hyc97ynf\n"
  },
  {
    "path": "docs/learn/guides/event-handler-in-pre-react-18.md",
    "content": "---\ntitle: Calling actions outside a React event handler in pre React 18\nnav: 11\n---\n\nBecause React handles `setState` synchronously if it's called outside an event handler, updating the state outside an event handler will force react to update the components synchronously. Therefore, there is a risk of encountering the zombie-child effect.\nIn order to fix this, the action needs to be wrapped in `unstable_batchedUpdates` like so:\n\n```jsx\nimport { unstable_batchedUpdates } from 'react-dom' // or 'react-native'\n\nconst useFishStore = create((set) => ({\n  fishes: 0,\n  increaseFishes: () => set((prev) => ({ fishes: prev.fishes + 1 })),\n}))\n\nconst nonReactCallback = () => {\n  unstable_batchedUpdates(() => {\n    useFishStore.getState().increaseFishes()\n  })\n}\n```\n\nMore details: https://github.com/pmndrs/zustand/issues/302\n"
  },
  {
    "path": "docs/learn/guides/flux-inspired-practice.md",
    "content": "---\ntitle: Flux inspired practice\nnav: 19\n---\n\nAlthough Zustand is an unopinionated library, we do recommend a few patterns.\nThese are inspired by practices originally found in [Flux](https://github.com/facebookarchive/flux),\nand more recently [Redux](https://redux.js.org/understanding/thinking-in-redux/three-principles),\nso if you are coming from another library, you should feel right at home.\n\nHowever, Zustand does differ in some fundamental ways,\nso some terminology may not perfectly align to other libraries.\n\n## Recommended patterns\n\n### Single store\n\nYour applications global state should be located in a single Zustand store.\n\nIf you have a large application, Zustand supports [splitting the store into slices](./slices-pattern.md).\n\n### Use `set` / `setState` to update the store\n\nAlways use `set` (or `setState`) to perform updates to your store.\n`set` (and `setState`) ensures the described update is correctly merged and listeners are appropriately notified.\n\n### Colocate store actions\n\nIn Zustand, state can be updated without the use of dispatched actions and reducers found in other Flux libraries.\nThese store actions can be added directly to the store as shown below.\n\nOptionally, by using `setState` they can be [located external to the store](./practice-with-no-store-actions.md)\n\n```js\nconst useBoundStore = create((set) => ({\n  storeSliceA: ...,\n  storeSliceB: ...,\n  storeSliceC: ...,\n  updateX: () => set(...),\n  updateY: () => set(...),\n}))\n```\n\n## Redux-like patterns\n\nIf you can't live without Redux-like reducers, you can define a `dispatch` function on the root level of the store:\n\n```typescript\nconst types = { increase: 'INCREASE', decrease: 'DECREASE' }\n\nconst reducer = (state, { type, by = 1 }) => {\n  switch (type) {\n    case types.increase:\n      return { grumpiness: state.grumpiness + by }\n    case types.decrease:\n      return { grumpiness: state.grumpiness - by }\n  }\n}\n\nconst useGrumpyStore = create((set) => ({\n  grumpiness: 0,\n  dispatch: (args) => set((state) => reducer(state, args)),\n}))\n\nconst dispatch = useGrumpyStore((state) => state.dispatch)\ndispatch({ type: types.increase, by: 2 })\n```\n\nYou could also use our redux-middleware. It wires up your main reducer, sets initial state, and adds a dispatch function to the state itself and the vanilla api.\n\n```typescript\nimport { redux } from 'zustand/middleware'\n\nconst useReduxStore = create(redux(reducer, initialState))\n```\n\nAnother way to update the store could be through functions wrapping the state functions. These could also handle side-effects of actions. For example, with HTTP-calls. To use Zustand in a non-reactive way, see [the readme](https://github.com/pmndrs/zustand#readingwriting-state-and-reacting-to-changes-outside-of-components).\n"
  },
  {
    "path": "docs/learn/guides/how-to-reset-state.md",
    "content": "---\ntitle: How to reset state\nnav: 20\n---\n\nThe following pattern can be used to reset the state to its initial value.\n\n```ts\nconst useSomeStore = create<State & Actions>()((set, get, store) => ({\n  // your code here\n  reset: () => {\n    set(store.getInitialState())\n  },\n}))\n```\n\nResetting multiple stores at once\n\n```ts\nimport type { StateCreator } from 'zustand'\nimport { create: actualCreate } from 'zustand'\n\nconst storeResetFns = new Set<() => void>()\n\nconst resetAllStores = () => {\n  storeResetFns.forEach((resetFn) => {\n    resetFn()\n  })\n}\n\nexport const create = (<T>() => {\n  return (stateCreator: StateCreator<T>) => {\n    const store = actualCreate(stateCreator)\n    storeResetFns.add(() => {\n      store.setState(store.getInitialState(), true)\n    })\n    return store\n  }\n}) as typeof actualCreate\n```\n\n## Demo\n\n- Basic: https://stackblitz.com/edit/zustand-how-to-reset-state-basic\n- Advanced: https://stackblitz.com/edit/zustand-how-to-reset-state-advanced\n"
  },
  {
    "path": "docs/learn/guides/immutable-state-and-merging.md",
    "content": "---\ntitle: Immutable state and merging\nnav: 7\n---\n\nLike with React's `useState`, we need to update state immutably.\n\nHere's a typical example:\n\n```jsx\nimport { create } from 'zustand'\n\nconst useCountStore = create((set) => ({\n  count: 0,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n}))\n```\n\nThe `set` function is to update state in the store.\nBecause the state is immutable, it should have been like this:\n\n```js\nset((state) => ({ ...state, count: state.count + 1 }))\n```\n\nHowever, as this is a common pattern, `set` actually merges state, and\nwe can skip the `...state` part:\n\n```js\nset((state) => ({ count: state.count + 1 }))\n```\n\n## Nested objects\n\nThe `set` function merges state at only one level.\nIf you have a nested object, you need to merge them explicitly. You will use the spread operator pattern like so:\n\n```jsx\nimport { create } from 'zustand'\n\nconst useCountStore = create((set) => ({\n  nested: { count: 0 },\n  inc: () =>\n    set((state) => ({\n      nested: { ...state.nested, count: state.nested.count + 1 },\n    })),\n}))\n```\n\nFor complex use cases, consider using some libraries that help with immutable updates.\nYou can refer to [Updating nested state object values](./updating-state.md#deeply-nested-object).\n\n## Replace flag\n\nTo disable the merging behavior, you can specify a `replace` boolean value for `set` like so:\n\n```js\nset((state) => newState, true)\n```\n"
  },
  {
    "path": "docs/learn/guides/initialize-state-with-props.md",
    "content": "---\ntitle: Initialize state with props\nnav: 17\n---\n\nIn cases where [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) is needed, such as when a store should be initialized with props from a component, the recommended approach is to use a vanilla store with React.context.\n\n## Store creator with `createStore`\n\n```ts\nimport { createStore } from 'zustand'\n\ninterface BearProps {\n  bears: number\n}\n\ninterface BearState extends BearProps {\n  addBear: () => void\n}\n\ntype BearStore = ReturnType<typeof createBearStore>\n\nconst createBearStore = (initProps?: Partial<BearProps>) => {\n  const DEFAULT_PROPS: BearProps = {\n    bears: 0,\n  }\n  return createStore<BearState>()((set) => ({\n    ...DEFAULT_PROPS,\n    ...initProps,\n    addBear: () => set((state) => ({ bears: ++state.bears })),\n  }))\n}\n```\n\n## Creating a context with `React.createContext`\n\n```ts\nimport { createContext } from 'react'\n\nexport const BearContext = createContext<BearStore | null>(null)\n```\n\n## Basic component usage\n\n```tsx\n// Provider implementation\nimport { useState } from 'react'\n\nfunction App() {\n  const [store] = useState(() => createBearStore())\n  return (\n    <BearContext.Provider value={store}>\n      <BasicConsumer />\n    </BearContext.Provider>\n  )\n}\n```\n\n```tsx\n// Consumer component\nimport { useContext } from 'react'\nimport { useStore } from 'zustand'\n\nfunction BasicConsumer() {\n  const store = useContext(BearContext)\n  if (!store) throw new Error('Missing BearContext.Provider in the tree')\n\n  const bears = useStore(store, (s) => s.bears)\n  const addBear = useStore(store, (s) => s.addBear)\n\n  return (\n    <>\n      <div>{bears} Bears.</div>\n      <button onClick={addBear}>Add bear</button>\n    </>\n  )\n}\n```\n\n## Common patterns\n\n### Wrapping the context provider\n\n```tsx\n// Provider wrapper\nimport { useState } from 'react'\n\ntype BearProviderProps = React.PropsWithChildren<BearProps>\n\nfunction BearProvider({ children, ...props }: BearProviderProps) {\n  const [store] = useState(() => createBearStore(props))\n  return <BearContext.Provider value={store}>{children}</BearContext.Provider>\n}\n```\n\n### Extracting context logic into a custom hook\n\n```tsx\n// Mimic the hook returned by `create`\nimport { useContext } from 'react'\nimport { useStore } from 'zustand'\n\nfunction useBearContext<T>(selector: (state: BearState) => T): T {\n  const store = useContext(BearContext)\n  if (!store) throw new Error('Missing BearContext.Provider in the tree')\n  return useStore(store, selector)\n}\n```\n\n```tsx\n// Consumer usage of the custom hook\nfunction CommonConsumer() {\n  const bears = useBearContext((s) => s.bears)\n  const addBear = useBearContext((s) => s.addBear)\n  return (\n    <>\n      <div>{bears} Bears.</div>\n      <button onClick={addBear}>Add bear</button>\n    </>\n  )\n}\n```\n\n### Optionally use memoized selector for stable outputs\n\n```tsx\nimport { useShallow } from 'zustand/react/shallow'\n\nconst meals = ['Salmon', 'Berries', 'Nuts']\n\nfunction CommonConsumer() {\n  const bearMealsOrder = useBearContext(\n    useShallow((s) =>\n      Array.from({ length: s.bears }).map((_, index) =>\n        meals.at(index % meals.length),\n      ),\n    ),\n  )\n  return (\n    <>\n      Order:\n      <ul>\n        {bearMealsOrder.map((meal) => (\n          <li key={meal}>{meal}</li>\n        ))}\n      </ul>\n    </>\n  )\n}\n```\n\n### Optionally allow using a custom equality function\n\n```tsx\n// Allow custom equality function by using useStoreWithEqualityFn instead of useStore\nimport { useContext } from 'react'\nimport { useStoreWithEqualityFn } from 'zustand/traditional'\n\nfunction useBearContext<T>(\n  selector: (state: BearState) => T,\n  equalityFn?: (left: T, right: T) => boolean,\n): T {\n  const store = useContext(BearContext)\n  if (!store) throw new Error('Missing BearContext.Provider in the tree')\n  return useStoreWithEqualityFn(store, selector, equalityFn)\n}\n```\n\n### Complete example\n\n```tsx\n// Provider wrapper & custom hook consumer\nfunction App2() {\n  return (\n    <BearProvider bears={2}>\n      <HookConsumer />\n    </BearProvider>\n  )\n}\n```\n"
  },
  {
    "path": "docs/learn/guides/maps-and-sets-usage.md",
    "content": "---\ntitle: Map and Set Usage\nnav: 8\n---\n\n# Map and Set in Zustand\n\nMap and Set are mutable data structures. To use them in Zustand, you must create new instances when updating.\n\n## Map\n\n### Reading a Map\n\n```typescript\nconst foo = useSomeStore((state) => state.foo)\n```\n\n### Updating a Map\n\nAlways create a new Map instance:\n\n```ts\n// Update single entry\nset((state) => ({\n  foo: new Map(state.foo).set(key, value),\n}))\n\n// Delete entry\nset((state) => {\n  const next = new Map(state.foo)\n  next.delete(key)\n  return { foo: next }\n})\n\n// Update multiple entries\nset((state) => {\n  const next = new Map(state.foo)\n  next.set('key1', 'value1')\n  next.set('key2', 'value2')\n  return { foo: next }\n})\n\n// Clear\nset({ foo: new Map() })\n```\n\n## Set\n\n### Reading a Set\n\n```ts\nconst bar = useSomeStore((state) => state.bar)\n```\n\n### Updating a Set\n\nAlways create a new Set instance:\n\n```ts\n// Add item\nset((state) => ({\n  bar: new Set(state.bar).add(item),\n}))\n\n// Delete item\nset((state) => {\n  const next = new Set(state.bar)\n  next.delete(item)\n  return { bar: next }\n})\n\n// Toggle item\nset((state) => {\n  const next = new Set(state.bar)\n  next.has(item) ? next.delete(item) : next.add(item)\n  return { bar: next }\n})\n\n// Clear\nset({ bar: new Set() })\n```\n\n## Why New Instances?\n\nZustand detects changes by comparing references. Mutating a Map or Set doesn't change its reference:\n\n```ts\n// ❌ Wrong - same reference, no re-render\nset((state) => {\n  state.foo.set(key, value)\n  return { foo: state.foo }\n})\n\n// ✅ Correct - new reference, triggers re-render\nset((state) => ({\n  foo: new Map(state.foo).set(key, value),\n}))\n```\n\n## Pitfall: Type Hints for Empty Collections\n\nProvide type hints when initializing empty Maps and Sets:\n\n```ts\n{\n  ids: new Set([] as string[]),\n  users: new Map([] as [string, User][])\n}\n```\n\nWithout type hints, TypeScript infers `never[]` which prevents adding items later.\n\n## Demos\n\nBasic: https://stackblitz.com/edit/vitejs-vite-5cu5ddvx\n"
  },
  {
    "path": "docs/learn/guides/nextjs.md",
    "content": "---\ntitle: Setup with Next.js\nnav: 15\n---\n\n> [!NOTE]\n> We will be updating this guide soon based on our discussion in https://github.com/pmndrs/zustand/discussions/2740.\n\n[Next.js](https://nextjs.org) is a popular server-side rendering framework for React that presents\nsome unique challenges for using Zustand properly.\nKeep in mind that Zustand store is a global\nvariable (AKA module state) making it optional to use a `Context`.\nThese challenges include:\n\n- **Per-request store:** A Next.js server can handle multiple requests simultaneously. This means\n  that the store should be created per request and should not be shared across requests.\n- **SSR friendly:** Next.js applications are rendered twice, first on the server\n  and again on the client. Having different outputs on both the client and the server will result\n  in \"hydration errors.\" The store will have to be initialized on the server and then\n  re-initialized on the client with the same data in order to avoid that. Please read more about\n  that in our [SSR and Hydration](./ssr-and-hydration.md) guide.\n- **SPA routing friendly:** Next.js supports a hybrid model for client side routing, which means\n  that in order to reset a store, we need to initialize it at the component level using a\n  `Context`.\n- **Server caching friendly:** Recent versions of Next.js (specifically applications using the App\n  Router architecture) support aggressive server caching. Due to our store being a **module state**,\n  it is completely compatible with this caching.\n\nWe have these general recommendations for the appropriate use of Zustand:\n\n- **No global stores** - Because the store should not be shared across requests, it should not be defined\n  as a global variable. Instead, the store should be created per request.\n- **React Server Components should not read from or write to the store** - RSCs cannot use hooks or context. They aren't\n  meant to be stateful. Having an RSC read from or write values to a global store violates the\n  architecture of Next.js.\n\n### Creating a store per request\n\nLet's write our store factory function that will create a new store for each\nrequest.\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n```\n\n> **Note:** do not forget to remove all comments from your `tsconfig.json` file.\n\n### Initializing the store\n\n```ts\n// src/stores/counter-store.ts\nimport { createStore } from 'zustand/vanilla'\n\nexport type CounterState = {\n  count: number\n}\n\nexport type CounterActions = {\n  decrementCount: () => void\n  incrementCount: () => void\n}\n\nexport type CounterStore = CounterState & CounterActions\n\nexport const defaultInitState: CounterState = {\n  count: 0,\n}\n\nexport const createCounterStore = (\n  initState: CounterState = defaultInitState,\n) => {\n  return createStore<CounterStore>()((set) => ({\n    ...initState,\n    decrementCount: () => set((state) => ({ count: state.count - 1 })),\n    incrementCount: () => set((state) => ({ count: state.count + 1 })),\n  }))\n}\n```\n\n### Providing the store\n\nLet's use the `createCounterStore` in our component and share it using a context provider.\n\n```tsx\n// src/providers/counter-store-provider.tsx\n'use client'\n\nimport { type ReactNode, createContext, useState, useContext } from 'react'\nimport { useStore } from 'zustand'\n\nimport { type CounterStore, createCounterStore } from '@/stores/counter-store'\n\nexport type CounterStoreApi = ReturnType<typeof createCounterStore>\n\nexport const CounterStoreContext = createContext<CounterStoreApi | undefined>(\n  undefined,\n)\n\nexport interface CounterStoreProviderProps {\n  children: ReactNode\n}\n\nexport const CounterStoreProvider = ({\n  children,\n}: CounterStoreProviderProps) => {\n  const [store] = useState(() => createCounterStore())\n  return (\n    <CounterStoreContext.Provider value={store}>\n      {children}\n    </CounterStoreContext.Provider>\n  )\n}\n\nexport const useCounterStore = <T,>(\n  selector: (store: CounterStore) => T,\n): T => {\n  const counterStoreContext = useContext(CounterStoreContext)\n  if (!counterStoreContext) {\n    throw new Error(`useCounterStore must be used within CounterStoreProvider`)\n  }\n\n  return useStore(counterStoreContext, selector)\n}\n```\n\n> **Note:** In this example, we ensure that this component is re-render-safe by checking the\n> value of the reference, so that the store is only created once. This component will only be\n> rendered once per request on the server, but might be re-rendered multiple times on the client if\n> there are stateful client components located above this component in the tree, or if this component\n> also contains other mutable state that causes a re-render.\n\n### Using the store with different architectures\n\nThere are two architectures for a Next.js application: the\n[Pages Router](https://nextjs.org/docs/pages/building-your-application/routing) and the\n[App Router](https://nextjs.org/docs/app/building-your-application/routing). The usage of Zustand on\nboth architectures should be the same with slight differences related to each architecture.\n\n#### Pages Router\n\n```tsx\n// src/components/pages/home-page.tsx\nimport { useCounterStore } from '@/providers/counter-store-provider'\n\nexport const HomePage = () => {\n  const { count, incrementCount, decrementCount } = useCounterStore(\n    (state) => state,\n  )\n\n  return (\n    <div>\n      Count: {count}\n      <hr />\n      <button type=\"button\" onClick={incrementCount}>\n        Increment Count\n      </button>\n      <button type=\"button\" onClick={decrementCount}>\n        Decrement Count\n      </button>\n    </div>\n  )\n}\n```\n\n```tsx\n// src/_app.tsx\nimport type { AppProps } from 'next/app'\n\nimport { CounterStoreProvider } from '@/providers/counter-store-provider'\n\nexport default function App({ Component, pageProps }: AppProps) {\n  return (\n    <CounterStoreProvider>\n      <Component {...pageProps} />\n    </CounterStoreProvider>\n  )\n}\n```\n\n```tsx\n// src/pages/index.tsx\nimport { HomePage } from '@/components/pages/home-page'\n\nexport default function Home() {\n  return <HomePage />\n}\n```\n\n> **Note:** creating a store per route would require creating and sharing the store\n> at page (route) component level. Try not to use this if you do not need to create\n> a store per route.\n\n```tsx\n// src/pages/index.tsx\nimport { CounterStoreProvider } from '@/providers/counter-store-provider'\nimport { HomePage } from '@/components/pages/home-page'\n\nexport default function Home() {\n  return (\n    <CounterStoreProvider>\n      <HomePage />\n    </CounterStoreProvider>\n  )\n}\n```\n\n#### App Router\n\n```tsx\n// src/components/pages/home-page.tsx\n'use client'\n\nimport { useCounterStore } from '@/providers/counter-store-provider'\n\nexport const HomePage = () => {\n  const { count, incrementCount, decrementCount } = useCounterStore(\n    (state) => state,\n  )\n\n  return (\n    <div>\n      Count: {count}\n      <hr />\n      <button type=\"button\" onClick={incrementCount}>\n        Increment Count\n      </button>\n      <button type=\"button\" onClick={decrementCount}>\n        Decrement Count\n      </button>\n    </div>\n  )\n}\n```\n\n```tsx\n// src/app/layout.tsx\nimport type { Metadata } from 'next'\nimport { Inter } from 'next/font/google'\nimport './globals.css'\n\nimport { CounterStoreProvider } from '@/providers/counter-store-provider'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport const metadata: Metadata = {\n  title: 'Create Next App',\n  description: 'Generated by create next app',\n}\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode\n}>) {\n  return (\n    <html lang=\"en\">\n      <body className={inter.className}>\n        <CounterStoreProvider>{children}</CounterStoreProvider>\n      </body>\n    </html>\n  )\n}\n```\n\n```tsx\n// src/app/page.tsx\nimport { HomePage } from '@/components/pages/home-page'\n\nexport default function Home() {\n  return <HomePage />\n}\n```\n\n> **Note:** creating a store per route would require creating and sharing the store\n> at page (route) component level. Try not to use this if you do not need to create\n> a store per route.\n\n```tsx\n// src/app/page.tsx\nimport { CounterStoreProvider } from '@/providers/counter-store-provider'\nimport { HomePage } from '@/components/pages/home-page'\n\nexport default function Home() {\n  return (\n    <CounterStoreProvider>\n      <HomePage />\n    </CounterStoreProvider>\n  )\n}\n```\n"
  },
  {
    "path": "docs/learn/guides/practice-with-no-store-actions.md",
    "content": "---\ntitle: Practice with no store actions\nnav: 5\n---\n\nThe recommended usage is to colocate actions and states within the store (let your actions be located together with your state).\n\nFor example:\n\n```js\nexport const useBoundStore = create((set) => ({\n  count: 0,\n  text: 'hello',\n  inc: () => set((state) => ({ count: state.count + 1 })),\n  setText: (text) => set({ text }),\n}))\n```\n\nThis creates a self-contained store with data and actions together.\n\n---\n\nAn alternative approach is to define actions at module level, external to the store.\n\n```js\nexport const useBoundStore = create(() => ({\n  count: 0,\n  text: 'hello',\n}))\n\nexport const inc = () =>\n  useBoundStore.setState((state) => ({ count: state.count + 1 }))\n\nexport const setText = (text) => useBoundStore.setState({ text })\n```\n\nThis has a few advantages:\n\n- It doesn't require a hook to call an action;\n- It facilitates code splitting.\n\nWhile this pattern doesn't offer any downsides, some may prefer colocating due to its encapsulated nature.\n"
  },
  {
    "path": "docs/learn/guides/prevent-rerenders-with-use-shallow.md",
    "content": "---\ntitle: Prevent rerenders with useShallow\nnav: 9\n---\n\nWhen you need to subscribe to a computed state from a store, the recommended way is to\nuse a selector.\n\nThe computed selector will cause a rerender if the output has changed according to [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is?retiredLocale=it).\n\nIn this case you might want to use `useShallow` to avoid a rerender if the computed value is always shallow\nequal the previous one.\n\n## Example\n\nWe have a store that associates to each bear a meal and we want to render their names.\n\n```js\nimport { create } from 'zustand'\n\nconst useMeals = create(() => ({\n  papaBear: 'large porridge-pot',\n  mamaBear: 'middle-size porridge pot',\n  littleBear: 'A little, small, wee pot',\n}))\n\nexport const BearNames = () => {\n  const names = useMeals((state) => Object.keys(state))\n\n  return <div>{names.join(', ')}</div>\n}\n```\n\nNow papa bear wants a pizza instead:\n\n```js\nuseMeals.setState({\n  papaBear: 'a large pizza',\n})\n```\n\nThis change causes `BearNames` rerenders even though the actual output of `names` has not changed according to shallow equal.\n\nWe can fix that using `useShallow`!\n\n```js\nimport { create } from 'zustand'\nimport { useShallow } from 'zustand/react/shallow'\n\nconst useMeals = create(() => ({\n  papaBear: 'large porridge-pot',\n  mamaBear: 'middle-size porridge pot',\n  littleBear: 'A little, small, wee pot',\n}))\n\nexport const BearNames = () => {\n  const names = useMeals(useShallow((state) => Object.keys(state)))\n\n  return <div>{names.join(', ')}</div>\n}\n```\n\nNow they can all order other meals without causing unnecessary rerenders of our `BearNames` component.\n"
  },
  {
    "path": "docs/learn/guides/slices-pattern.md",
    "content": "---\ntitle: Slices Pattern\nnav: 6\n---\n\n## Slicing the store into smaller stores\n\nYour store can become bigger and bigger and tougher to maintain as you add more features.\n\nYou can divide your main store into smaller individual stores to achieve modularity. This is simple to accomplish in Zustand!\n\nThe first individual store:\n\n```js\nexport const createFishSlice = (set) => ({\n  fishes: 0,\n  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),\n})\n```\n\nAnother individual store:\n\n```js\nexport const createBearSlice = (set) => ({\n  bears: 0,\n  addBear: () => set((state) => ({ bears: state.bears + 1 })),\n  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),\n})\n```\n\nYou can now combine both the stores into **one bounded store**:\n\n```js\nimport { create } from 'zustand'\nimport { createBearSlice } from './bearSlice'\nimport { createFishSlice } from './fishSlice'\n\nexport const useBoundStore = create((...a) => ({\n  ...createBearSlice(...a),\n  ...createFishSlice(...a),\n}))\n```\n\n### Usage in a React component\n\n```jsx\nimport { useBoundStore } from './stores/useBoundStore'\n\nfunction App() {\n  const bears = useBoundStore((state) => state.bears)\n  const fishes = useBoundStore((state) => state.fishes)\n  const addBear = useBoundStore((state) => state.addBear)\n  return (\n    <div>\n      <h2>Number of bears: {bears}</h2>\n      <h2>Number of fishes: {fishes}</h2>\n      <button onClick={() => addBear()}>Add a bear</button>\n    </div>\n  )\n}\n\nexport default App\n```\n\n### Updating multiple stores\n\nYou can update multiple stores, at the same time, in a single function.\n\n```js\nexport const createBearFishSlice = (set, get) => ({\n  addBearAndFish: () => {\n    get().addBear()\n    get().addFish()\n  },\n})\n```\n\nCombining all the stores together is the same as before.\n\n```js\nimport { create } from 'zustand'\nimport { createBearSlice } from './bearSlice'\nimport { createFishSlice } from './fishSlice'\nimport { createBearFishSlice } from './createBearFishSlice'\n\nexport const useBoundStore = create((...a) => ({\n  ...createBearSlice(...a),\n  ...createFishSlice(...a),\n  ...createBearFishSlice(...a),\n}))\n```\n\n## Adding middlewares\n\nAdding middlewares to a combined store is the same as with other normal stores.\n\nAdding [`persist` middleware](../../reference/integrations/persisting-store-data.md) to our `useBoundStore`:\n\n```js\nimport { create } from 'zustand'\nimport { createBearSlice } from './bearSlice'\nimport { createFishSlice } from './fishSlice'\nimport { persist } from 'zustand/middleware'\n\nexport const useBoundStore = create(\n  persist(\n    (...a) => ({\n      ...createBearSlice(...a),\n      ...createFishSlice(...a),\n    }),\n    { name: 'bound-store' },\n  ),\n)\n```\n\nPlease keep in mind you should only apply middlewares in the combined store. Applying them inside individual slices can lead to unexpected issues.\n\n## Usage with TypeScript\n\nA detailed guide on how to use the slice pattern in Zustand with TypeScript can be found [here](./advanced-typescript.md#slices-pattern).\n"
  },
  {
    "path": "docs/learn/guides/ssr-and-hydration.md",
    "content": "---\ntitle: SSR and Hydration\nnav: 16\n---\n\n## Server-side Rendering (SSR)\n\nServer-side Rendering (SSR) is a technique that helps us render our components into\nHTML strings on the server, send them directly to the browser, and finally \"hydrate\" the\nstatic markup into a fully interactive app on the client.\n\n### React\n\nLet's say we want to render a stateless app using React. In order to do that, we need\nto use `express`, `react` and `react-dom/server`. We don't need `react-dom/client`\nsince it's a stateless app.\n\nLet's dive into that:\n\n- `express` helps us build a web app that we can run using Node,\n- `react` helps us build the UI components that we use in our app,\n- `react-dom/server` helps us render our components on a server.\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"noImplicitAny\": false,\n    \"noEmitOnError\": true,\n    \"removeComments\": false,\n    \"sourceMap\": true,\n    \"target\": \"esnext\"\n  },\n  \"include\": [\"**/*\"]\n}\n```\n\n> **Note:** do not forget to remove all comments from your `tsconfig.json` file.\n\n```tsx\n// app.tsx\nexport const App = () => {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <title>Static Server-side-rendered App</title>\n      </head>\n      <body>\n        <div>Hello World!</div>\n      </body>\n    </html>\n  )\n}\n```\n\n```tsx\n// server.tsx\nimport express from 'express'\nimport React from 'react'\nimport ReactDOMServer from 'react-dom/server'\n\nimport { App } from './app.tsx'\n\nconst port = Number.parseInt(process.env.PORT || '3000', 10)\nconst app = express()\n\napp.get('/', (_, res) => {\n  const { pipe } = ReactDOMServer.renderToPipeableStream(<App />, {\n    onShellReady() {\n      res.setHeader('content-type', 'text/html')\n      pipe(res)\n    },\n  })\n})\n\napp.listen(port, () => {\n  console.log(`Server is listening at ${port}`)\n})\n```\n\n```sh\ntsc --build\n```\n\n```sh\nnode server.js\n```\n\n## Hydration\n\nHydration turns the initial HTML snapshot from the server into a fully interactive app\nthat runs in the browser. The right way to \"hydrate\" a component is by using `hydrateRoot`.\n\n### React\n\nLet's say we want to render a stateful app using React. In order to do that we need to\nuse `express`, `react`, `react-dom/server` and `react-dom/client`.\n\nLet's dive into that:\n\n- `express` helps us build a web app that we can run using Node,\n- `react` helps us build the UI components that we use in our app,\n- `react-dom/server` helps us render our components on a server,\n- `react-dom/client` helps us hydrate our components on a client.\n\n> **Note:** Do not forget that even if we can render our components on a server, it is\n> important to \"hydrate\" them on a client to make them interactive.\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"noImplicitAny\": false,\n    \"noEmitOnError\": true,\n    \"removeComments\": false,\n    \"sourceMap\": true,\n    \"target\": \"esnext\"\n  },\n  \"include\": [\"**/*\"]\n}\n```\n\n> **Note:** do not forget to remove all comments in your `tsconfig.json` file.\n\n```tsx\n// app.tsx\nexport const App = () => {\n  return (\n    <html>\n      <head>\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <title>Static Server-side-rendered App</title>\n      </head>\n      <body>\n        <div>Hello World!</div>\n      </body>\n    </html>\n  )\n}\n```\n\n```tsx\n// main.tsx\nimport ReactDOMClient from 'react-dom/client'\n\nimport { App } from './app.tsx'\n\nReactDOMClient.hydrateRoot(document, <App />)\n```\n\n```tsx\n// server.tsx\nimport express from 'express'\nimport React from 'react'\nimport ReactDOMServer from 'react-dom/server'\n\nimport { App } from './app.tsx'\n\nconst port = Number.parseInt(process.env.PORT || '3000', 10)\nconst app = express()\n\napp.use('/', (_, res) => {\n  const { pipe } = ReactDOMServer.renderToPipeableStream(<App />, {\n    bootstrapScripts: ['/main.js'],\n    onShellReady() {\n      res.setHeader('content-type', 'text/html')\n      pipe(res)\n    },\n  })\n})\n\napp.listen(port, () => {\n  console.log(`Server is listening at ${port}`)\n})\n```\n\n```sh\ntsc --build\n```\n\n```sh\nnode server.js\n```\n\n> **Warning:** The React tree you pass to `hydrateRoot` needs to produce the same output as it did on the server.\n> The most common causes leading to hydration errors include:\n>\n> - Extra whitespace (like newlines) around the React-generated HTML inside the root node.\n> - Using checks like typeof window !== 'undefined' in your rendering logic.\n> - Using browser-only APIs like `window.matchMedia` in your rendering logic.\n> - Rendering different data on the server and the client.\n>\n> React recovers from some hydration errors, but you must fix them like other bugs. In the best case, they’ll lead to a slowdown; in the worst case, event handlers can get attached to the wrong elements.\n\nYou can read more about the caveats and pitfalls here: [hydrateRoot](https://react.dev/reference/react-dom/client/hydrateRoot)\n"
  },
  {
    "path": "docs/learn/guides/testing.md",
    "content": "---\ntitle: Testing\ndescription: Writing Tests\nnav: 18\n---\n\n## Setting Up a Test Environment\n\n### Test Runners\n\nUsually, your test runner needs to be configured to run JavaScript/TypeScript syntax. If you're\ngoing to be testing UI components, you will likely need to configure the test runner to use JSDOM\nto provide a mock DOM environment.\n\nSee these resources for test runner configuration instructions:\n\n- **Jest**\n  - [Jest: Getting Started](https://jestjs.io/docs/getting-started)\n  - [Jest: Configuration - Test Environment](https://jestjs.io/docs/configuration#testenvironment-string)\n- **Vitest**\n  - [Vitest: Getting Started](https://vitest.dev/guide)\n  - [Vitest: Configuration - Test Environment](https://vitest.dev/config/#environment)\n\n### UI and Network Testing Tools\n\n**We recommend using [React Testing Library (RTL)](https://testing-library.com/docs/react-testing-library/intro)\nto test out React components that connect to Zustand**. RTL is a simple and complete React DOM\ntesting utility that encourages good testing practices. It uses ReactDOM's `render` function and\n`act` from `react-dom/tests-utils`. Furthermore, [Native Testing Library (RNTL)](https://testing-library.com/docs/react-native-testing-library/intro)\nis the alternative to RTL to test out React Native components. The [Testing Library](https://testing-library.com/)\nfamily of tools also includes adapters for many other popular frameworks.\n\nWe also recommend using [Mock Service Worker (MSW)](https://mswjs.io/) to mock network requests, as\nthis means your application logic does not need to be changed or mocked when writing tests.\n\n- **React Testing Library (DOM)**\n  - [DOM Testing Library: Setup](https://testing-library.com/docs/dom-testing-library/setup)\n  - [React Testing Library: Setup](https://testing-library.com/docs/react-testing-library/setup)\n  - [Testing Library Jest-DOM Matchers](https://testing-library.com/docs/ecosystem-jest-dom)\n- **Native Testing Library (React Native)**\n  - [Native Testing Library: Setup](https://testing-library.com/docs/react-native-testing-library/setup)\n- **User Event Testing Library (DOM)**\n  - [User Event Testing Library: Setup](https://testing-library.com/docs/user-event/setup)\n- **TypeScript for Jest**\n  - [TypeScript for Jest: Setup](https://kulshekhar.github.io/ts-jest/docs/getting-started/installation)\n- **TypeScript for Node**\n  - [TypeScript for Node: Setup](https://typestrong.org/ts-node/docs/installation)\n- **Mock Service Worker**\n  - [MSW: Installation](https://mswjs.io/docs/getting-started/install)\n  - [MSW: Setting up mock requests](https://mswjs.io/docs/getting-started/mocks/rest-api)\n  - [MSW: Mock server configuration for Node](https://mswjs.io/docs/getting-started/integrate/node)\n\n## Setting Up Zustand for testing\n\n> **Note**: Since Jest and Vitest have slight differences, like Vitest using **ES modules** and Jest using\n> **CommonJS modules**, you need to keep that in mind if you are using Vitest instead of Jest.\n\nThe mock provided below will enable the relevant test runner to reset the zustand stores after each test.\n\n### Shared code just for testing purposes\n\nThis shared code was added to avoid code duplication in our demo since we use the same counter store\ncreator for both implementations, with and without `Context` API — `createStore` and `create`, respectively.\n\n```ts\n// shared/counter-store-creator.ts\nimport { type StateCreator } from 'zustand'\n\nexport type CounterStore = {\n  count: number\n  inc: () => void\n}\n\nexport const counterStoreCreator: StateCreator<CounterStore> = (set) => ({\n  count: 1,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n})\n```\n\n### Jest\n\nIn the next steps we are going to setup our Jest environment in order to mock Zustand.\n\n```ts\n// __mocks__/zustand.ts\nimport { act } from '@testing-library/react'\nimport type * as ZustandExportedTypes from 'zustand'\nexport * from 'zustand'\n\nconst { create: actualCreate, createStore: actualCreateStore } =\n  jest.requireActual<typeof ZustandExportedTypes>('zustand')\n\n// a variable to hold reset functions for all stores declared in the app\nexport const storeResetFns = new Set<() => void>()\n\nconst createUncurried = <T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  const store = actualCreate(stateCreator)\n  const initialState = store.getInitialState()\n  storeResetFns.add(() => {\n    store.setState(initialState, true)\n  })\n  return store\n}\n\n// when creating a store, we get its initial state, create a reset function and add it in the set\nexport const create = (<T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  console.log('zustand create mock')\n\n  // to support curried version of create\n  return typeof stateCreator === 'function'\n    ? createUncurried(stateCreator)\n    : createUncurried\n}) as typeof ZustandExportedTypes.create\n\nconst createStoreUncurried = <T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  const store = actualCreateStore(stateCreator)\n  const initialState = store.getInitialState()\n  storeResetFns.add(() => {\n    store.setState(initialState, true)\n  })\n  return store\n}\n\n// when creating a store, we get its initial state, create a reset function and add it in the set\nexport const createStore = (<T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  console.log('zustand createStore mock')\n\n  // to support curried version of createStore\n  return typeof stateCreator === 'function'\n    ? createStoreUncurried(stateCreator)\n    : createStoreUncurried\n}) as typeof ZustandExportedTypes.createStore\n\n// reset all stores after each test run\nafterEach(() => {\n  act(() => {\n    storeResetFns.forEach((resetFn) => {\n      resetFn()\n    })\n  })\n})\n```\n\n```ts\n// setup-jest.ts\nimport '@testing-library/jest-dom'\n```\n\n```ts\n// jest.config.ts\nimport type { JestConfigWithTsJest } from 'ts-jest'\n\nconst config: JestConfigWithTsJest = {\n  preset: 'ts-jest',\n  testEnvironment: 'jsdom',\n  setupFilesAfterEnv: ['./setup-jest.ts'],\n}\n\nexport default config\n```\n\n> **Note**: to use TypeScript we need to install two packages `ts-jest` and `ts-node`.\n\n### Vitest\n\nIn the next steps we are going to setup our Vitest environment in order to mock Zustand.\n\n> **Warning:** In Vitest you can change the [root](https://vitest.dev/config/#root).\n> Due to that, you need make sure that you are creating your `__mocks__` directory in the right place.\n> Let's say that you change the **root** to `./src`, that means you need to create a `__mocks__`\n> directory under `./src`. The end result would be `./src/__mocks__`, rather than `./__mocks__`.\n> Creating `__mocks__` directory in the wrong place can lead to issues when using Vitest.\n\n```ts\n// __mocks__/zustand.ts\nimport { act } from '@testing-library/react'\nimport type * as ZustandExportedTypes from 'zustand'\nexport * from 'zustand'\n\nconst { create: actualCreate, createStore: actualCreateStore } =\n  await vi.importActual<typeof ZustandExportedTypes>('zustand')\n\n// a variable to hold reset functions for all stores declared in the app\nexport const storeResetFns = new Set<() => void>()\n\nconst createUncurried = <T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  const store = actualCreate(stateCreator)\n  const initialState = store.getInitialState()\n  storeResetFns.add(() => {\n    store.setState(initialState, true)\n  })\n  return store\n}\n\n// when creating a store, we get its initial state, create a reset function and add it in the set\nexport const create = (<T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  console.log('zustand create mock')\n\n  // to support curried version of create\n  return typeof stateCreator === 'function'\n    ? createUncurried(stateCreator)\n    : createUncurried\n}) as typeof ZustandExportedTypes.create\n\nconst createStoreUncurried = <T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  const store = actualCreateStore(stateCreator)\n  const initialState = store.getInitialState()\n  storeResetFns.add(() => {\n    store.setState(initialState, true)\n  })\n  return store\n}\n\n// when creating a store, we get its initial state, create a reset function and add it in the set\nexport const createStore = (<T>(\n  stateCreator: ZustandExportedTypes.StateCreator<T>,\n) => {\n  console.log('zustand createStore mock')\n\n  // to support curried version of createStore\n  return typeof stateCreator === 'function'\n    ? createStoreUncurried(stateCreator)\n    : createStoreUncurried\n}) as typeof ZustandExportedTypes.createStore\n\n// reset all stores after each test run\nafterEach(() => {\n  act(() => {\n    storeResetFns.forEach((resetFn) => {\n      resetFn()\n    })\n  })\n})\n```\n\n> **Note**: without [globals configuration](https://vitest.dev/config/#globals) enabled, we need\n> to add `import { afterEach, vi } from 'vitest'` at the top.\n\n```ts\n// global.d.ts\n/// <reference types=\"vite/client\" />\n/// <reference types=\"vitest/globals\" />\n```\n\n> **Note**: without [globals configuration](https://vitest.dev/config/#globals) enabled, we do\n> need to remove `/// <reference types=\"vitest/globals\" />`.\n\n```ts\n// setup-vitest.ts\nimport '@testing-library/jest-dom/vitest'\n\nvi.mock('zustand') // to make it work like Jest (auto-mocking)\n```\n\n> **Note**: without [globals configuration](https://vitest.dev/config/#globals) enabled, we need\n> to add `import { vi } from 'vitest'` at the top.\n\n```ts\n// vitest.config.ts\nimport { defineConfig, mergeConfig } from 'vitest/config'\nimport viteConfig from './vite.config'\n\nexport default defineConfig((configEnv) =>\n  mergeConfig(\n    viteConfig(configEnv),\n    defineConfig({\n      test: {\n        globals: true,\n        environment: 'jsdom',\n        setupFiles: ['./setup-vitest.ts'],\n      },\n    }),\n  ),\n)\n```\n\n### Testing Components\n\nIn the next examples we are going to use `useCounterStore`\n\n> **Note**: all of these examples are written using TypeScript.\n\n```ts\n// shared/counter-store-creator.ts\nimport { type StateCreator } from 'zustand'\n\nexport type CounterStore = {\n  count: number\n  inc: () => void\n}\n\nexport const counterStoreCreator: StateCreator<CounterStore> = (set) => ({\n  count: 1,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n})\n```\n\n```ts\n// stores/use-counter-store.ts\nimport { create } from 'zustand'\n\nimport {\n  type CounterStore,\n  counterStoreCreator,\n} from '../shared/counter-store-creator'\n\nexport const useCounterStore = create<CounterStore>()(counterStoreCreator)\n```\n\n```tsx\n// contexts/use-counter-store-context.tsx\nimport { type ReactNode, createContext, useContext, useState } from 'react'\nimport { createStore } from 'zustand'\nimport { useStoreWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/shallow'\n\nimport {\n  type CounterStore,\n  counterStoreCreator,\n} from '../shared/counter-store-creator'\n\nexport const createCounterStore = () => {\n  return createStore<CounterStore>(counterStoreCreator)\n}\n\nexport type CounterStoreApi = ReturnType<typeof createCounterStore>\n\nexport const CounterStoreContext = createContext<CounterStoreApi | undefined>(\n  undefined,\n)\n\nexport interface CounterStoreProviderProps {\n  children: ReactNode\n}\n\nexport const CounterStoreProvider = ({\n  children,\n}: CounterStoreProviderProps) => {\n  const [store] = useState(() => createCounterStore())\n  return (\n    <CounterStoreContext.Provider value={store}>\n      {children}\n    </CounterStoreContext.Provider>\n  )\n}\n\nexport type UseCounterStoreContextSelector<T> = (store: CounterStore) => T\n\nexport const useCounterStoreContext = <T,>(\n  selector: UseCounterStoreContextSelector<T>,\n): T => {\n  const counterStoreContext = useContext(CounterStoreContext)\n\n  if (counterStoreContext === undefined) {\n    throw new Error(\n      'useCounterStoreContext must be used within CounterStoreProvider',\n    )\n  }\n\n  return useStoreWithEqualityFn(counterStoreContext, selector, shallow)\n}\n```\n\n```tsx\n// components/counter/counter.tsx\nimport { useCounterStore } from '../../stores/use-counter-store'\n\nexport function Counter() {\n  const { count, inc } = useCounterStore()\n\n  return (\n    <div>\n      <h2>Counter Store</h2>\n      <h4>{count}</h4>\n      <button onClick={inc}>One Up</button>\n    </div>\n  )\n}\n```\n\n```ts\n// components/counter/index.ts\nexport * from './counter'\n```\n\n```tsx\n// components/counter/counter.test.tsx\nimport { act, render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\n\nimport { Counter } from './counter'\n\ndescribe('Counter', () => {\n  test('should render with initial state of 1', async () => {\n    renderCounter()\n\n    expect(await screen.findByText(/^1$/)).toBeInTheDocument()\n    expect(\n      await screen.findByRole('button', { name: /one up/i }),\n    ).toBeInTheDocument()\n  })\n\n  test('should increase count by clicking a button', async () => {\n    const user = userEvent.setup()\n\n    renderCounter()\n\n    expect(await screen.findByText(/^1$/)).toBeInTheDocument()\n\n    await user.click(await screen.findByRole('button', { name: /one up/i }))\n\n    expect(await screen.findByText(/^2$/)).toBeInTheDocument()\n  })\n})\n\nconst renderCounter = () => {\n  return render(<Counter />)\n}\n```\n\n```tsx\n// components/counter-with-context/counter-with-context.tsx\nimport {\n  CounterStoreProvider,\n  useCounterStoreContext,\n} from '../../contexts/use-counter-store-context'\n\nconst Counter = () => {\n  const { count, inc } = useCounterStoreContext((state) => state)\n\n  return (\n    <div>\n      <h2>Counter Store Context</h2>\n      <h4>{count}</h4>\n      <button onClick={inc}>One Up</button>\n    </div>\n  )\n}\n\nexport const CounterWithContext = () => {\n  return (\n    <CounterStoreProvider>\n      <Counter />\n    </CounterStoreProvider>\n  )\n}\n```\n\n```tsx\n// components/counter-with-context/index.ts\nexport * from './counter-with-context'\n```\n\n```tsx\n// components/counter-with-context/counter-with-context.test.tsx\nimport { act, render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\n\nimport { CounterWithContext } from './counter-with-context'\n\ndescribe('CounterWithContext', () => {\n  test('should render with initial state of 1', async () => {\n    renderCounterWithContext()\n\n    expect(await screen.findByText(/^1$/)).toBeInTheDocument()\n    expect(\n      await screen.findByRole('button', { name: /one up/i }),\n    ).toBeInTheDocument()\n  })\n\n  test('should increase count by clicking a button', async () => {\n    const user = userEvent.setup()\n\n    renderCounterWithContext()\n\n    expect(await screen.findByText(/^1$/)).toBeInTheDocument()\n\n    await user.click(await screen.findByRole('button', { name: /one up/i }))\n\n    expect(await screen.findByText(/^2$/)).toBeInTheDocument()\n  })\n})\n\nconst renderCounterWithContext = () => {\n  return render(<CounterWithContext />)\n}\n```\n\n> **Note**: without [globals configuration](https://vitest.dev/config/#globals) enabled, we need\n> to add `import { describe, test, expect } from 'vitest'` at the top of each test file.\n\n### Testing Stores\n\nIn the next examples we are going to use `useCounterStore`\n\n> **Note**: all of these examples are written using TypeScript.\n\n```ts\n// shared/counter-store-creator.ts\nimport { type StateCreator } from 'zustand'\n\nexport type CounterStore = {\n  count: number\n  inc: () => void\n}\n\nexport const counterStoreCreator: StateCreator<CounterStore> = (set) => ({\n  count: 1,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n})\n```\n\n```ts\n// stores/use-counter-store.ts\nimport { create } from 'zustand'\n\nimport {\n  type CounterStore,\n  counterStoreCreator,\n} from '../shared/counter-store-creator'\n\nexport const useCounterStore = create<CounterStore>()(counterStoreCreator)\n```\n\n```tsx\n// contexts/use-counter-store-context.tsx\nimport { type ReactNode, createContext, useContext, useState } from 'react'\nimport { createStore } from 'zustand'\nimport { useStoreWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/shallow'\n\nimport {\n  type CounterStore,\n  counterStoreCreator,\n} from '../shared/counter-store-creator'\n\nexport const createCounterStore = () => {\n  return createStore<CounterStore>(counterStoreCreator)\n}\n\nexport type CounterStoreApi = ReturnType<typeof createCounterStore>\n\nexport const CounterStoreContext = createContext<CounterStoreApi | undefined>(\n  undefined,\n)\n\nexport interface CounterStoreProviderProps {\n  children: ReactNode\n}\n\nexport const CounterStoreProvider = ({\n  children,\n}: CounterStoreProviderProps) => {\n  const [store] = useState(() => createCounterStore())\n  return (\n    <CounterStoreContext.Provider value={store}>\n      {children}\n    </CounterStoreContext.Provider>\n  )\n}\n\nexport type UseCounterStoreContextSelector<T> = (store: CounterStore) => T\n\nexport const useCounterStoreContext = <T,>(\n  selector: UseCounterStoreContextSelector<T>,\n): T => {\n  const counterStoreContext = useContext(CounterStoreContext)\n\n  if (counterStoreContext === undefined) {\n    throw new Error(\n      'useCounterStoreContext must be used within CounterStoreProvider',\n    )\n  }\n\n  return useStoreWithEqualityFn(counterStoreContext, selector, shallow)\n}\n```\n\n```tsx\n// components/counter/counter.tsx\nimport { useCounterStore } from '../../stores/use-counter-store'\n\nexport function Counter() {\n  const { count, inc } = useCounterStore()\n\n  return (\n    <div>\n      <h2>Counter Store</h2>\n      <h4>{count}</h4>\n      <button onClick={inc}>One Up</button>\n    </div>\n  )\n}\n```\n\n```ts\n// components/counter/index.ts\nexport * from './counter'\n```\n\n```tsx\n// components/counter/counter.test.tsx\nimport { act, render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\n\nimport { Counter, useCounterStore } from '../../../stores/use-counter-store.ts'\n\ndescribe('Counter', () => {\n  test('should render with initial state of 1', async () => {\n    renderCounter()\n\n    expect(useCounterStore.getState().count).toBe(1)\n  })\n\n  test('should increase count by clicking a button', async () => {\n    const user = userEvent.setup()\n\n    renderCounter()\n\n    expect(useCounterStore.getState().count).toBe(1)\n\n    await user.click(await screen.findByRole('button', { name: /one up/i }))\n\n    expect(useCounterStore.getState().count).toBe(2)\n  })\n})\n\nconst renderCounter = () => {\n  return render(<Counter />)\n}\n```\n\n```tsx\n// components/counter-with-context/counter-with-context.tsx\nimport {\n  CounterStoreProvider,\n  useCounterStoreContext,\n} from '../../contexts/use-counter-store-context'\n\nconst Counter = () => {\n  const { count, inc } = useCounterStoreContext((state) => state)\n\n  return (\n    <div>\n      <h2>Counter Store Context</h2>\n      <h4>{count}</h4>\n      <button onClick={inc}>One Up</button>\n    </div>\n  )\n}\n\nexport const CounterWithContext = () => {\n  return (\n    <CounterStoreProvider>\n      <Counter />\n    </CounterStoreProvider>\n  )\n}\n```\n\n```tsx\n// components/counter-with-context/index.ts\nexport * from './counter-with-context'\n```\n\n```tsx\n// components/counter-with-context/counter-with-context.test.tsx\nimport { act, render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\n\nimport { CounterStoreContext } from '../../../contexts/use-counter-store-context'\nimport { counterStoreCreator } from '../../../shared/counter-store-creator'\n\ndescribe('CounterWithContext', () => {\n  test('should render with initial state of 1', async () => {\n    const counterStore = counterStoreCreator()\n\n    renderCounterWithContext(counterStore)\n\n    expect(counterStore.getState().count).toBe(1)\n    expect(\n      await screen.findByRole('button', { name: /one up/i }),\n    ).toBeInTheDocument()\n  })\n\n  test('should increase count by clicking a button', async () => {\n    const user = userEvent.setup()\n    const counterStore = counterStoreCreator()\n\n    renderCounterWithContext(counterStore)\n\n    expect(counterStore.getState().count).toBe(1)\n\n    await user.click(await screen.findByRole('button', { name: /one up/i }))\n\n    expect(counterStore.getState().count).toBe(2)\n  })\n})\n\nconst renderCounterWithContext = (store) => {\n  return render(<CounterWithContext />, {\n    wrapper: ({ children }) => (\n      <CounterStoreContext.Provider value={store}>\n        {children}\n      </CounterStoreContext.Provider>\n    ),\n  })\n}\n```\n\n## References\n\n- **React Testing Library**: [React Testing Library (RTL)](https://testing-library.com/docs/react-testing-library/intro)\n  is a very lightweight solution for testing React components. It provides utility functions on top\n  of `react-dom` and `react-dom/test-utils`, in a way that encourages better testing practices. Its\n  primary guiding principle is: \"The more your tests resemble the way your software is used, the\n  more confidence they can give you.\"\n- **Native Testing Library**: [Native Testing Library (RNTL)](https://testing-library.com/docs/react-native-testing-library/intro)\n  is a very lightweight solution for testing React Native components, similarly to RTL, but its\n  functions are built on top of `react-test-renderer`.\n- **Testing Implementation Details**: Blog post by Kent C. Dodds on why he recommends to avoid\n  [testing implementation details](https://kentcdodds.com/blog/testing-implementation-details).\n\n## Demos\n\n- Jest: https://stackblitz.com/edit/jest-zustand\n- Vitest: https://stackblitz.com/edit/vitest-zustand\n"
  },
  {
    "path": "docs/learn/guides/tutorial-tic-tac-toe.md",
    "content": "---\ntitle: 'Tutorial: Tic-Tac-Toe'\ndescription: Building a game\nnav: 3\n---\n\n# Tutorial: Tic-Tac-Toe\n\n## Building a game\n\nYou will build a small tic-tac-toe game during this tutorial. This tutorial does assume existing\nReact knowledge. The techniques you'll learn in the tutorial are fundamental to building any React\napp, and fully understanding it will give you a deep understanding of React and Zustand.\n\n> [!NOTE]\n> This tutorial is crafted for those who learn best through hands-on experience and want to swiftly\n> create something tangible. It draws inspiration from React's tic-tac-toe tutorial.\n\nThe tutorial is divided into several sections:\n\n- Setup for the tutorial will give you a starting point to follow the tutorial.\n- Overview will teach you the fundamentals of React: components, props, and state.\n- Completing the game will teach you the most common techniques in React development.\n- Adding time travel will give you a deeper insight into the unique strengths of React.\n\n### What are you building?\n\nIn this tutorial, you'll build an interactive tic-tac-toe game with React and Zustand.\n\nYou can see what it will look like when you're finished here:\n\n```jsx\nimport { create } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\nconst useGameStore = create(\n  combine(\n    {\n      history: [Array(9).fill(null)],\n      currentMove: 0,\n    },\n    (set, get) => {\n      return {\n        setHistory: (nextHistory) => {\n          set((state) => ({\n            history:\n              typeof nextHistory === 'function'\n                ? nextHistory(state.history)\n                : nextHistory,\n          }))\n        },\n        setCurrentMove: (nextCurrentMove) => {\n          set((state) => ({\n            currentMove:\n              typeof nextCurrentMove === 'function'\n                ? nextCurrentMove(state.currentMove)\n                : nextCurrentMove,\n          }))\n        },\n      }\n    },\n  ),\n)\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button\n      style={{\n        display: 'inline-flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        padding: 0,\n        backgroundColor: '#fff',\n        border: '1px solid #999',\n        outline: 0,\n        borderRadius: 0,\n        fontSize: '1rem',\n        fontWeight: 'bold',\n      }}\n      onClick={onSquareClick}\n    >\n      {value}\n    </button>\n  )\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  const winner = calculateWinner(squares)\n  const turns = calculateTurns(squares)\n  const player = xIsNext ? 'X' : 'O'\n  const status = calculateStatus(winner, turns, player)\n\n  function handleClick(i) {\n    if (squares[i] || winner) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    onPlay(nextSquares)\n  }\n\n  return (\n    <>\n      <div style={{ marginBottom: '0.5rem' }}>{status}</div>\n      <div\n        style={{\n          display: 'grid',\n          gridTemplateColumns: 'repeat(3, 1fr)',\n          gridTemplateRows: 'repeat(3, 1fr)',\n          width: 'calc(3 * 2.5rem)',\n          height: 'calc(3 * 2.5rem)',\n          border: '1px solid #999',\n        }}\n      >\n        {squares.map((_, i) => (\n          <Square\n            key={`square-${i}`}\n            value={squares[i]}\n            onSquareClick={() => handleClick(i)}\n          />\n        ))}\n      </div>\n    </>\n  )\n}\n\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const currentMove = useGameStore((state) => state.currentMove)\n  const setCurrentMove = useGameStore((state) => state.setCurrentMove)\n  const xIsNext = currentMove % 2 === 0\n  const currentSquares = history[currentMove]\n\n  function handlePlay(nextSquares) {\n    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]\n    setHistory(nextHistory)\n    setCurrentMove(nextHistory.length - 1)\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>\n          {history.map((_, historyIndex) => {\n            const description =\n              historyIndex > 0\n                ? `Go to move #${historyIndex}`\n                : 'Go to game start'\n\n            return (\n              <li key={historyIndex}>\n                <button onClick={() => jumpTo(historyIndex)}>\n                  {description}\n                </button>\n              </li>\n            )\n          })}\n        </ol>\n      </div>\n    </div>\n  )\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ]\n\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i]\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a]\n    }\n  }\n\n  return null\n}\n\nfunction calculateTurns(squares) {\n  return squares.filter((square) => !square).length\n}\n\nfunction calculateStatus(winner, turns, player) {\n  if (!winner && !turns) return 'Draw'\n  if (winner) return `Winner ${winner}`\n  return `Next player: ${player}`\n}\n```\n\n### Building the board\n\nLet's start by creating the `Square` component, which will be a building block for our `Board`\ncomponent. This component will represent each square in our game.\n\nThe `Square` component should take `value` and `onSquareClick` as props. It should return a\n`<button>` element, styled to look like a square. The button displays the value prop, which can be\n`'X'`, `'O'`, or `null`, depending on the game's state. When the button is clicked, it triggers the\n`onSquareClick` function passed in as a prop, allowing the game to respond to user input.\n\nHere's the code for the `Square` component:\n\n```jsx\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button\n      style={{\n        display: 'inline-flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        padding: 0,\n        backgroundColor: '#fff',\n        border: '1px solid #999',\n        outline: 0,\n        borderRadius: 0,\n        fontSize: '1rem',\n        fontWeight: 'bold',\n      }}\n      onClick={onSquareClick}\n    >\n      {value}\n    </button>\n  )\n}\n```\n\nLet's move on to creating the Board component, which will consist of 9 squares arranged in a grid.\nThis component will serve as the main playing area for our game.\n\nThe `Board` component should return a `<div>` element styled as a grid. The grid layout is achieved\nusing CSS Grid, with three columns and three rows, each taking up an equal fraction of the available\nspace. The overall size of the grid is determined by the width and height properties, ensuring that\nit is square-shaped and appropriately sized.\n\nInside the grid, we place nine Square components, each with a value prop representing its position.\nThese Square components will eventually hold the game symbols (`'X'` or `'O'`) and handle user\ninteractions.\n\nHere's the code for the `Board` component:\n\n```jsx\nexport default function Board() {\n  return (\n    <div\n      style={{\n        display: 'grid',\n        gridTemplateColumns: 'repeat(3, 1fr)',\n        gridTemplateRows: 'repeat(3, 1fr)',\n        width: 'calc(3 * 2.5rem)',\n        height: 'calc(3 * 2.5rem)',\n        border: '1px solid #999',\n      }}\n    >\n      <Square value=\"1\" />\n      <Square value=\"2\" />\n      <Square value=\"3\" />\n      <Square value=\"4\" />\n      <Square value=\"5\" />\n      <Square value=\"6\" />\n      <Square value=\"7\" />\n      <Square value=\"8\" />\n      <Square value=\"9\" />\n    </div>\n  )\n}\n```\n\nThis Board component sets up the basic structure for our game board by arranging nine squares in a\n3x3 grid. It positions the squares neatly, providing a foundation for adding more features and\nhandling player interactions in the future.\n\n### Lifting state up\n\nEach `Square` component could maintain a part of the game's state. To check for a winner in a\ntic-tac-toe game, the `Board` component would need to somehow know the state of each of the 9\n`Square` components.\n\nHow would you approach that? At first, you might guess that the `Board` component needs to ask each\n`Square` component for that `Square`'s component state. Although this approach is technically\npossible in React, we discourage it because the code becomes difficult to understand, susceptible\nto bugs, and hard to refactor. Instead, the best approach is to store the game's state in the\nparent `Board` component instead of in each `Square` component. The `Board` component can tell each\n`Square` component what to display by passing a prop, like you did when you passed a number to each\n`Square` component.\n\n> [!IMPORTANT]\n> To collect data from multiple children, or to have two or more child components\n> communicate with each other, declare the shared state in their parent component instead. The\n> parent component can pass that state back down to the children via props. This keeps the child\n> components in sync with each other and with their parent.\n\nLet's take this opportunity to try it out. Edit the `Board` component so that it declares a state\nvariable named squares that defaults to an array of 9 nulls corresponding to the 9 squares:\n\n```jsx\nimport { create } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\nconst useGameStore = create(\n  combine({ squares: Array(9).fill(null) }, (set) => {\n    return {\n      setSquares: (nextSquares) => {\n        set((state) => ({\n          squares:\n            typeof nextSquares === 'function'\n              ? nextSquares(state.squares)\n              : nextSquares,\n        }))\n      },\n    }\n  }),\n)\n\nexport default function Board() {\n  const squares = useGameStore((state) => state.squares)\n  const setSquares = useGameStore((state) => state.setSquares)\n\n  return (\n    <div\n      style={{\n        display: 'grid',\n        gridTemplateColumns: 'repeat(3, 1fr)',\n        gridTemplateRows: 'repeat(3, 1fr)',\n        width: 'calc(3 * 2.5rem)',\n        height: 'calc(3 * 2.5rem)',\n        border: '1px solid #999',\n      }}\n    >\n      {squares.map((square, squareIndex) => (\n        <Square key={squareIndex} value={square} />\n      ))}\n    </div>\n  )\n}\n```\n\n`Array(9).fill(null)` creates an array with nine elements and sets each of them to `null`. The\n`useGameStore` declares a `squares` state that's initially set to that array. Each entry in the\narray corresponds to the value of a square. When you fill the board in later, the squares array\nwill look like this:\n\n```js\nconst squares = ['O', null, 'X', 'X', 'X', 'O', 'O', null, null]\n```\n\nEach Square will now receive a `value` prop that will either be `'X'`, `'O'`, or `null` for empty\nsquares.\n\nNext, you need to change what happens when a `Square` component is clicked. The `Board` component\nnow maintains which squares are filled. You'll need to create a way for the `Square` component to\nupdate the `Board`'s component state. Since state is private to a component that defines it, you\ncannot update the `Board`'s component state directly from `Square` component.\n\nInstead, you'll pass down a function from the Board component to the `Square` component, and you'll\nhave `Square` component call that function when a square is clicked. You'll start with the function\nthat the `Square` component will call when it is clicked. You'll call that function `onSquareClick`:\n\nNow you'll connect the `onSquareClick` prop to a function in the `Board` component that you'll name\n`handleClick`. To connect `onSquareClick` to `handleClick` you'll pass an inline function to the\n`onSquareClick` prop of the first Square component:\n\n```jsx\n<Square key={squareIndex} value={square} onSquareClick={() => handleClick(i)} />\n```\n\nLastly, you will define the `handleClick` function inside the `Board` component to update the\nsquares array holding your board's state.\n\nThe `handleClick` function should take the index of the square to update and create a copy of the\n`squares` array (`nextSquares`). Then, `handleClick` updates the `nextSquares` array by adding `X`\nto the square at the specified index (`i`) if is not already filled.\n\n```jsx {5-10,27}\nexport default function Board() {\n  const squares = useGameStore((state) => state.squares)\n  const setSquares = useGameStore((state) => state.setSquares)\n\n  function handleClick(i) {\n    if (squares[i]) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = 'X'\n    setSquares(nextSquares)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'grid',\n        gridTemplateColumns: 'repeat(3, 1fr)',\n        gridTemplateRows: 'repeat(3, 1fr)',\n        width: 'calc(3 * 2.5rem)',\n        height: 'calc(3 * 2.5rem)',\n        border: '1px solid #999',\n      }}\n    >\n      {squares.map((square, squareIndex) => (\n        <Square\n          key={squareIndex}\n          value={square}\n          onSquareClick={() => handleClick(squareIndex)}\n        />\n      ))}\n    </div>\n  )\n}\n```\n\n> [!IMPORTANT]\n> Note how in `handleClick` function, you call `.slice()` to create a copy of the squares array\n> instead of modifying the existing array.\n\n### Taking turns\n\nIt's now time to fix a major defect in this tic-tac-toe game: the `'O'`s cannot be used on the\nboard.\n\nYou'll set the first move to be `'X'` by default. Let's keep track of this by adding another piece\nof state to the `useGameStore` hook:\n\n```jsx {2,12-18}\nconst useGameStore = create(\n  combine({ squares: Array(9).fill(null), xIsNext: true }, (set) => {\n    return {\n      setSquares: (nextSquares) => {\n        set((state) => ({\n          squares:\n            typeof nextSquares === 'function'\n              ? nextSquares(state.squares)\n              : nextSquares,\n        }))\n      },\n      setXIsNext: (nextXIsNext) => {\n        set((state) => ({\n          xIsNext:\n            typeof nextXIsNext === 'function'\n              ? nextXIsNext(state.xIsNext)\n              : nextXIsNext,\n        }))\n      },\n    }\n  }),\n)\n```\n\nEach time a player moves, `xIsNext` (a boolean) will be flipped to determine which player goes next\nand the game's state will be saved. You'll update the Board's `handleClick` function to flip the\nvalue of `xIsNext`:\n\n```jsx {2-3,6,11}\nexport default function Board() {\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const squares = useGameStore((state) => state.squares)\n  const setSquares = useGameStore((state) => state.setSquares)\n  const player = xIsNext ? 'X' : 'O'\n\n  function handleClick(i) {\n    if (squares[i]) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    setSquares(nextSquares)\n    setXIsNext(!xIsNext)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'grid',\n        gridTemplateColumns: 'repeat(3, 1fr)',\n        gridTemplateRows: 'repeat(3, 1fr)',\n        width: 'calc(3 * 2.5rem)',\n        height: 'calc(3 * 2.5rem)',\n        border: '1px solid #999',\n      }}\n    >\n      {squares.map((square, squareIndex) => (\n        <Square\n          key={squareIndex}\n          value={square}\n          onSquareClick={() => handleClick(squareIndex)}\n        />\n      ))}\n    </div>\n  )\n}\n```\n\n### Declaring a winner or draw\n\nNow that the players can take turns, you'll want to show when the game is won or drawn and there\nare no more turns to make. To do this you'll add three helper functions. The first helper function\ncalled `calculateWinner` that takes an array of 9 squares, checks for a winner and returns `'X'`,\n`'O'`, or `null` as appropriate. The second helper function called `calculateTurns` that takes the\nsame array, checks for remaining turns by filtering out only `null` items, and returns the count of\nthem. The last helper called `calculateStatus` that takes the remaining turns, the winner, and the\ncurrent player (`'X' or 'O'`):\n\n```js\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ]\n\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i]\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a]\n    }\n  }\n\n  return null\n}\n\nfunction calculateTurns(squares) {\n  return squares.filter((square) => !square).length\n}\n\nfunction calculateStatus(winner, turns, player) {\n  if (!winner && !turns) return 'Draw'\n  if (winner) return `Winner ${winner}`\n  return `Next player: ${player}`\n}\n```\n\nYou will use the result of `calculateWinner(squares)` in the Board component's `handleClick`\nfunction to check if a player has won. You can perform this check at the same time you check if a\nuser has clicked a square that already has a `'X'` or and `'O'`. We'd like to return early in\nboth cases:\n\n```js {2}\nfunction handleClick(i) {\n  if (squares[i] || winner) return\n  const nextSquares = squares.slice()\n  nextSquares[i] = player\n  setSquares(nextSquares)\n  setXIsNext(!xIsNext)\n}\n```\n\nTo let the players know when the game is over, you can display text such as `'Winner: X'` or\n`'Winner: O'`. To do that you'll add a `status` section to the `Board` component. The status will\ndisplay the winner or draw if the game is over and if the game is ongoing you'll display which\nplayer's turn is next:\n\n```jsx {6-7,9,21}\nexport default function Board() {\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const squares = useGameStore((state) => state.squares)\n  const setSquares = useGameStore((state) => state.setSquares)\n  const winner = calculateWinner(squares)\n  const turns = calculateTurns(squares)\n  const player = xIsNext ? 'X' : 'O'\n  const status = calculateStatus(winner, turns, player)\n\n  function handleClick(i) {\n    if (squares[i] || winner) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    setSquares(nextSquares)\n    setXIsNext(!xIsNext)\n  }\n\n  return (\n    <>\n      <div style={{ marginBottom: '0.5rem' }}>{status}</div>\n      <div\n        style={{\n          display: 'grid',\n          gridTemplateColumns: 'repeat(3, 1fr)',\n          gridTemplateRows: 'repeat(3, 1fr)',\n          width: 'calc(3 * 2.5rem)',\n          height: 'calc(3 * 2.5rem)',\n          border: '1px solid #999',\n        }}\n      >\n        {squares.map((square, squareIndex) => (\n          <Square\n            key={squareIndex}\n            value={square}\n            onSquareClick={() => handleClick(squareIndex)}\n          />\n        ))}\n      </div>\n    </>\n  )\n}\n```\n\nCongratulations! You now have a working tic-tac-toe game. And you've just learned the basics of\nReact and Zustand too. So you are the real winner here. Here is what the code should look like:\n\n```jsx\nimport { create } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\nconst useGameStore = create(\n  combine({ squares: Array(9).fill(null), xIsNext: true }, (set) => {\n    return {\n      setSquares: (nextSquares) => {\n        set((state) => ({\n          squares:\n            typeof nextSquares === 'function'\n              ? nextSquares(state.squares)\n              : nextSquares,\n        }))\n      },\n      setXIsNext: (nextXIsNext) => {\n        set((state) => ({\n          xIsNext:\n            typeof nextXIsNext === 'function'\n              ? nextXIsNext(state.xIsNext)\n              : nextXIsNext,\n        }))\n      },\n    }\n  }),\n)\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button\n      style={{\n        display: 'inline-flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        padding: 0,\n        backgroundColor: '#fff',\n        border: '1px solid #999',\n        outline: 0,\n        borderRadius: 0,\n        fontSize: '1rem',\n        fontWeight: 'bold',\n      }}\n      onClick={onSquareClick}\n    >\n      {value}\n    </button>\n  )\n}\n\nexport default function Board() {\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const squares = useGameStore((state) => state.squares)\n  const setSquares = useGameStore((state) => state.setSquares)\n  const winner = calculateWinner(squares)\n  const turns = calculateTurns(squares)\n  const player = xIsNext ? 'X' : 'O'\n  const status = calculateStatus(winner, turns, player)\n\n  function handleClick(i) {\n    if (squares[i] || winner) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    setSquares(nextSquares)\n    setXIsNext(!xIsNext)\n  }\n\n  return (\n    <>\n      <div style={{ marginBottom: '0.5rem' }}>{status}</div>\n      <div\n        style={{\n          display: 'grid',\n          gridTemplateColumns: 'repeat(3, 1fr)',\n          gridTemplateRows: 'repeat(3, 1fr)',\n          width: 'calc(3 * 2.5rem)',\n          height: 'calc(3 * 2.5rem)',\n          border: '1px solid #999',\n        }}\n      >\n        {squares.map((square, squareIndex) => (\n          <Square\n            key={squareIndex}\n            value={square}\n            onSquareClick={() => handleClick(squareIndex)}\n          />\n        ))}\n      </div>\n    </>\n  )\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ]\n\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i]\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a]\n    }\n  }\n\n  return null\n}\n\nfunction calculateTurns(squares) {\n  return squares.filter((square) => !square).length\n}\n\nfunction calculateStatus(winner, turns, player) {\n  if (!winner && !turns) return 'Draw'\n  if (winner) return `Winner ${winner}`\n  return `Next player: ${player}`\n}\n```\n\n### Adding time travel\n\nAs a final exercise, let's make it possible to “go back in time” and revisit previous moves in the\ngame.\n\nIf you had directly modified the squares array, implementing this time-travel feature would be very\ndifficult. However, since you used `slice()` to create a new copy of the squares array after every\nmove, treating it as immutable, you can store every past version of the squares array and navigate\nbetween them.\n\nYou'll keep track of these past squares arrays in a new state variable called `history`. This\n`history` array will store all board states, from the first move to the latest one, and will look\nsomething like this:\n\n```js\nconst history = [\n  // First move\n  [null, null, null, null, null, null, null, null, null],\n  // Second move\n  ['X', null, null, null, null, null, null, null, null],\n  // Third move\n  ['X', 'O', null, null, null, null, null, null, null],\n  // and so on...\n]\n```\n\nThis approach allows you to easily navigate between different game states and implement the\ntime-travel feature.\n\n### Lifting state up, again\n\nNext, you will create a new top-level component called `Game` to display a list of past moves. This\nis where you will store the `history` state that contains the entire game history.\n\nBy placing the `history` state in the `Game` component, you can remove the `squares` state from the\n`Board` component. You will now lift the state up from the `Board` component to the top-level `Game`\ncomponent. This change allows the `Game` component to have full control over the `Board`'s\ncomponent data and instruct the `Board` component to render previous turns from the `history`.\n\nFirst, add a `Game` component with `export default` and remove it from `Board` component. Here is\nwhat the code should look like:\n\n```jsx {1,44-61}\nfunction Board() {\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const squares = useGameStore((state) => state.squares)\n  const setSquares = useGameStore((state) => state.setSquares)\n  const winner = calculateWinner(squares)\n  const turns = calculateTurns(squares)\n  const player = xIsNext ? 'X' : 'O'\n  const status = calculateStatus(winner, turns, player)\n\n  function handleClick(i) {\n    if (squares[i] || winner) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    setSquares(nextSquares)\n    setXIsNext(!xIsNext)\n  }\n\n  return (\n    <>\n      <div style={{ marginBottom: '0.5rem' }}>{status}</div>\n      <div\n        style={{\n          display: 'grid',\n          gridTemplateColumns: 'repeat(3, 1fr)',\n          gridTemplateRows: 'repeat(3, 1fr)',\n          width: 'calc(3 * 2.5rem)',\n          height: 'calc(3 * 2.5rem)',\n          border: '1px solid #999',\n        }}\n      >\n        {squares.map((square, squareIndex) => (\n          <Square\n            key={squareIndex}\n            value={square}\n            onSquareClick={() => handleClick(squareIndex)}\n          />\n        ))}\n      </div>\n    </>\n  )\n}\n\nexport default function Game() {\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>{/* TODO */}</ol>\n      </div>\n    </div>\n  )\n}\n```\n\nAdd some state to the `useGameStore` hook to track the history of moves:\n\n```js {2,4-11}\nconst useGameStore = create(\n  combine({ history: [Array(9).fill(null)], xIsNext: true }, (set) => {\n    return {\n      setHistory: (nextHistory) => {\n        set((state) => ({\n          history:\n            typeof nextHistory === 'function'\n              ? nextHistory(state.history)\n              : nextHistory,\n        }))\n      },\n      setXIsNext: (nextXIsNext) => {\n        set((state) => ({\n          xIsNext:\n            typeof nextXIsNext === 'function'\n              ? nextXIsNext(state.xIsNext)\n              : nextXIsNext,\n        }))\n      },\n    }\n  }),\n)\n```\n\nNotice how `[Array(9).fill(null)]` creates an array with a single item, which is itself an array of\n9 null values.\n\nTo render the squares for the current move, you'll need to read the most recent squares array from\nthe `history` state. You don't need an extra state for this because you already have enough\ninformation to calculate it during rendering:\n\n```jsx {2-6}\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const currentSquares = history[history.length - 1]\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>{/*TODO*/}</ol>\n      </div>\n    </div>\n  )\n}\n```\n\nNext, create a `handlePlay` function inside the `Game` component that will be called by the `Board`\ncomponent to update the game. Pass `xIsNext`, `currentSquares` and `handlePlay` as props to the\n`Board` component:\n\n```jsx {8-10,21}\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const currentSquares = history[history.length - 1]\n\n  function handlePlay(nextSquares) {\n    // TODO\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>{/*TODO*/}</ol>\n      </div>\n    </div>\n  )\n}\n```\n\nLet's make the `Board` component fully controlled by the props it receives. To do this, we'll modify\nthe `Board` component to accept three props: `xIsNext`, `squares`, and a new `onPlay` function that\nthe `Board` component can call with the updated squares array when a player makes a move.\n\n```jsx {1}\nfunction Board({ xIsNext, squares, onPlay }) {\n  const winner = calculateWinner(squares)\n  const turns = calculateTurns(squares)\n  const player = xIsNext ? 'X' : 'O'\n  const status = calculateStatus(winner, turns, player)\n\n  function handleClick(i) {\n    if (squares[i] || winner) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    onPlay(nextSquares)\n  }\n\n  return (\n    <>\n      <div style={{ marginBottom: '0.5rem' }}>{status}</div>\n      <div\n        style={{\n          display: 'grid',\n          gridTemplateColumns: 'repeat(3, 1fr)',\n          gridTemplateRows: 'repeat(3, 1fr)',\n          width: 'calc(3 * 2.5rem)',\n          height: 'calc(3 * 2.5rem)',\n          border: '1px solid #999',\n        }}\n      >\n        {squares.map((square, squareIndex) => (\n          <Square\n            key={squareIndex}\n            value={square}\n            onSquareClick={() => handleClick(squareIndex)}\n          />\n        ))}\n      </div>\n    </>\n  )\n}\n```\n\nThe `Board` component is now fully controlled by the props passed to it by the `Game` component. To\nget the game working again, you need to implement the `handlePlay` function in the `Game`\ncomponent.\n\nWhat should `handlePlay` do when called? Previously, the `Board` component called `setSquares` with\nan updated array; now it passes the updated squares array to `onPlay`.\n\nThe `handlePlay` function needs to update the `Game` component's state to trigger a re-render.\nInstead of using `setSquares`, you'll update the `history` state variable by appending the updated\nsquares array as a new `history` entry. You also need to toggle `xIsNext`, just as the `Board`\ncomponent used\nto do.\n\n```js {2-3}\nfunction handlePlay(nextSquares) {\n  setHistory(history.concat([nextSquares]))\n  setXIsNext(!xIsNext)\n}\n```\n\nAt this point, you've moved the state to live in the `Game` component, and the UI should be fully\nworking, just as it was before the refactor. Here is what the code should look like at this point:\n\n```jsx\nimport { create } from 'zustand'\nimport { combine } from 'zustand/middleware'\n\nconst useGameStore = create(\n  combine({ history: [Array(9).fill(null)], xIsNext: true }, (set) => {\n    return {\n      setHistory: (nextHistory) => {\n        set((state) => ({\n          history:\n            typeof nextHistory === 'function'\n              ? nextHistory(state.history)\n              : nextHistory,\n        }))\n      },\n      setXIsNext: (nextXIsNext) => {\n        set((state) => ({\n          xIsNext:\n            typeof nextXIsNext === 'function'\n              ? nextXIsNext(state.xIsNext)\n              : nextXIsNext,\n        }))\n      },\n    }\n  }),\n)\n\nfunction Square({ value, onSquareClick }) {\n  return (\n    <button\n      style={{\n        display: 'inline-flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        padding: 0,\n        backgroundColor: '#fff',\n        border: '1px solid #999',\n        outline: 0,\n        borderRadius: 0,\n        fontSize: '1rem',\n        fontWeight: 'bold',\n      }}\n      onClick={onSquareClick}\n    >\n      {value}\n    </button>\n  )\n}\n\nfunction Board({ xIsNext, squares, onPlay }) {\n  const winner = calculateWinner(squares)\n  const turns = calculateTurns(squares)\n  const player = xIsNext ? 'X' : 'O'\n  const status = calculateStatus(winner, turns, player)\n\n  function handleClick(i) {\n    if (squares[i] || winner) return\n    const nextSquares = squares.slice()\n    nextSquares[i] = player\n    onPlay(nextSquares)\n  }\n\n  return (\n    <>\n      <div style={{ marginBottom: '0.5rem' }}>{status}</div>\n      <div\n        style={{\n          display: 'grid',\n          gridTemplateColumns: 'repeat(3, 1fr)',\n          gridTemplateRows: 'repeat(3, 1fr)',\n          width: 'calc(3 * 2.5rem)',\n          height: 'calc(3 * 2.5rem)',\n          border: '1px solid #999',\n        }}\n      >\n        {squares.map((square, squareIndex) => (\n          <Square\n            key={squareIndex}\n            value={square}\n            onSquareClick={() => handleClick(squareIndex)}\n          />\n        ))}\n      </div>\n    </>\n  )\n}\n\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const currentSquares = history[history.length - 1]\n\n  function handlePlay(nextSquares) {\n    setHistory(history.concat([nextSquares]))\n    setXIsNext(!xIsNext)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>{/*TODO*/}</ol>\n      </div>\n    </div>\n  )\n}\n\nfunction calculateWinner(squares) {\n  const lines = [\n    [0, 1, 2],\n    [3, 4, 5],\n    [6, 7, 8],\n    [0, 3, 6],\n    [1, 4, 7],\n    [2, 5, 8],\n    [0, 4, 8],\n    [2, 4, 6],\n  ]\n\n  for (let i = 0; i < lines.length; i++) {\n    const [a, b, c] = lines[i]\n    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\n      return squares[a]\n    }\n  }\n\n  return null\n}\n\nfunction calculateTurns(squares) {\n  return squares.filter((square) => !square).length\n}\n\nfunction calculateStatus(winner, turns, player) {\n  if (!winner && !turns) return 'Draw'\n  if (winner) return `Winner ${winner}`\n  return `Next player: ${player}`\n}\n```\n\n### Showing the past moves\n\nSince you are recording the tic-tac-toe game's history, you can now display a list of past moves to\nthe player.\n\nYou already have an array of `history` moves in store, so now you need to transform it to an array\nof React elements. In JavaScript, to transform one array into another, you can use the Array\n`.map()` method:\n\nYou'll use `map` to transform your `history` of moves into React elements representing buttons on the\nscreen, and display a list of buttons to **jump** to past moves. Let's `map` over the `history` in\nthe `Game` component:\n\n```jsx {29-44}\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const currentSquares = history[history.length - 1]\n\n  function handlePlay(nextSquares) {\n    setHistory(history.concat([nextSquares]))\n    setXIsNext(!xIsNext)\n  }\n\n  function jumpTo(nextMove) {\n    // TODO\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>\n          {history.map((_, historyIndex) => {\n            const description =\n              historyIndex > 0\n                ? `Go to move #${historyIndex}`\n                : 'Go to game start'\n\n            return (\n              <li key={historyIndex}>\n                <button onClick={() => jumpTo(historyIndex)}>\n                  {description}\n                </button>\n              </li>\n            )\n          })}\n        </ol>\n      </div>\n    </div>\n  )\n}\n```\n\nBefore you can implement the `jumpTo` function, you need the `Game` component to keep track of which\nstep the user is currently viewing. To do this, define a new state variable called `currentMove`,\nwhich will start at `0`:\n\n```js {3,14-21}\nconst useGameStore = create(\n  combine(\n    { history: [Array(9).fill(null)], currentMove: 0, xIsNext: true },\n    (set) => {\n      return {\n        setHistory: (nextHistory) => {\n          set((state) => ({\n            history:\n              typeof nextHistory === 'function'\n                ? nextHistory(state.history)\n                : nextHistory,\n          }))\n        },\n        setCurrentMove: (nextCurrentMove) => {\n          set((state) => ({\n            currentMove:\n              typeof nextCurrentMove === 'function'\n                ? nextCurrentMove(state.currentMove)\n                : nextCurrentMove,\n          }))\n        },\n        setXIsNext: (nextXIsNext) => {\n          set((state) => ({\n            xIsNext:\n              typeof nextXIsNext === 'function'\n                ? nextXIsNext(state.xIsNext)\n                : nextXIsNext,\n          }))\n        },\n      }\n    },\n  ),\n)\n```\n\nNext, update the `jumpTo` function inside `Game` component to update that `currentMove`. You’ll\nalso set `xIsNext` to `true` if the number that you’re changing `currentMove` to is even.\n\n```js {2-3}\nfunction jumpTo(nextMove) {\n  setCurrentMove(nextMove)\n  setXIsNext(currentMove % 2 === 0)\n}\n```\n\nYou will now make two changes to the `handlePlay` function in the `Game` component, which is called\nwhen you click on a square.\n\n- If you \"go back in time\" and then make a new move from that point, you only want to keep the\n  history up to that point. Instead of adding `nextSquares` after all items in the history (using\n  the Array `.concat()` method), you'll add it after all items in\n  `history.slice(0, currentMove + 1)` to keep only that portion of the old history.\n- Each time a move is made, you need to update `currentMove` to point to the latest history entry.\n\n```js {2-4}\nfunction handlePlay(nextSquares) {\n  const nextHistory = history.slice(0, currentMove + 1).concat([nextSquares])\n  setHistory(nextHistory)\n  setCurrentMove(nextHistory.length - 1)\n  setXIsNext(!xIsNext)\n}\n```\n\nFinally, you will modify the `Game` component to render the currently selected move, instead of\nalways rendering the final move:\n\n```jsx {2-8}\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const currentMove = useGameStore((state) => state.currentMove)\n  const setCurrentMove = useGameStore((state) => state.setCurrentMove)\n  const xIsNext = useGameStore((state) => state.xIsNext)\n  const setXIsNext = useGameStore((state) => state.setXIsNext)\n  const currentSquares = history[currentMove]\n\n  function handlePlay(nextSquares) {\n    const nextHistory = history.slice(0, currentMove + 1).concat([nextSquares])\n    setHistory(nextHistory)\n    setCurrentMove(nextHistory.length - 1)\n    setXIsNext(!xIsNext)\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove)\n    setXIsNext(nextMove % 2 === 0)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>\n          {history.map((_, historyIndex) => {\n            const description =\n              historyIndex > 0\n                ? `Go to move #${historyIndex}`\n                : 'Go to game start'\n\n            return (\n              <li key={historyIndex}>\n                <button onClick={() => jumpTo(historyIndex)}>\n                  {description}\n                </button>\n              </li>\n            )\n          })}\n        </ol>\n      </div>\n    </div>\n  )\n}\n```\n\n### Final cleanup\n\nIf you look closely at the code, you'll see that `xIsNext` is `true` when `currentMove` is even and\n`false` when `currentMove` is odd. This means that if you know the value of `currentMove`, you can\nalways determine what `xIsNext` should be.\n\nThere's no need to store `xIsNext` separately in the state. It’s better to avoid redundant state\nbecause it can reduce bugs and make your code easier to understand. Instead, you can calculate\n`xIsNext` based on `currentMove`:\n\n```jsx {2-5,13,17}\nexport default function Game() {\n  const history = useGameStore((state) => state.history)\n  const setHistory = useGameStore((state) => state.setHistory)\n  const currentMove = useGameStore((state) => state.currentMove)\n  const setCurrentMove = useGameStore((state) => state.setCurrentMove)\n  const xIsNext = currentMove % 2 === 0\n  const currentSquares = history[currentMove]\n\n  function handlePlay(nextSquares) {\n    const nextHistory = history.slice(0, currentMove + 1).concat([nextSquares])\n    setHistory(nextHistory)\n    setCurrentMove(nextHistory.length - 1)\n  }\n\n  function jumpTo(nextMove) {\n    setCurrentMove(nextMove)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'row',\n        fontFamily: 'monospace',\n      }}\n    >\n      <div>\n        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\n      </div>\n      <div style={{ marginLeft: '1rem' }}>\n        <ol>\n          {history.map((_, historyIndex) => {\n            const description =\n              historyIndex > 0\n                ? `Go to move #${historyIndex}`\n                : 'Go to game start'\n\n            return (\n              <li key={historyIndex}>\n                <button onClick={() => jumpTo(historyIndex)}>\n                  {description}\n                </button>\n              </li>\n            )\n          })}\n        </ol>\n      </div>\n    </div>\n  )\n}\n```\n\nYou no longer need the `xIsNext` state declaration or the calls to `setXIsNext`. Now, there’s no\nchance for `xIsNext` to get out of sync with `currentMove`, even if you make a mistake while coding\nthe components.\n\n### Wrapping up\n\nCongratulations! You’ve created a tic-tac-toe game that:\n\n- Lets you play tic-tac-toe,\n- Indicates when a player has won the game or when is drawn,\n- Stores a game’s history as a game progresses,\n- Allows players to review a game’s history and see previous versions of a game’s board.\n\nNice work! We hope you now feel like you have a decent grasp of how React and Zustand works.\n"
  },
  {
    "path": "docs/learn/guides/updating-state.md",
    "content": "---\ntitle: Updating state\nnav: 4\n---\n\n## Flat updates\n\nUpdating state with Zustand is simple! Call the provided `set` function with\nthe new state, and it will be shallowly merged with the existing state in the\nstore. **Note** See next section for nested state.\n\n```tsx\nimport { create } from 'zustand'\n\ntype State = {\n  firstName: string\n  lastName: string\n}\n\ntype Action = {\n  updateFirstName: (firstName: State['firstName']) => void\n  updateLastName: (lastName: State['lastName']) => void\n}\n\n// Create your store, which includes both state and (optionally) actions\nconst usePersonStore = create<State & Action>((set) => ({\n  firstName: '',\n  lastName: '',\n  updateFirstName: (firstName) => set(() => ({ firstName: firstName })),\n  updateLastName: (lastName) => set(() => ({ lastName: lastName })),\n}))\n\n// In consuming app\nfunction App() {\n  // \"select\" the needed state and actions, in this case, the firstName value\n  // and the action updateFirstName\n  const firstName = usePersonStore((state) => state.firstName)\n  const updateFirstName = usePersonStore((state) => state.updateFirstName)\n\n  return (\n    <main>\n      <label>\n        First name\n        <input\n          // Update the \"firstName\" state\n          onChange={(e) => updateFirstName(e.currentTarget.value)}\n          value={firstName}\n        />\n      </label>\n\n      <p>\n        Hello, <strong>{firstName}!</strong>\n      </p>\n    </main>\n  )\n}\n```\n\n## Deeply nested object\n\nIf you have a deep state object like this:\n\n```ts\ntype State = {\n  deep: {\n    nested: {\n      obj: { count: number }\n    }\n  }\n}\n```\n\nUpdating nested state requires some effort to ensure the process is completed\nimmutably.\n\n### Normal approach\n\nSimilar to React or Redux, the normal approach is to copy each level of the\nstate object. This is done with the spread operator `...`, and by manually\nmerging that in with the new state values. Like so:\n\n```ts\n  normalInc: () =>\n    set((state) => ({\n      deep: {\n        ...state.deep,\n        nested: {\n          ...state.deep.nested,\n          obj: {\n            ...state.deep.nested.obj,\n            count: state.deep.nested.obj.count + 1\n          }\n        }\n      }\n    })),\n```\n\nThis is very long! Let's explore some alternatives that will make your life\neasier.\n\n### With Immer\n\nMany people use [Immer](https://github.com/immerjs/immer) to update nested\nvalues. Immer can be used anytime you need to update nested state such as in\nReact, Redux and of course, Zustand!\n\nYou can use Immer to shorten your state updates for deeply nested object. Let's\ntake a look at an example:\n\n```ts\n  immerInc: () =>\n    set(produce((state: State) => { ++state.deep.nested.obj.count })),\n```\n\nWhat a reduction! Please take note of the [gotchas listed here](../../reference/integrations/immer-middleware.md).\n\n### With optics-ts\n\nThere is another option with [optics-ts](https://github.com/akheron/optics-ts/):\n\n```ts\n  opticsInc: () =>\n    set(O.modify(O.optic<State>().path(\"deep.nested.obj.count\"))((c) => c + 1)),\n```\n\nUnlike Immer, optics-ts doesn't use proxies or mutation syntax.\n\n### With Ramda\n\nYou can also use [Ramda](https://ramdajs.com/):\n\n```ts\n  ramdaInc: () =>\n    set(R.modifyPath([\"deep\", \"nested\", \"obj\", \"count\"], (c) => c + 1)),\n```\n\nBoth ramda and optics-ts also work with types.\n\n### Demo\n\nhttps://stackblitz.com/edit/vitejs-vite-j6bjdygu\n"
  },
  {
    "path": "docs/learn/index.md",
    "content": "---\ntitle: Learn\ndescription: A guided path to understand Zustand fundamentals, common patterns, and when to reach for specific tools.\n---\n\n## Start here\n\nIf you are new to Zustand, begin here for installation, a high-level overview, and a hands-on tutorial.\n\n- [Introduction](./getting-started/introduction.md) — Install Zustand and create your first store.\n- [Comparison with other tools](./getting-started/comparison.md) — See how Zustand compares to Redux, Jotai, Recoil, and others.\n- [Tutorial: Tic Tac Toe](./guides/tutorial-tic-tac-toe.md) — Build a complete game to learn Zustand concepts step by step.\n\n## Core concepts\n\nThe fundamentals of reading and updating state in a Zustand store.\n\n- [Updating state](./guides/updating-state.md) — How to update primitive values, objects, and nested state.\n- [Practice with no store actions](./guides/practice-with-no-store-actions.md) — Define state updates outside the store for simpler patterns.\n- [Slices pattern](./guides/slices-pattern.md) — Split a large store into smaller, composable slices.\n- [Immutable state and merging](./guides/immutable-state-and-merging.md) — Understand how Zustand merges state and when to spread manually.\n- [Maps and sets usage](./guides/maps-and-sets-usage.md) — Work with `Map` and `Set` inside Zustand state correctly.\n\n## Performance and rendering\n\nTechniques for keeping re-renders minimal and components fast.\n\n- [Prevent rerenders with useShallow](./guides/prevent-rerenders-with-use-shallow.md) — Use shallow comparison to avoid unnecessary re-renders when selecting objects.\n- [Connect to state with URL hash](./guides/connect-to-state-with-url-hash.md) — Sync store state with the URL hash for shareable UI state.\n- [Event handler in pre React 18](./guides/event-handler-in-pre-react-18.md) — Handle the batching edge case in React 17 and earlier.\n\n## TypeScript path\n\nGuides for typing stores, actions, and selectors with TypeScript.\n\n- [Beginner TypeScript](./guides/beginner-typescript.md) — Type a basic store with state and actions.\n- [Advanced TypeScript](./guides/advanced-typescript.md) — Type slices, middleware stacks, and complex patterns.\n- [Auto-generating selectors](./guides/auto-generating-selectors.md) — Generate typed selectors automatically from a store definition.\n\n## Frameworks and platforms\n\nUsing Zustand in server-rendered and framework-specific environments.\n\n- [Next.js](./guides/nextjs.md) — Set up Zustand in a Next.js app with proper SSR handling.\n- [SSR and hydration](./guides/ssr-and-hydration.md) — Avoid hydration mismatches when rendering on the server.\n- [Initialize state with props](./guides/initialize-state-with-props.md) — Seed a store's initial state from React component props.\n\n## Testing and quality\n\nBest practices for writing reliable, maintainable code with Zustand.\n\n- [Testing stores and components](./guides/testing.md) — Test store logic and React components that consume a store.\n- [Flux-inspired practice](./guides/flux-inspired-practice.md) — Apply Flux conventions to keep state changes predictable.\n- [How to reset state](./guides/how-to-reset-state.md) — Reset a store back to its initial state on demand.\n"
  },
  {
    "path": "docs/reference/apis/create-store.md",
    "content": "---\ntitle: createStore\ndescription: How to create vanilla stores\nnav: 22\n---\n\n`createStore` lets you create a vanilla store that exposes API utilities.\n\n```js\nconst someStore = createStore(stateCreatorFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Updating state based on previous state](#updating-state-based-on-previous-state)\n  - [Updating Primitives in State](#updating-primitives-in-state)\n  - [Updating Objects in State](#updating-objects-in-state)\n  - [Updating Arrays in State](#updating-arrays-in-state)\n  - [Subscribing to state updates](#subscribing-to-state-updates)\n- [Troubleshooting](#troubleshooting)\n  - [I’ve updated the state, but the screen doesn’t update](#i’ve-updated-the-state,-but-the-screen-doesn’t-update)\n\n## Types\n\n### Signature\n\n```ts\ncreateStore<T>()(stateCreatorFn: StateCreator<T, [], []>): StoreApi<T>\n```\n\n## Reference\n\n### `createStore(stateCreatorFn)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n\n#### Returns\n\n`createStore` returns a vanilla store that exposes API utilities, `setState`, `getState`,\n`getInitialState` and `subscribe`.\n\n## Usage\n\n### Updating state based on previous state\n\nThis example shows how you can support **updater functions** within **actions**.\n\n```tsx\nimport { createStore } from 'zustand/vanilla'\n\ntype AgeStoreState = { age: number }\n\ntype AgeStoreActions = {\n  setAge: (\n    nextAge:\n      | AgeStoreState['age']\n      | ((currentAge: AgeStoreState['age']) => AgeStoreState['age']),\n  ) => void\n}\n\ntype AgeStore = AgeStoreState & AgeStoreActions\n\nconst ageStore = createStore<AgeStore>()((set) => ({\n  age: 42,\n  setAge: (nextAge) =>\n    set((state) => ({\n      age: typeof nextAge === 'function' ? nextAge(state.age) : nextAge,\n    })),\n}))\n\nfunction increment() {\n  ageStore.getState().setAge((currentAge) => currentAge + 1)\n}\n\nconst $yourAgeHeading = document.getElementById(\n  'your-age',\n) as HTMLHeadingElement\nconst $incrementBy3Button = document.getElementById(\n  'increment-by-3',\n) as HTMLButtonElement\nconst $incrementBy1Button = document.getElementById(\n  'increment-by-1',\n) as HTMLButtonElement\n\n$incrementBy3Button.addEventListener('click', () => {\n  increment()\n  increment()\n  increment()\n})\n\n$incrementBy1Button.addEventListener('click', () => {\n  increment()\n})\n\nconst render: Parameters<typeof ageStore.subscribe>[0] = (state) => {\n  $yourAgeHeading.innerHTML = `Your age: ${state.age}`\n}\n\nrender(ageStore.getInitialState(), ageStore.getInitialState())\n\nageStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<h1 id=\"your-age\"></h1>\n<button id=\"increment-by-3\" type=\"button\">+3</button>\n<button id=\"increment-by-1\" type=\"button\">+1</button>\n```\n\n### Updating Primitives in State\n\nState can hold any kind of JavaScript value. When you want to update built-in primitive values like\nnumbers, strings, booleans, etc. you should directly assign new values to ensure updates are applied\ncorrectly, and avoid unexpected behaviors.\n\n> [!NOTE]\n> By default, `set` function performs a shallow merge. If you need to completely replace\n> the state with a new one, use the `replace` parameter set to `true`\n\n```ts\nimport { createStore } from 'zustand/vanilla'\n\ntype XStore = number\n\nconst xStore = createStore<XStore>()(() => 0)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  xStore.setState(event.clientX, true)\n})\n\nconst render: Parameters<typeof xStore.subscribe>[0] = (x) => {\n  $dot.style.transform = `translate(${x}px, 0)`\n}\n\nrender(xStore.getInitialState(), xStore.getInitialState())\n\nxStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Updating Objects in State\n\nObjects are **mutable** in JavaScript, but you should treat them as **immutable** when you store\nthem in state. Instead, when you want to update an object, you need to create a new one (or make a\ncopy of an existing one), and then set the state to use the new object.\n\nBy default, `set` function performs a shallow merge. For most updates where you only need to modify\nspecific properties, the default shallow merge is preferred as it's more efficient. To completely\nreplace the state with a new one, use the `replace` parameter set to `true` with caution, as it\ndiscards any existing nested data within the state.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (position) => set({ position }),\n}))\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getInitialState(), positionStore.getInitialState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Updating Arrays in State\n\nArrays are mutable in JavaScript, but you should treat them as immutable when you store them in\nstate. Just like with objects, when you want to update an array stored in state, you need to create\na new one (or make a copy of an existing one), and then set state to use the new array.\n\nBy default, `set` function performs a shallow merge. To update array values we should assign new\nvalues to ensure updates are applied correctly, and avoid unexpected behaviors. To completely\nreplace the state with a new one, use the `replace` parameter set to `true`.\n\n> [!IMPORTANT]\n> We should prefer immutable operations like: `[...array]`, `concat(...)`, `filter(...)`,\n> `slice(...)`, `map(...)`, `toSpliced(...)`, `toSorted(...)`, and `toReversed(...)`, and avoid\n> mutable operations like `array[arrayIndex] = ...`, `push(...)`, `unshift(...)`, `pop(...)`,\n> `shift(...)`, `splice(...)`, `reverse(...)`, and `sort(...)`.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\n\ntype PositionStore = [number, number]\n\nconst positionStore = createStore<PositionStore>()(() => [0, 0])\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.setState([event.clientX, event.clientY], true)\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = ([x, y]) => {\n  $dot.style.transform = `translate(${x}px, ${y}px)`\n}\n\nrender(positionStore.getInitialState(), positionStore.getInitialState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Subscribing to state updates\n\nBy subscribing to state updates, you register a callback that fires whenever the store's state\nupdates. We can use `subscribe` for external state management.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (position) => set({ position }),\n}))\n\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dot.addEventListener('mouseenter', (event) => {\n  const parent = event.currentTarget.parentElement\n  const parentWidth = parent.clientWidth\n  const parentHeight = parent.clientHeight\n\n  positionStore.getState().setPosition({\n    x: Math.ceil(Math.random() * parentWidth),\n    y: Math.ceil(Math.random() * parentHeight),\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getInitialState(), positionStore.getInitialState())\n\npositionStore.subscribe(render)\n\nconst logger: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  console.log('new position', { position: state.position })\n}\n\npositionStore.subscribe(logger)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n## Troubleshooting\n\n### I’ve updated the state, but the screen doesn’t update\n\nIn the previous example, the `position` object is always created fresh from the current cursor\nposition. But often, you will want to include existing data as a part of the new object you’re\ncreating. For example, you may want to update only one field in a form, but keep the previous\nvalues for all other fields.\n\nThese input fields don’t work because the `oninput` handlers mutate the state:\n\n```ts\nimport { createStore } from 'zustand/vanilla'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (nextPerson: PersonStoreState['person']) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst personStore = createStore<PersonStore>()((set) => ({\n  person: {\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com',\n  },\n  setPerson: (person) => set({ person }),\n}))\n\nconst $firstNameInput = document.getElementById(\n  'first-name',\n) as HTMLInputElement\nconst $lastNameInput = document.getElementById('last-name') as HTMLInputElement\nconst $emailInput = document.getElementById('email') as HTMLInputElement\nconst $result = document.getElementById('result') as HTMLDivElement\n\nfunction handleFirstNameChange(event: Event) {\n  personStore.getState().person.firstName = (event.target as any).value\n}\n\nfunction handleLastNameChange(event: Event) {\n  personStore.getState().person.lastName = (event.target as any).value\n}\n\nfunction handleEmailChange(event: Event) {\n  personStore.getState().person.email = (event.target as any).value\n}\n\n$firstNameInput.addEventListener('input', handleFirstNameChange)\n$lastNameInput.addEventListener('input', handleLastNameChange)\n$emailInput.addEventListener('input', handleEmailChange)\n\nconst render: Parameters<typeof personStore.subscribe>[0] = (state) => {\n  $firstNameInput.value = state.person.firstName\n  $lastNameInput.value = state.person.lastName\n  $emailInput.value = state.person.email\n\n  $result.innerHTML = `${state.person.firstName} ${state.person.lastName} (${state.person.email})`\n}\n\nrender(personStore.getInitialState(), personStore.getInitialState())\n\npersonStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<label style=\"display: block\">\n  First name:\n  <input id=\"first-name\" />\n</label>\n<label style=\"display: block\">\n  Last name:\n  <input id=\"last-name\" />\n</label>\n<label style=\"display: block\">\n  Email:\n  <input id=\"email\" />\n</label>\n<p id=\"result\"></p>\n```\n\nFor example, this line mutates the state from a past render:\n\n```ts\npersonStore.getState().firstName = (e.target as any).value\n```\n\nThe reliable way to get the behavior you’re looking for is to create a new object and pass it to\n`setPerson`. But here you want to also copy the existing data into it because only one of the\nfields has changed:\n\n```ts\npersonStore.getState().setPerson({\n  firstName: e.target.value, // New first name from the input\n})\n```\n\n> [!NOTE]\n> We don’t need to copy every property separately due to `set` function performing shallow merge by\n> default.\n\nNow the form works!\n\nNotice how you didn’t declare a separate state variable for each input field. For large forms,\nkeeping all data grouped in an object is very convenient—as long as you update it correctly!\n\n```ts {32-34,38-40,44-46}\nimport { createStore } from 'zustand/vanilla'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (nextPerson: PersonStoreState['person']) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst personStore = createStore<PersonStore>()((set) => ({\n  person: {\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com',\n  },\n  setPerson: (person) => set({ person }),\n}))\n\nconst $firstNameInput = document.getElementById(\n  'first-name',\n) as HTMLInputElement\nconst $lastNameInput = document.getElementById('last-name') as HTMLInputElement\nconst $emailInput = document.getElementById('email') as HTMLInputElement\nconst $result = document.getElementById('result') as HTMLDivElement\n\nfunction handleFirstNameChange(event: Event) {\n  personStore.getState().setPerson({\n    ...personStore.getState().person,\n    firstName: (event.target as any).value,\n  })\n}\n\nfunction handleLastNameChange(event: Event) {\n  personStore.getState().setPerson({\n    ...personStore.getState().person,\n    lastName: (event.target as any).value,\n  })\n}\n\nfunction handleEmailChange(event: Event) {\n  personStore.getState().setPerson({\n    ...personStore.getState().person,\n    email: (event.target as any).value,\n  })\n}\n\n$firstNameInput.addEventListener('input', handleFirstNameChange)\n$lastNameInput.addEventListener('input', handleLastNameChange)\n$emailInput.addEventListener('input', handleEmailChange)\n\nconst render: Parameters<typeof personStore.subscribe>[0] = (state) => {\n  $firstNameInput.value = state.person.firstName\n  $lastNameInput.value = state.person.lastName\n  $emailInput.value = state.person.email\n\n  $result.innerHTML = `${state.person.firstName} ${state.person.lastName} (${state.person.email})`\n}\n\nrender(personStore.getInitialState(), personStore.getInitialState())\n\npersonStore.subscribe(render)\n```\n"
  },
  {
    "path": "docs/reference/apis/create-with-equality-fn.md",
    "content": "---\ntitle: createWithEqualityFn\ndescription: How to create efficient stores\ntag: react\nnav: 23\n---\n\n`createWithEqualityFn` lets you create a React Hook with API utilities attached, just like `create`.\nHowever, it offers a way to define a custom equality check. This allows for more granular control\nover when components re-render, improving performance and responsiveness.\n\n> [!IMPORTANT]\n> In order to use `createWithEqualityFn` from `zustand/traditional` you need to install\n> `use-sync-external-store` library due to `zustand/traditional` relies on `useSyncExternalStoreWithSelector`.\n\n```js\nconst useSomeStore = createWithEqualityFn(stateCreatorFn, equalityFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Updating state based on previous state](#updating-state-based-on-previous-state)\n  - [Updating Primitives in State](#updating-primitives-in-state)\n  - [Updating Objects in State](#updating-objects-in-state)\n  - [Updating Arrays in State](#updating-arrays-in-state)\n  - [Updating state with no store actions](#updating-state-with-no-store-actions)\n  - [Subscribing to state updates](#subscribing-to-state-updates)\n- [Troubleshooting](#troubleshooting)\n  - [I’ve updated the state, but the screen doesn’t update](#i’ve-updated-the-state,-but-the-screen-doesn’t-update)\n\n## Types\n\n### Signature\n\n```ts\ncreateWithEqualityFn<T>()(stateCreatorFn: StateCreator<T, [], []>, equalityFn?: (a: T, b: T) => boolean): UseBoundStore<StoreApi<T>>\n```\n\n## Reference\n\n### `createWithEqualityFn(stateCreatorFn)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n- **optional** `equalityFn`: Defaults to `Object.is`. A function that lets you skip re-renders.\n\n#### Returns\n\n`createWithEqualityFn` returns a React Hook with API utilities attached, just like `create`. It\nlets you return data that is based on current state, using a selector function, and lets you skip\nre-renders using an equality function. It should take a selector function, and an equality function\nas arguments.\n\n## Usage\n\n### Updating state based on previous state\n\nTo update a state based on previous state we should use **updater functions**. Read more\nabout that [here](https://react.dev/learn/queueing-a-series-of-state-updates).\n\nThis example shows how you can support **updater functions** within **actions**.\n\n```tsx\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype AgeStoreState = { age: number }\n\ntype AgeStoreActions = {\n  setAge: (\n    nextAge:\n      | AgeStoreState['age']\n      | ((currentAge: AgeStoreState['age']) => AgeStoreState['age']),\n  ) => void\n}\n\ntype AgeStore = AgeStoreState & AgeStoreActions\n\nconst useAgeStore = createWithEqualityFn<AgeStore>()(\n  (set) => ({\n    age: 42,\n    setAge: (nextAge) =>\n      set((state) => ({\n        age: typeof nextAge === 'function' ? nextAge(state.age) : nextAge,\n      })),\n  }),\n  shallow,\n)\n\nexport default function App() {\n  const age = useAgeStore((state) => state.age)\n  const setAge = useAgeStore((state) => state.setAge)\n\n  function increment() {\n    setAge((currentAge) => currentAge + 1)\n  }\n\n  return (\n    <>\n      <h1>Your age: {age}</h1>\n      <button\n        type=\"button\"\n        onClick={() => {\n          increment()\n          increment()\n          increment()\n        }}\n      >\n        +3\n      </button>\n      <button\n        type=\"button\"\n        onClick={() => {\n          increment()\n        }}\n      >\n        +1\n      </button>\n    </>\n  )\n}\n```\n\n### Updating Primitives in State\n\nState can hold any kind of JavaScript value. When you want to update built-in primitive values like\nnumbers, strings, booleans, etc. you should directly assign new values to ensure updates are applied\ncorrectly, and avoid unexpected behaviors.\n\n> [!NOTE]\n> By default, `set` function performs a shallow merge. If you need to completely replace\n> the state with a new one, use the `replace` parameter set to `true`\n\n```tsx\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype XStore = number\n\nconst useXStore = createWithEqualityFn<XStore>()(() => 0, shallow)\n\nexport default function MovingDot() {\n  const x = useXStore()\n  const setX = (nextX: number) => {\n    useXStore.setState(nextX, true)\n  }\n  const position = { y: 0, x }\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setX(e.clientX)\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Updating Objects in State\n\nObjects are **mutable** in JavaScript, but you should treat them as **immutable** when you store\nthem in state. Instead, when you want to update an object, you need to create a new one (or make a\ncopy of an existing one), and then set the state to use the new object.\n\nBy default, `set` function performs a shallow merge. For most updates where you only need to modify\nspecific properties, the default shallow merge is preferred as it's more efficient. To completely\nreplace the state with a new one, use the `replace` parameter set to `true` with caution, as it\ndiscards any existing nested data within the state.\n\n```tsx\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst usePositionStore = createWithEqualityFn<PositionStore>()(\n  (set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (position) => set({ position }),\n  }),\n  shallow,\n)\n\nexport default function MovingDot() {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Updating Arrays in State\n\nArrays are mutable in JavaScript, but you should treat them as immutable when you store them in\nstate. Just like with objects, when you want to update an array stored in state, you need to create\na new one (or make a copy of an existing one), and then set state to use the new array.\n\nBy default, `set` function performs a shallow merge. To update array values we should assign new\nvalues to ensure updates are applied correctly, and avoid unexpected behaviors. To completely\nreplace the state with a new one, use the `replace` parameter set to `true`.\n\n> [!IMPORTANT]\n> We should prefer immutable operations like: `[...array]`, `concat(...)`, `filter(...)`,\n> `slice(...)`, `map(...)`, `toSpliced(...)`, `toSorted(...)`, and `toReversed(...)`, and avoid\n> mutable operations like `array[arrayIndex] = ...`, `push(...)`, `unshift(...)`, `pop(...)`,\n> `shift(...)`, `splice(...)`, `reverse(...)`, and `sort(...)`.\n\n```tsx\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype PositionStore = [number, number]\n\nconst usePositionStore = createWithEqualityFn<PositionStore>()(\n  () => [0, 0],\n  shallow,\n)\n\nexport default function MovingDot() {\n  const [x, y] = usePositionStore()\n  const position = { x, y }\n  const setPosition: typeof usePositionStore.setState = (nextPosition) => {\n    usePositionStore.setState(nextPosition, true)\n  }\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition([e.clientX, e.clientY])\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Updating state with no store actions\n\nDefining actions at module level, external to the store have a few advantages like: it doesn't\nrequire a hook to call an action, and it facilitates code splitting.\n\n> [!NOTE]\n> The recommended way is to colocate actions and states within the store (let your actions be\n> located together with your state).\n\n```tsx\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\nconst usePositionStore = createWithEqualityFn<{\n  x: number\n  y: number\n}>()(() => ({ x: 0, y: 0 }), shallow)\n\nconst setPosition: typeof usePositionStore.setState = (nextPosition) => {\n  usePositionStore.setState(nextPosition)\n}\n\nexport default function MovingDot() {\n  const position = usePositionStore()\n\n  return (\n    <div\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n        onMouseEnter={(event) => {\n          const parent = event.currentTarget.parentElement\n          const parentWidth = parent.clientWidth\n          const parentHeight = parent.clientHeight\n\n          setPosition({\n            x: Math.ceil(Math.random() * parentWidth),\n            y: Math.ceil(Math.random() * parentHeight),\n          })\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Subscribing to state updates\n\nBy subscribing to state updates, you register a callback that fires whenever the store's state\nupdates. We can use `subscribe` for external state management.\n\n```tsx\nimport { useEffect } from 'react'\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst usePositionStore = createWithEqualityFn<PositionStore>()(\n  (set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (nextPosition) => set({ position: nextPosition }),\n  }),\n  shallow,\n)\n\nexport default function MovingDot() {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  useEffect(() => {\n    const unsubscribePositionStore = usePositionStore.subscribe(\n      ({ position }) => {\n        console.log('new position', { position })\n      },\n    )\n\n    return () => {\n      unsubscribePositionStore()\n    }\n  }, [])\n\n  return (\n    <div\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n        onMouseEnter={(event) => {\n          const parent = event.currentTarget.parentElement\n          const parentWidth = parent.clientWidth\n          const parentHeight = parent.clientHeight\n\n          setPosition({\n            x: Math.ceil(Math.random() * parentWidth),\n            y: Math.ceil(Math.random() * parentHeight),\n          })\n        }}\n      />\n    </div>\n  )\n}\n```\n\n## Troubleshooting\n\n### I’ve updated the state, but the screen doesn’t update\n\nIn the previous example, the `position` object is always created fresh from the current cursor\nposition. But often, you will want to include existing data as a part of the new object you’re\ncreating. For example, you may want to update only one field in a form, but keep the previous\nvalues for all other fields.\n\nThese input fields don’t work because the `onChange` handlers mutate the state:\n\n```tsx\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (nextPerson: PersonStoreState['person']) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst usePersonStore = createWithEqualityFn<PersonStore>()(\n  (set) => ({\n    person: {\n      firstName: 'Barbara',\n      lastName: 'Hepworth',\n      email: 'bhepworth@sculpture.com',\n    },\n    setPerson: (person) => set({ person }),\n  }),\n  shallow,\n)\n\nexport default function Form() {\n  const person = usePersonStore((state) => state.person)\n  const setPerson = usePersonStore((state) => state.setPerson)\n\n  function handleFirstNameChange(e: ChangeEvent<HTMLInputElement>) {\n    person.firstName = e.target.value\n  }\n\n  function handleLastNameChange(e: ChangeEvent<HTMLInputElement>) {\n    person.lastName = e.target.value\n  }\n\n  function handleEmailChange(e: ChangeEvent<HTMLInputElement>) {\n    person.email = e.target.value\n  }\n\n  return (\n    <>\n      <label style={{ display: 'block' }}>\n        First name:\n        <input value={person.firstName} onChange={handleFirstNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Last name:\n        <input value={person.lastName} onChange={handleLastNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Email:\n        <input value={person.email} onChange={handleEmailChange} />\n      </label>\n      <p>\n        {person.firstName} {person.lastName} ({person.email})\n      </p>\n    </>\n  )\n}\n```\n\nFor example, this line mutates the state from a past render:\n\n```tsx\nperson.firstName = e.target.value\n```\n\nThe reliable way to get the behavior you’re looking for is to create a new object and pass it to\n`setPerson`. But here you want to also copy the existing data into it because only one of the\nfields has changed:\n\n```ts\nsetPerson({ ...person, firstName: e.target.value }) // New first name from the input\n```\n\n> [!NOTE]\n> We don’t need to copy every property separately due to `set` function performing shallow merge by\n> default.\n\nNow the form works!\n\nNotice how you didn’t declare a separate state variable for each input field. For large forms,\nkeeping all data grouped in an object is very convenient—as long as you update it correctly!\n\n```tsx {32,36,40}\nimport { type ChangeEvent } from 'react'\nimport { createWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/vanilla/shallow'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (nextPerson: PersonStoreState['person']) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst usePersonStore = createWithEqualityFn<PersonStore>()(\n  (set) => ({\n    person: {\n      firstName: 'Barbara',\n      lastName: 'Hepworth',\n      email: 'bhepworth@sculpture.com',\n    },\n    setPerson: (nextPerson) => set({ person: nextPerson }),\n  }),\n  shallow,\n)\n\nexport default function Form() {\n  const person = usePersonStore((state) => state.person)\n  const setPerson = usePersonStore((state) => state.setPerson)\n\n  function handleFirstNameChange(e: ChangeEvent<HTMLInputElement>) {\n    setPerson({ ...person, firstName: e.target.value })\n  }\n\n  function handleLastNameChange(e: ChangeEvent<HTMLInputElement>) {\n    setPerson({ ...person, lastName: e.target.value })\n  }\n\n  function handleEmailChange(e: ChangeEvent<HTMLInputElement>) {\n    setPerson({ ...person, email: e.target.value })\n  }\n\n  return (\n    <>\n      <label style={{ display: 'block' }}>\n        First name:\n        <input value={person.firstName} onChange={handleFirstNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Last name:\n        <input value={person.lastName} onChange={handleLastNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Email:\n        <input value={person.email} onChange={handleEmailChange} />\n      </label>\n      <p>\n        {person.firstName} {person.lastName} ({person.email})\n      </p>\n    </>\n  )\n}\n```\n"
  },
  {
    "path": "docs/reference/apis/create.md",
    "content": "---\ntitle: create\ndescription: How to create stores\ntag: react\nnav: 21\n---\n\n`create` lets you create a React Hook with API utilities attached.\n\n```js\nconst useSomeStore = create(stateCreatorFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Updating state based on previous state](#updating-state-based-on-previous-state)\n  - [Updating Primitives in State](#updating-primitives-in-state)\n  - [Updating Objects in State](#updating-objects-in-state)\n  - [Updating Arrays in State](#updating-arrays-in-state)\n  - [Updating state with no store actions](#updating-state-with-no-store-actions)\n  - [Subscribing to state updates](#subscribing-to-state-updates)\n- [Troubleshooting](#troubleshooting)\n  - [I’ve updated the state, but the screen doesn’t update](#i’ve-updated-the-state,-but-the-screen-doesn’t-update)\n\n## Types\n\n### Signature\n\n```ts\ncreate<T>()(stateCreatorFn: StateCreator<T, [], []>): UseBoundStore<StoreApi<T>>\n```\n\n## Reference\n\n### `create(stateCreatorFn)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n\n#### Returns\n\n`create` returns a React Hook with API utilities, `setState`, `getState`, `getInitialState` and\n`subscribe`, attached. It lets you return data that is based on current state, using a selector\nfunction. It should take a selector function as its only argument.\n\n## Usage\n\n### Updating state based on previous state\n\nTo update a state based on previous state we should use **updater functions**. Read more\nabout that [here](https://react.dev/learn/queueing-a-series-of-state-updates).\n\nThis example shows how you can support **updater functions** within **actions**.\n\n```tsx\nimport { create } from 'zustand'\n\ntype AgeStoreState = { age: number }\n\ntype AgeStoreActions = {\n  setAge: (\n    nextAge:\n      | AgeStoreState['age']\n      | ((currentAge: AgeStoreState['age']) => AgeStoreState['age']),\n  ) => void\n}\n\ntype AgeStore = AgeStoreState & AgeStoreActions\n\nconst useAgeStore = create<AgeStore>()((set) => ({\n  age: 42,\n  setAge: (nextAge) => {\n    set((state) => ({\n      age: typeof nextAge === 'function' ? nextAge(state.age) : nextAge,\n    }))\n  },\n}))\n\nexport default function App() {\n  const age = useAgeStore((state) => state.age)\n  const setAge = useAgeStore((state) => state.setAge)\n\n  function increment() {\n    setAge((currentAge) => currentAge + 1)\n  }\n\n  return (\n    <>\n      <h1>Your age: {age}</h1>\n      <button\n        onClick={() => {\n          increment()\n          increment()\n          increment()\n        }}\n      >\n        +3\n      </button>\n      <button\n        onClick={() => {\n          increment()\n        }}\n      >\n        +1\n      </button>\n    </>\n  )\n}\n```\n\n### Updating Primitives in State\n\nState can hold any kind of JavaScript value. When you want to update built-in primitive values like\nnumbers, strings, booleans, etc. you should directly assign new values to ensure updates are applied\ncorrectly, and avoid unexpected behaviors.\n\n> [!NOTE]\n> By default, `set` function performs a shallow merge. If you need to completely replace the state\n> with a new one, use the `replace` parameter set to `true`\n\n```tsx\nimport { create } from 'zustand'\n\ntype XStore = number\n\nconst useXStore = create<XStore>()(() => 0)\n\nexport default function MovingDot() {\n  const x = useXStore()\n  const setX = (nextX: number) => {\n    useXStore.setState(nextX, true)\n  }\n  const position = { y: 0, x }\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setX(e.clientX)\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Updating Objects in State\n\nObjects are **mutable** in JavaScript, but you should treat them as **immutable** when you store\nthem in state. Instead, when you want to update an object, you need to create a new one (or make a\ncopy of an existing one), and then set the state to use the new object.\n\nBy default, `set` function performs a shallow merge. For most updates where you only need to modify\nspecific properties, the default shallow merge is preferred as it's more efficient. To completely\nreplace the state with a new one, use the `replace` parameter set to `true` with caution, as it\ndiscards any existing nested data within the state.\n\n```tsx\nimport { create } from 'zustand'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst usePositionStore = create<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (nextPosition) => set({ position: nextPosition }),\n}))\n\nexport default function MovingDot() {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Updating Arrays in State\n\nArrays are mutable in JavaScript, but you should treat them as immutable when you store them in\nstate. Just like with objects, when you want to update an array stored in state, you need to create\na new one (or make a copy of an existing one), and then set state to use the new array.\n\nBy default, `set` function performs a shallow merge. To update array values we should assign new\nvalues to ensure updates are applied correctly, and avoid unexpected behaviors. To completely\nreplace the state with a new one, use the `replace` parameter set to `true`.\n\n> [!IMPORTANT]\n> We should prefer immutable operations like: `[...array]`, `concat(...)`, `filter(...)`,\n> `slice(...)`, `map(...)`, `toSpliced(...)`, `toSorted(...)`, and `toReversed(...)`, and avoid\n> mutable operations like `array[arrayIndex] = ...`, `push(...)`, `unshift(...)`, `pop(...)`,\n> `shift(...)`, `splice(...)`, `reverse(...)`, and `sort(...)`.\n\n```tsx\nimport { create } from 'zustand'\n\ntype PositionStore = [number, number]\n\nconst usePositionStore = create<PositionStore>()(() => [0, 0])\n\nexport default function MovingDot() {\n  const [x, y] = usePositionStore()\n  const setPosition: typeof usePositionStore.setState = (nextPosition) => {\n    usePositionStore.setState(nextPosition, true)\n  }\n  const position = { x, y }\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition([e.clientX, e.clientY])\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Updating state with no store actions\n\nDefining actions at module level, external to the store have a few advantages like: it doesn't\nrequire a hook to call an action, and it facilitates code splitting.\n\n> [!NOTE]\n> The recommended way is to colocate actions and states within the store (let your actions be\n> located together with your state).\n\n```tsx\nimport { create } from 'zustand'\n\nconst usePositionStore = create<{\n  x: number\n  y: number\n}>()(() => ({ x: 0, y: 0 }))\n\nconst setPosition: typeof usePositionStore.setState = (nextPosition) => {\n  usePositionStore.setState(nextPosition)\n}\n\nexport default function MovingDot() {\n  const position = usePositionStore()\n\n  return (\n    <div\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n        onMouseEnter={(event) => {\n          const parent = event.currentTarget.parentElement\n          const parentWidth = parent.clientWidth\n          const parentHeight = parent.clientHeight\n\n          setPosition({\n            x: Math.ceil(Math.random() * parentWidth),\n            y: Math.ceil(Math.random() * parentHeight),\n          })\n        }}\n      />\n    </div>\n  )\n}\n```\n\n### Subscribing to state updates\n\nBy subscribing to state updates, you register a callback that fires whenever the store's state\nupdates. We can use `subscribe` for external state management.\n\n```tsx\nimport { useEffect } from 'react'\nimport { create } from 'zustand'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst usePositionStore = create<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (nextPosition) => set({ position: nextPosition }),\n}))\n\nexport default function MovingDot() {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  useEffect(() => {\n    const unsubscribePositionStore = usePositionStore.subscribe(\n      ({ position }) => {\n        console.log('new position', { position })\n      },\n    )\n\n    return () => {\n      unsubscribePositionStore()\n    }\n  }, [])\n\n  return (\n    <div\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n        onMouseEnter={(event) => {\n          const parent = event.currentTarget.parentElement\n          const parentWidth = parent.clientWidth\n          const parentHeight = parent.clientHeight\n\n          setPosition({\n            x: Math.ceil(Math.random() * parentWidth),\n            y: Math.ceil(Math.random() * parentHeight),\n          })\n        }}\n      />\n    </div>\n  )\n}\n```\n\n## Troubleshooting\n\n### I’ve updated the state, but the screen doesn’t update\n\nIn the previous example, the `position` object is always created fresh from the current cursor\nposition. But often, you will want to include existing data as a part of the new object you’re\ncreating. For example, you may want to update only one field in a form, but keep the previous\nvalues for all other fields.\n\nThese input fields don’t work because the `onChange` handlers mutate the state:\n\n```tsx\nimport { create } from 'zustand'\n\ntype PersonStoreState = {\n  firstName: string\n  lastName: string\n  email: string\n}\n\ntype PersonStoreActions = {\n  setPerson: (nextPerson: Partial<PersonStoreState>) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst usePersonStore = create<PersonStore>()((set) => ({\n  firstName: 'Barbara',\n  lastName: 'Hepworth',\n  email: 'bhepworth@sculpture.com',\n  setPerson: (nextPerson) => set(nextPerson),\n}))\n\nexport default function Form() {\n  const person = usePersonStore((state) => state)\n  const setPerson = usePersonStore((state) => state.setPerson)\n\n  function handleFirstNameChange(e: ChangeEvent<HTMLInputElement>) {\n    person.firstName = e.target.value\n  }\n\n  function handleLastNameChange(e: ChangeEvent<HTMLInputElement>) {\n    person.lastName = e.target.value\n  }\n\n  function handleEmailChange(e: ChangeEvent<HTMLInputElement>) {\n    person.email = e.target.value\n  }\n\n  return (\n    <>\n      <label style={{ display: 'block' }}>\n        First name:\n        <input value={person.firstName} onChange={handleFirstNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Last name:\n        <input value={person.lastName} onChange={handleLastNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Email:\n        <input value={person.email} onChange={handleEmailChange} />\n      </label>\n      <p>\n        {person.firstName} {person.lastName} ({person.email})\n      </p>\n    </>\n  )\n}\n```\n\nFor example, this line mutates the state from a past render:\n\n```tsx\nperson.firstName = e.target.value\n```\n\nThe reliable way to get the behavior you’re looking for is to create a new object and pass it to\n`setPerson`. But here you want to also copy the existing data into it because only one of the\nfields has changed:\n\n```ts\nsetPerson({ ...person, firstName: e.target.value }) // New first name from the input\n```\n\n> [!NOTE]\n> We don’t need to copy every property separately due to `set` function performing shallow merge by\n> default.\n\nNow the form works!\n\nNotice how you didn’t declare a separate state variable for each input field. For large forms,\nkeeping all data grouped in an object is very convenient—as long as you update it correctly!\n\n```tsx {27,31,35}\nimport { create } from 'zustand'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (nextPerson: PersonStoreState['person']) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst usePersonStore = create<PersonStore>()((set) => ({\n  person: {\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com',\n  },\n  setPerson: (nextPerson) => set(nextPerson),\n}))\n\nexport default function Form() {\n  const person = usePersonStore((state) => state.person)\n  const setPerson = usePersonStore((state) => state.setPerson)\n\n  function handleFirstNameChange(e: ChangeEvent<HTMLInputElement>) {\n    setPerson({ ...person, firstName: e.target.value })\n  }\n\n  function handleLastNameChange(e: ChangeEvent<HTMLInputElement>) {\n    setPerson({ ...person, lastName: e.target.value })\n  }\n\n  function handleEmailChange(e: ChangeEvent<HTMLInputElement>) {\n    setPerson({ ...person, email: e.target.value })\n  }\n\n  return (\n    <>\n      <label style={{ display: 'block' }}>\n        First name:\n        <input value={person.firstName} onChange={handleFirstNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Last name:\n        <input value={person.lastName} onChange={handleLastNameChange} />\n      </label>\n      <label style={{ display: 'block' }}>\n        Email:\n        <input value={person.email} onChange={handleEmailChange} />\n      </label>\n      <p>\n        {person.firstName} {person.lastName} ({person.email})\n      </p>\n    </>\n  )\n}\n```\n"
  },
  {
    "path": "docs/reference/apis/shallow.md",
    "content": "---\ntitle: shallow\ndescription: How compare simple data effectively\nnav: 24\n---\n\n`shallow` lets you run fast checks on simple data structures. It effectively identifies changes in\n**top-level** properties when you're working with data structures that don't have nested objects or\narrays within them.\n\n> [!NOTE]\n> Shallow lets you perform quick comparisons, but keep its limitations in mind.\n\n```js\nconst equal = shallow(a, b)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Comparing Primitives](#comparing-primitives)\n  - [Comparing Objects](#comparing-objects)\n  - [Comparing Sets](#comparing-sets)\n  - [Comparing Maps](#comparing-maps)\n- [Troubleshooting](#troubleshooting)\n  - [Comparing objects returns `false` even if they are identical](#comparing-objects-returns-false-even-if-they-are-identical)\n  - [Comparing objects with different prototypes](#comparing-objects-with-different-prototypes)\n\n## Types\n\n### Signature\n\n```ts\nshallow<T>(a: T, b: T): boolean\n```\n\n## Reference\n\n### `shallow(a, b)`\n\n#### Parameters\n\n- `a`: The first value.\n- `b`: The second value.\n\n#### Returns\n\n`shallow` returns `true` when `a` and `b` are equal based on a shallow comparison of their\n**top-level** properties. Otherwise, it should return `false`.\n\n## Usage\n\n### Comparing Primitives\n\nWhen comparing primitive values like `string`s, `number`s, `boolean`s, and `BigInt`s, both\n`Object.is` and `shallow` function return `true` if the values are the same. This is because\nprimitive values are compared by their actual value rather than by reference.\n\n```ts\nconst stringLeft = 'John Doe'\nconst stringRight = 'John Doe'\n\nObject.is(stringLeft, stringRight) // -> true\nshallow(stringLeft, stringRight) // -> true\n\nconst numberLeft = 10\nconst numberRight = 10\n\nObject.is(numberLeft, numberRight) // -> true\nshallow(numberLeft, numberRight) // -> true\n\nconst booleanLeft = true\nconst booleanRight = true\n\nObject.is(booleanLeft, booleanRight) // -> true\nshallow(booleanLeft, booleanRight) // -> true\n\nconst bigIntLeft = 1n\nconst bigIntRight = 1n\n\nObject.is(bigIntLeft, bigIntRight) // -> true\nshallow(bigIntLeft, bigIntRight) // -> true\n```\n\n### Comparing Objects\n\nWhen comparing objects, it's important to understand how `Object.is` and `shallow` function\noperate, as they handle comparisons differently.\n\nThe `shallow` function returns `true` because shallow performs a shallow comparison of the objects.\nIt checks if the top-level properties and their values are the same. In this case, the top-level\nproperties (`firstName`, `lastName`, and `age`) and their values are identical between `objectLeft`\nand `objectRight`, so shallow considers them equal.\n\n```ts\nconst objectLeft = {\n  firstName: 'John',\n  lastName: 'Doe',\n  age: 30,\n}\nconst objectRight = {\n  firstName: 'John',\n  lastName: 'Doe',\n  age: 30,\n}\n\nObject.is(objectLeft, objectRight) // -> false\nshallow(objectLeft, objectRight) // -> true\n```\n\n### Comparing Sets\n\nWhen comparing sets, it's important to understand how `Object.is` and `shallow` function operate,\nas they handle comparisons differently.\n\nThe `shallow` function returns `true` because shallow performs a shallow comparison of the sets. It\nchecks if the top-level properties (in this case, the sets themselves) are the same. Since `setLeft`\nand `setRight` are both instances of the Set object and contain the same elements, shallow considers\nthem equal.\n\n```ts\nconst setLeft = new Set([1, 2, 3])\nconst setRight = new Set([1, 2, 3])\n\nObject.is(setLeft, setRight) // -> false\nshallow(setLeft, setRight) // -> true\n```\n\n### Comparing Maps\n\nWhen comparing maps, it's important to understand how `Object.is` and `shallow` function operate, as\nthey handle comparisons differently.\n\nThe `shallow` returns `true` because shallow performs a shallow comparison of the maps. It checks if\nthe top-level properties (in this case, the maps themselves) are the same. Since `mapLeft` and\n`mapRight` are both instances of the Map object and contain the same key-value pairs, shallow\nconsiders them equal.\n\n```ts\nconst mapLeft = new Map([\n  [1, 'one'],\n  [2, 'two'],\n  [3, 'three'],\n])\nconst mapRight = new Map([\n  [1, 'one'],\n  [2, 'two'],\n  [3, 'three'],\n])\n\nObject.is(mapLeft, mapRight) // -> false\nshallow(mapLeft, mapRight) // -> true\n```\n\n## Troubleshooting\n\n### Comparing objects returns `false` even if they are identical\n\nThe `shallow` function performs a shallow comparison. A shallow comparison checks if the top-level\nproperties of two objects are equal. It does not check nested objects or deeply nested properties.\nIn other words, it only compares the references of the properties.\n\nIn the following example, the shallow function returns `false` because it compares only the\ntop-level properties and their references. The address property in both objects is a nested object,\nand even though their contents are identical, their references are different. Consequently, shallow\nsees them as different, resulting in `false`.\n\n```ts\nconst objectLeft = {\n  firstName: 'John',\n  lastName: 'Doe',\n  age: 30,\n  address: {\n    street: 'Kulas Light',\n    suite: 'Apt. 556',\n    city: 'Gwenborough',\n    zipcode: '92998-3874',\n    geo: {\n      lat: '-37.3159',\n      lng: '81.1496',\n    },\n  },\n}\nconst objectRight = {\n  firstName: 'John',\n  lastName: 'Doe',\n  age: 30,\n  address: {\n    street: 'Kulas Light',\n    suite: 'Apt. 556',\n    city: 'Gwenborough',\n    zipcode: '92998-3874',\n    geo: {\n      lat: '-37.3159',\n      lng: '81.1496',\n    },\n  },\n}\n\nObject.is(objectLeft, objectRight) // -> false\nshallow(objectLeft, objectRight) // -> false\n```\n\nIf we remove the `address` property, the shallow comparison would work as expected because all\ntop-level properties would be primitive values or references to the same values:\n\n```ts\nconst objectLeft = {\n  firstName: 'John',\n  lastName: 'Doe',\n  age: 30,\n}\nconst objectRight = {\n  firstName: 'John',\n  lastName: 'Doe',\n  age: 30,\n}\n\nObject.is(objectLeft, objectRight) // -> false\nshallow(objectLeft, objectRight) // -> true\n```\n\nIn this modified example, `objectLeft` and `objectRight` have the same top-level properties and\nprimitive values. Since `shallow` function only compares the top-level properties, it will return\n`true` because the primitive values (`firstName`, `lastName`, and `age`) are identical in both\nobjects.\n\n### Comparing objects with different prototypes\n\nThe `shallow` function checks whether the two objects have the same prototype. If their prototypes\nare referentially different, shallow will return `false`. This comparison is done using:\n\n```ts\nObject.getPrototypeOf(a) === Object.getPrototypeOf(b)\n```\n\n> [!IMPORTANT]\n> Objects created with the object initializer (`{}`) or with `new Object()` inherit from\n> `Object.prototype` by default. However, objects created with `Object.create(proto)` inherit from\n> the proto you pass in—which may not be `Object.prototype.`\n\n```ts\nconst a = Object.create({}) // -> prototype is `{}`\nconst b = {} // -> prototype is `Object.prototype`\n\nshallow(a, b) // -> false\n```\n"
  },
  {
    "path": "docs/reference/hooks/use-shallow.md",
    "content": "---\ntitle: useShallow\ndescription: How to memoize selector functions\ntag: react\nnav: 27\n---\n\n`useShallow` is a React Hook that lets you optimize re-renders.\n\n```js\nconst memoizedSelector = useShallow(selector)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Writing a memoized selector](#writing-a-memoized-selector)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\nuseShallow<T, U = T>(selectorFn: (state: T) => U): (state: T) => U\n```\n\n## Reference\n\n### `useShallow(selectorFn)`\n\n#### Parameters\n\n- `selectorFn`: A function that lets you return data that is based on current state.\n\n#### Returns\n\n`useShallow` returns a memoized version of a selector function using a shallow comparison for\nmemoization.\n\n## Usage\n\n### Writing a memoized selector\n\nFirst, we need to setup a store to hold the state for the bear family. In this store, we define\nthree properties: `papaBear`, `mamaBear`, and `babyBear`, each representing a different member of\nthe bear family and their respective oatmeal pot sizes.\n\n```tsx\nimport { create } from 'zustand'\n\ntype BearFamilyMealsStore = {\n  [key: string]: string\n}\n\nconst useBearFamilyMealsStore = create<BearFamilyMealsStore>()(() => ({\n  papaBear: 'large porridge-pot',\n  mamaBear: 'middle-size porridge pot',\n  babyBear: 'A little, small, wee pot',\n}))\n```\n\nNext, we'll create a `BearNames` component that retrieves the keys of our state (the bear family\nmembers) and displays them.\n\n```tsx\nfunction BearNames() {\n  const names = useBearFamilyMealsStore((state) => Object.keys(state))\n\n  return <div>{names.join(', ')}</div>\n}\n```\n\nNext, we will create a `UpdateBabyBearMeal` component that periodically updates baby bear's meal\nchoice.\n\n```tsx\nconst meals = [\n  'A tiny, little, wee bowl',\n  'A small, petite, tiny pot',\n  'A wee, itty-bitty, small bowl',\n  'A little, petite, tiny dish',\n  'A tiny, small, wee vessel',\n  'A small, little, wee cauldron',\n  'A little, tiny, small cup',\n  'A wee, small, little jar',\n  'A tiny, wee, small pan',\n  'A small, wee, little crock',\n]\n\nfunction UpdateBabyBearMeal() {\n  useEffect(() => {\n    const timer = setInterval(() => {\n      useBearFamilyMealsStore.setState({\n        babyBear: meals[Math.floor(Math.random() * (meals.length - 1))],\n      })\n    }, 1000)\n\n    return () => {\n      clearInterval(timer)\n    }\n  }, [])\n\n  return null\n}\n```\n\nFinally, we combine both components in the `App` component to see them in action.\n\n```tsx\nexport default function App() {\n  return (\n    <>\n      <UpdateBabyBearMeal />\n      <BearNames />\n    </>\n  )\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { useEffect } from 'react'\nimport { create } from 'zustand'\n\ntype BearFamilyMealsStore = {\n  [key: string]: string\n}\n\nconst useBearFamilyMealsStore = create<BearFamilyMealsStore>()(() => ({\n  papaBear: 'large porridge-pot',\n  mamaBear: 'middle-size porridge pot',\n  babyBear: 'A little, small, wee pot',\n}))\n\nconst meals = [\n  'A tiny, little, wee bowl',\n  'A small, petite, tiny pot',\n  'A wee, itty-bitty, small bowl',\n  'A little, petite, tiny dish',\n  'A tiny, small, wee vessel',\n  'A small, little, wee cauldron',\n  'A little, tiny, small cup',\n  'A wee, small, little jar',\n  'A tiny, wee, small pan',\n  'A small, wee, little crock',\n]\n\nfunction UpdateBabyBearMeal() {\n  useEffect(() => {\n    const timer = setInterval(() => {\n      useBearFamilyMealsStore.setState({\n        babyBear: meals[Math.floor(Math.random() * (meals.length - 1))],\n      })\n    }, 1000)\n\n    return () => {\n      clearInterval(timer)\n    }\n  }, [])\n\n  return null\n}\n\nfunction BearNames() {\n  const names = useBearFamilyMealsStore((state) => Object.keys(state))\n\n  return <div>{names.join(', ')}</div>\n}\n\nexport default function App() {\n  return (\n    <>\n      <UpdateBabyBearMeal />\n      <BearNames />\n    </>\n  )\n}\n```\n\nEverything might look fine, but there’s a small problem: the `BearNames` component keeps\nre-rendering even if the names haven’t changed. This happens because the component re-renders\nwhenever any part of the state changes, even if the specific part we care about (the list of names) hasn’t changed.\n\nTo fix this, we use `useShallow` to make sure the component only re-renders when the actual keys of\nthe state change:\n\n```tsx\nfunction BearNames() {\n  const names = useBearFamilyMealsStore(\n    useShallow((state) => Object.keys(state)),\n  )\n\n  return <div>{names.join(', ')}</div>\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { useEffect } from 'react'\nimport { create } from 'zustand'\nimport { useShallow } from 'zustand/react/shallow'\n\ntype BearFamilyMealsStore = {\n  [key: string]: string\n}\n\nconst useBearFamilyMealsStore = create<BearFamilyMealsStore>()(() => ({\n  papaBear: 'large porridge-pot',\n  mamaBear: 'middle-size porridge pot',\n  babyBear: 'A little, small, wee pot',\n}))\n\nconst meals = [\n  'A tiny, little, wee bowl',\n  'A small, petite, tiny pot',\n  'A wee, itty-bitty, small bowl',\n  'A little, petite, tiny dish',\n  'A tiny, small, wee vessel',\n  'A small, little, wee cauldron',\n  'A little, tiny, small cup',\n  'A wee, small, little jar',\n  'A tiny, wee, small pan',\n  'A small, wee, little crock',\n]\n\nfunction UpdateBabyBearMeal() {\n  useEffect(() => {\n    const timer = setInterval(() => {\n      useBearFamilyMealsStore.setState({\n        babyBear: meals[Math.floor(Math.random() * (meals.length - 1))],\n      })\n    }, 1000)\n\n    return () => {\n      clearInterval(timer)\n    }\n  }, [])\n\n  return null\n}\n\nfunction BearNames() {\n  const names = useBearFamilyMealsStore(\n    useShallow((state) => Object.keys(state)),\n  )\n\n  return <div>{names.join(', ')}</div>\n}\n\nexport default function App() {\n  return (\n    <>\n      <UpdateBabyBearMeal />\n      <BearNames />\n    </>\n  )\n}\n```\n\nBy using `useShallow`, we optimized the rendering process, ensuring that the component only\nre-renders when necessary, which improves overall performance.\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/hooks/use-store-with-equality-fn.md",
    "content": "---\ntitle: useStoreWithEqualityFn\ndescription: How to use vanilla stores effectively in React\ntag: react\nnav: 26\n---\n\n`useStoreWithEqualityFn` is a React Hook that lets you use a vanilla store in React, just like\n`useStore`. However, it offers a way to define a custom equality check. This allows for more\ngranular control over when components re-render, improving performance and responsiveness.\n\n> [!IMPORTANT]\n> In order to use `useStoreWithEqualityFn` from `zustand/traditional` you need to install\n> `use-sync-external-store` library due to `zustand/traditional` relies on `useSyncExternalStoreWithSelector`.\n\n```js\nconst someState = useStoreWithEqualityFn(store, selectorFn, equalityFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Using a global vanilla store in React](#using-a-global-vanilla-store-in-react)\n  - [Using dynamic vanilla stores in React](#using-dynamic-global-vanilla-stores-in-react)\n  - [Using scoped (non-global) vanilla store in React](<#using-scoped-(non-global)-vanilla-store-in-react>)\n  - [Using dynamic scoped (non-global) vanilla stores in React](<#using-dynamic-scoped-(non-global)-vanilla-stores-in-react>)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\nuseStoreWithEqualityFn<T, U = T>(store: StoreApi<T>, selectorFn: (state: T) => U, equalityFn?: (a: T, b: T) => boolean): U\n```\n\n## Reference\n\n### `useStoreWithEqualityFn(store, selectorFn, equalityFn)`\n\n#### Parameters\n\n- `storeApi`: The instance that lets you access to store API utilities.\n- `selectorFn`: A function that lets you return data that is based on current state.\n- `equalityFn`: A function that lets you skip re-renders.\n\n#### Returns\n\n`useStoreWithEqualityFn` returns any data based on current state depending on the selector function,\nand lets you skip re-renders using an equality function. It should take a store, a selector\nfunction, and an equality function as arguments.\n\n## Usage\n\n### Using a global vanilla store in React\n\nFirst, let's set up a store that will hold the position of the dot on the screen. We'll define the\nstore to manage `x` and `y` coordinates and provide an action to update these coordinates.\n\n```tsx\nimport { createStore, useStore } from 'zustand'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (position) => set({ position }),\n}))\n```\n\nNext, we'll create a `MovingDot` component that renders a div representing the dot. This component\nwill use the store to track and update the dot's position.\n\n```tsx\nfunction MovingDot() {\n  const position = useStoreWithEqualityFn(\n    positionStore,\n    (state) => state.position,\n    shallow,\n  )\n  const setPosition = useStoreWithEqualityFn(\n    positionStore,\n    (state) => state.setPosition,\n    shallow,\n  )\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\nFinally, we’ll render the `MovingDot` component in our `App` component.\n\n```tsx\nexport default function App() {\n  return <MovingDot />\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { createStore } from 'zustand'\nimport { useStoreWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/shallow'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (position) => set({ position }),\n}))\n\nfunction MovingDot() {\n  const position = useStoreWithEqualityFn(\n    positionStore,\n    (state) => state.position,\n    shallow,\n  )\n  const setPosition = useStoreWithEqualityFn(\n    positionStore,\n    (state) => state.setPosition,\n    shallow,\n  )\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n\nexport default function App() {\n  return <MovingDot />\n}\n```\n\n### Using dynamic global vanilla stores in React\n\nFirst, we'll create a factory function that generates a store for managing the counter state.\nEach tab will have its own instance of this store.\n\n```ts\nimport { createStore } from 'zustand'\n\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n```\n\nNext, we'll create a factory function that manages the creation and retrieval of counter stores.\nThis allows each tab to have its own independent counter.\n\n```ts\nconst defaultCounterStores = new Map<\n  string,\n  ReturnType<typeof createCounterStore>\n>()\n\nconst createCounterStoreFactory = (\n  counterStores: typeof defaultCounterStores,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n\nconst getOrCreateCounterStoreByKey =\n  createCounterStoreFactory(defaultCounterStores)\n```\n\nNow, let’s build the Tabs component, where users can switch between tabs and increment each tab’s\ncounter.\n\n```tsx\nconst [currentTabIndex, setCurrentTabIndex] = useState(0)\nconst counterState = useStoreWithEqualityFn(\n  getOrCreateCounterStoreByKey(`tab-${currentTabIndex}`),\n  (state) => state,\n  shallow,\n)\n\nreturn (\n  <div style={{ fontFamily: 'monospace' }}>\n    <div\n      style={{\n        display: 'flex',\n        gap: '0.5rem',\n        borderBottom: '1px solid salmon',\n        paddingBottom: 4,\n      }}\n    >\n      <button\n        type=\"button\"\n        style={{\n          border: '1px solid salmon',\n          backgroundColor: '#fff',\n          cursor: 'pointer',\n        }}\n        onClick={() => setCurrentTabIndex(0)}\n      >\n        Tab 1\n      </button>\n      <button\n        type=\"button\"\n        style={{\n          border: '1px solid salmon',\n          backgroundColor: '#fff',\n          cursor: 'pointer',\n        }}\n        onClick={() => setCurrentTabIndex(1)}\n      >\n        Tab 2\n      </button>\n      <button\n        type=\"button\"\n        style={{\n          border: '1px solid salmon',\n          backgroundColor: '#fff',\n          cursor: 'pointer',\n        }}\n        onClick={() => setCurrentTabIndex(2)}\n      >\n        Tab 3\n      </button>\n    </div>\n    <div style={{ padding: 4 }}>\n      Content of Tab {currentTabIndex + 1}\n      <br /> <br />\n      <button type=\"button\" onClick={() => counterState.increment()}>\n        Count: {counterState.count}\n      </button>\n    </div>\n  </div>\n)\n```\n\nFinally, we'll create the `App` component, which renders the tabs and their respective counters.\nThe counter state is managed independently for each tab.\n\n```tsx\nexport default function App() {\n  return <Tabs />\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { useState } from 'react'\nimport { createStore } from 'zustand'\nimport { useStoreWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/shallow'\n\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n\nconst defaultCounterStores = new Map<\n  string,\n  ReturnType<typeof createCounterStore>\n>()\n\nconst createCounterStoreFactory = (\n  counterStores: typeof defaultCounterStores,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n\nconst getOrCreateCounterStoreByKey =\n  createCounterStoreFactory(defaultCounterStores)\n\nexport default function App() {\n  const [currentTabIndex, setCurrentTabIndex] = useState(0)\n  const counterState = useStoreWithEqualityFn(\n    getOrCreateCounterStoreByKey(`tab-${currentTabIndex}`),\n    (state) => state,\n    shallow,\n  )\n\n  return (\n    <div style={{ fontFamily: 'monospace' }}>\n      <div\n        style={{\n          display: 'flex',\n          gap: '0.5rem',\n          borderBottom: '1px solid salmon',\n          paddingBottom: 4,\n        }}\n      >\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(0)}\n        >\n          Tab 1\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(1)}\n        >\n          Tab 2\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(2)}\n        >\n          Tab 3\n        </button>\n      </div>\n      <div style={{ padding: 4 }}>\n        Content of Tab {currentTabIndex + 1}\n        <br /> <br />\n        <button type=\"button\" onClick={() => counterState.increment()}>\n          Count: {counterState.count}\n        </button>\n      </div>\n    </div>\n  )\n}\n```\n\n### Using scoped (non-global) vanilla store in React\n\nFirst, let's set up a store that will hold the position of the dot on the screen. We'll define the\nstore to manage `x` and `y` coordinates and provide an action to update these coordinates.\n\n```tsx\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst createPositionStore = () => {\n  return createStore<PositionStore>()((set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (position) => set({ position }),\n  }))\n}\n```\n\nNext, we'll create a context and a provider component to pass down the store through the React\ncomponent tree. This allows each `MovingDot` component to have its own independent state.\n\n```tsx\nconst PositionStoreContext = createContext<ReturnType<\n  typeof createPositionStore\n> | null>(null)\n\nfunction PositionStoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(() => createPositionStore())\n  return (\n    <PositionStoreContext.Provider value={store}>\n      {children}\n    </PositionStoreContext.Provider>\n  )\n}\n```\n\nTo simplify accessing the store, we’ll create a React custom hook, `usePositionStore`. This hook\nwill read the store from the context and allow us to select specific parts of the state.\n\n```ts\nfunction usePositionStore<U>(selector: (state: PositionStore) => U) {\n  const store = useContext(PositionStoreContext)\n\n  if (store === null) {\n    throw new Error(\n      'usePositionStore must be used within PositionStoreProvider',\n    )\n  }\n\n  return useStoreWithEqualityFn(store, selector, shallow)\n}\n```\n\nNow, let's create the `MovingDot` component, which will render a dot that follows the mouse cursor\nwithin its container.\n\n```tsx\nfunction MovingDot({ color }: { color: string }) {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x:\n            e.clientX > e.currentTarget.clientWidth\n              ? e.clientX - e.currentTarget.clientWidth\n              : e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '50vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: color,\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\nFinally, we'll bring everything together in the `App` component, where we render two `MovingDot`\ncomponents, each with its own independent state.\n\n```tsx\nexport default function App() {\n  return (\n    <div style={{ display: 'flex' }}>\n      <PositionStoreProvider>\n        <MovingDot color=\"red\" />\n      </PositionStoreProvider>\n      <PositionStoreProvider>\n        <MovingDot color=\"blue\" />\n      </PositionStoreProvider>\n    </div>\n  )\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { type ReactNode, useState, createContext, useContext } from 'react'\nimport { createStore } from 'zustand'\nimport { useStoreWithEqualityFn } from 'zustand/traditional'\nimport { shallow } from 'zustand/shallow'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst createPositionStore = () => {\n  return createStore<PositionStore>()((set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (position) => set({ position }),\n  }))\n}\n\nconst PositionStoreContext = createContext<ReturnType<\n  typeof createPositionStore\n> | null>(null)\n\nfunction PositionStoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(() => createPositionStore())\n  return (\n    <PositionStoreContext.Provider value={store}>\n      {children}\n    </PositionStoreContext.Provider>\n  )\n}\n\nfunction usePositionStore<U>(selector: (state: PositionStore) => U) {\n  const store = useContext(PositionStoreContext)\n\n  if (store === null) {\n    throw new Error(\n      'usePositionStore must be used within PositionStoreProvider',\n    )\n  }\n\n  return useStoreWithEqualityFn(store, selector, shallow)\n}\n\nfunction MovingDot({ color }: { color: string }) {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x:\n            e.clientX > e.currentTarget.clientWidth\n              ? e.clientX - e.currentTarget.clientWidth\n              : e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '50vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: color,\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <div style={{ display: 'flex' }}>\n      <PositionStoreProvider>\n        <MovingDot color=\"red\" />\n      </PositionStoreProvider>\n      <PositionStoreProvider>\n        <MovingDot color=\"blue\" />\n      </PositionStoreProvider>\n    </div>\n  )\n}\n```\n\n### Using dynamic scoped (non-global) vanilla stores in React\n\nFirst, we'll create a factory function that generates a store for managing the counter state.\nEach tab will have its own instance of this store.\n\n```ts\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n```\n\nNext, we'll create a factory function that manages the creation and retrieval of counter stores.\nThis allows each tab to have its own independent counter.\n\n```ts\nconst createCounterStoreFactory = (\n  counterStores: Map<string, ReturnType<typeof createCounterStore>>,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n```\n\nNext, we need a way to manage and access these stores throughout our app. We’ll use React’s context\nfor this.\n\n```tsx\nconst CounterStoresContext = createContext(null)\n\nconst CounterStoresProvider = ({ children }) => {\n  const [stores] = useState(\n    () => new Map<string, ReturnType<typeof createCounterStore>>(),\n  )\n\n  return (\n    <CounterStoresContext.Provider value={stores}>\n      {children}\n    </CounterStoresContext.Provider>\n  )\n}\n```\n\nNow, we’ll create a custom hook, `useCounterStore`, that lets us access the correct store for a\ngiven tab.\n\n```tsx\nconst useCounterStore = <U,>(\n  key: string,\n  selector: (state: CounterStore) => U,\n) => {\n  const stores = useContext(CounterStoresContext)\n\n  if (stores === undefined) {\n    throw new Error('useCounterStore must be used within CounterStoresProvider')\n  }\n\n  const getOrCreateCounterStoreByKey = useCallback(\n    (key: string) => createCounterStoreFactory(stores!)(key),\n    [stores],\n  )\n\n  return useStore(getOrCreateCounterStoreByKey(key), selector)\n}\n```\n\nNow, let’s build the Tabs component, where users can switch between tabs and increment each tab’s\ncounter.\n\n```tsx\nfunction Tabs() {\n  const [currentTabIndex, setCurrentTabIndex] = useState(0)\n  const counterState = useCounterStore(\n    `tab-${currentTabIndex}`,\n    (state) => state,\n  )\n\n  return (\n    <div style={{ fontFamily: 'monospace' }}>\n      <div\n        style={{\n          display: 'flex',\n          gap: '0.5rem',\n          borderBottom: '1px solid salmon',\n          paddingBottom: 4,\n        }}\n      >\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(0)}\n        >\n          Tab 1\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(1)}\n        >\n          Tab 2\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(2)}\n        >\n          Tab 3\n        </button>\n      </div>\n      <div style={{ padding: 4 }}>\n        Content of Tab {currentTabIndex + 1}\n        <br /> <br />\n        <button type=\"button\" onClick={() => counterState.increment()}>\n          Count: {counterState.count}\n        </button>\n      </div>\n    </div>\n  )\n}\n```\n\nFinally, we'll create the `App` component, which renders the tabs and their respective counters.\nThe counter state is managed independently for each tab.\n\n```tsx\nexport default function App() {\n  return (\n    <CounterStoresProvider>\n      <Tabs />\n    </CounterStoresProvider>\n  )\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport {\n  type ReactNode,\n  useState,\n  useCallback,\n  useContext,\n  createContext,\n} from 'react'\nimport { createStore, useStore } from 'zustand'\n\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n\nconst createCounterStoreFactory = (\n  counterStores: Map<string, ReturnType<typeof createCounterStore>>,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n\nconst CounterStoresContext = createContext<Map<\n  string,\n  ReturnType<typeof createCounterStore>\n> | null>(null)\n\nconst CounterStoresProvider = ({ children }: { children: ReactNode }) => {\n  const [stores] = useState(\n    () => new Map<string, ReturnType<typeof createCounterStore>>(),\n  )\n\n  return (\n    <CounterStoresContext.Provider value={stores}>\n      {children}\n    </CounterStoresContext.Provider>\n  )\n}\n\nconst useCounterStore = <U,>(\n  key: string,\n  selector: (state: CounterStore) => U,\n) => {\n  const stores = useContext(CounterStoresContext)\n\n  if (stores === undefined) {\n    throw new Error('useCounterStore must be used within CounterStoresProvider')\n  }\n\n  const getOrCreateCounterStoreByKey = useCallback(\n    (key: string) => createCounterStoreFactory(stores!)(key),\n    [stores],\n  )\n\n  return useStore(getOrCreateCounterStoreByKey(key), selector)\n}\n\nfunction Tabs() {\n  const [currentTabIndex, setCurrentTabIndex] = useState(0)\n  const counterState = useCounterStore(\n    `tab-${currentTabIndex}`,\n    (state) => state,\n  )\n\n  return (\n    <div style={{ fontFamily: 'monospace' }}>\n      <div\n        style={{\n          display: 'flex',\n          gap: '0.5rem',\n          borderBottom: '1px solid salmon',\n          paddingBottom: 4,\n        }}\n      >\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(0)}\n        >\n          Tab 1\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(1)}\n        >\n          Tab 2\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(2)}\n        >\n          Tab 3\n        </button>\n      </div>\n      <div style={{ padding: 4 }}>\n        Content of Tab {currentTabIndex + 1}\n        <br /> <br />\n        <button type=\"button\" onClick={() => counterState.increment()}>\n          Count: {counterState.count}\n        </button>\n      </div>\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <CounterStoresProvider>\n      <Tabs />\n    </CounterStoresProvider>\n  )\n}\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/hooks/use-store.md",
    "content": "---\ntitle: useStore\ndescription: How to use vanilla stores in React\ntag: react\nnav: 25\n---\n\n`useStore` is a React Hook that lets you use a vanilla store in React.\n\n```js\nconst someState = useStore(store, selectorFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Use a vanilla store in React](#using-a-global-vanilla-store-in-react)\n  - [Using dynamic vanilla stores in React](#using-dynamic-global-vanilla-stores-in-react)\n  - [Using scoped (non-global) vanilla store in React](<#using-scoped-(non-global)-vanilla-store-in-react>)\n  - [Using dynamic scoped (non-global) vanilla stores in React](<#using-dynamic-scoped-(non-global)-vanilla-stores-in-react>)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\nuseStore<StoreApi<T>, U = T>(store: StoreApi<T>, selectorFn?: (state: T) => U) => UseBoundStore<StoreApi<T>>\n```\n\n## Reference\n\n### `useStore(store, selectorFn)`\n\n#### Parameters\n\n- `storeApi`: The instance that lets you access to store API utilities.\n- `selectorFn`: A function that lets you return data that is based on current state.\n\n#### Returns\n\n`useStore` returns any data based on current state depending on the selector function. It should\ntake a store, and a selector function as arguments.\n\n## Usage\n\n### Using a global vanilla store in React\n\nFirst, let's set up a store that will hold the position of the dot on the screen. We'll define the\nstore to manage `x` and `y` coordinates and provide an action to update these coordinates.\n\n```tsx\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (position) => set({ position }),\n}))\n```\n\nNext, we'll create a `MovingDot` component that renders a div representing the dot. This component\nwill use the store to track and update the dot's position.\n\n```tsx\nfunction MovingDot() {\n  const position = useStore(positionStore, (state) => state.position)\n  const setPosition = useStore(positionStore, (state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\nFinally, we’ll render the `MovingDot` component in our `App` component.\n\n```tsx\nexport default function App() {\n  return <MovingDot />\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { createStore, useStore } from 'zustand'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()((set) => ({\n  position: { x: 0, y: 0 },\n  setPosition: (position) => set({ position }),\n}))\n\nfunction MovingDot() {\n  const position = useStore(positionStore, (state) => state.position)\n  const setPosition = useStore(positionStore, (state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x: e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '100vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: 'red',\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n\nexport default function App() {\n  return <MovingDot />\n}\n```\n\n### Using dynamic global vanilla stores in React\n\nFirst, we'll create a factory function that generates a store for managing the counter state.\nEach tab will have its own instance of this store.\n\n```ts\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n```\n\nNext, we'll create a factory function that manages the creation and retrieval of counter stores.\nThis allows each tab to have its own independent counter.\n\n```ts\nconst defaultCounterStores = new Map<\n  string,\n  ReturnType<typeof createCounterStore>\n>()\n\nconst createCounterStoreFactory = (\n  counterStores: typeof defaultCounterStores,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n\nconst getOrCreateCounterStoreByKey =\n  createCounterStoreFactory(defaultCounterStores)\n```\n\nNow, let’s build the Tabs component, where users can switch between tabs and increment each tab’s\ncounter.\n\n```tsx\nconst [currentTabIndex, setCurrentTabIndex] = useState(0)\nconst counterState = useStore(\n  getOrCreateCounterStoreByKey(`tab-${currentTabIndex}`),\n)\n\nreturn (\n  <div style={{ fontFamily: 'monospace' }}>\n    <div\n      style={{\n        display: 'flex',\n        gap: '0.5rem',\n        borderBottom: '1px solid salmon',\n        paddingBottom: 4,\n      }}\n    >\n      <button\n        type=\"button\"\n        style={{\n          border: '1px solid salmon',\n          backgroundColor: '#fff',\n          cursor: 'pointer',\n        }}\n        onClick={() => setCurrentTabIndex(0)}\n      >\n        Tab 1\n      </button>\n      <button\n        type=\"button\"\n        style={{\n          border: '1px solid salmon',\n          backgroundColor: '#fff',\n          cursor: 'pointer',\n        }}\n        onClick={() => setCurrentTabIndex(1)}\n      >\n        Tab 2\n      </button>\n      <button\n        type=\"button\"\n        style={{\n          border: '1px solid salmon',\n          backgroundColor: '#fff',\n          cursor: 'pointer',\n        }}\n        onClick={() => setCurrentTabIndex(2)}\n      >\n        Tab 3\n      </button>\n    </div>\n    <div style={{ padding: 4 }}>\n      Content of Tab {currentTabIndex + 1}\n      <br /> <br />\n      <button type=\"button\" onClick={() => counterState.increment()}>\n        Count: {counterState.count}\n      </button>\n    </div>\n  </div>\n)\n```\n\nFinally, we'll create the `App` component, which renders the tabs and their respective counters.\nThe counter state is managed independently for each tab.\n\n```tsx\nexport default function App() {\n  return <Tabs />\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { useState } from 'react'\nimport { createStore, useStore } from 'zustand'\n\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n\nconst defaultCounterStores = new Map<\n  string,\n  ReturnType<typeof createCounterStore>\n>()\n\nconst createCounterStoreFactory = (\n  counterStores: typeof defaultCounterStores,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n\nconst getOrCreateCounterStoreByKey =\n  createCounterStoreFactory(defaultCounterStores)\n\nexport default function App() {\n  const [currentTabIndex, setCurrentTabIndex] = useState(0)\n  const counterState = useStore(\n    getOrCreateCounterStoreByKey(`tab-${currentTabIndex}`),\n  )\n\n  return (\n    <div style={{ fontFamily: 'monospace' }}>\n      <div\n        style={{\n          display: 'flex',\n          gap: '0.5rem',\n          borderBottom: '1px solid salmon',\n          paddingBottom: 4,\n        }}\n      >\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(0)}\n        >\n          Tab 1\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(1)}\n        >\n          Tab 2\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(2)}\n        >\n          Tab 3\n        </button>\n      </div>\n      <div style={{ padding: 4 }}>\n        Content of Tab {currentTabIndex + 1}\n        <br /> <br />\n        <button type=\"button\" onClick={() => counterState.increment()}>\n          Count: {counterState.count}\n        </button>\n      </div>\n    </div>\n  )\n}\n```\n\n### Using scoped (non-global) vanilla store in React\n\nFirst, let's set up a store that will hold the position of the dot on the screen. We'll define the\nstore to manage `x` and `y` coordinates and provide an action to update these coordinates.\n\n```tsx\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst createPositionStore = () => {\n  return createStore<PositionStore>()((set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (position) => set({ position }),\n  }))\n}\n```\n\nNext, we'll create a context and a provider component to pass down the store through the React\ncomponent tree. This allows each `MovingDot` component to have its own independent state.\n\n```tsx\nconst PositionStoreContext = createContext<ReturnType<\n  typeof createPositionStore\n> | null>(null)\n\nfunction PositionStoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(() => createPositionStore())\n  return (\n    <PositionStoreContext.Provider value={store}>\n      {children}\n    </PositionStoreContext.Provider>\n  )\n}\n```\n\nTo simplify accessing the store, we’ll create a React custom hook, `usePositionStore`. This hook\nwill read the store from the context and allow us to select specific parts of the state.\n\n```ts\nfunction usePositionStore<U>(selector: (state: PositionStore) => U) {\n  const store = useContext(PositionStoreContext)\n\n  if (store === null) {\n    throw new Error(\n      'usePositionStore must be used within PositionStoreProvider',\n    )\n  }\n\n  return useStore(store, selector)\n}\n```\n\nNow, let's create the `MovingDot` component, which will render a dot that follows the mouse cursor\nwithin its container.\n\n```tsx\nfunction MovingDot({ color }: { color: string }) {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x:\n            e.clientX > e.currentTarget.clientWidth\n              ? e.clientX - e.currentTarget.clientWidth\n              : e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '50vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: color,\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n```\n\nFinally, we'll bring everything together in the `App` component, where we render two `MovingDot`\ncomponents, each with its own independent state.\n\n```tsx\nexport default function App() {\n  return (\n    <div style={{ display: 'flex' }}>\n      <PositionStoreProvider>\n        <MovingDot color=\"red\" />\n      </PositionStoreProvider>\n      <PositionStoreProvider>\n        <MovingDot color=\"blue\" />\n      </PositionStoreProvider>\n    </div>\n  )\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport { type ReactNode, useState, createContext, useContext } from 'react'\nimport { createStore, useStore } from 'zustand'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst createPositionStore = () => {\n  return createStore<PositionStore>()((set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (position) => set({ position }),\n  }))\n}\n\nconst PositionStoreContext = createContext<ReturnType<\n  typeof createPositionStore\n> | null>(null)\n\nfunction PositionStoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(() => createPositionStore())\n  return (\n    <PositionStoreContext.Provider value={store}>\n      {children}\n    </PositionStoreContext.Provider>\n  )\n}\n\nfunction usePositionStore<U>(selector: (state: PositionStore) => U) {\n  const store = useContext(PositionStoreContext)\n\n  if (store === null) {\n    throw new Error(\n      'usePositionStore must be used within PositionStoreProvider',\n    )\n  }\n\n  return useStore(store, selector)\n}\n\nfunction MovingDot({ color }: { color: string }) {\n  const position = usePositionStore((state) => state.position)\n  const setPosition = usePositionStore((state) => state.setPosition)\n\n  return (\n    <div\n      onPointerMove={(e) => {\n        setPosition({\n          x:\n            e.clientX > e.currentTarget.clientWidth\n              ? e.clientX - e.currentTarget.clientWidth\n              : e.clientX,\n          y: e.clientY,\n        })\n      }}\n      style={{\n        position: 'relative',\n        width: '50vw',\n        height: '100vh',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          backgroundColor: color,\n          borderRadius: '50%',\n          transform: `translate(${position.x}px, ${position.y}px)`,\n          left: -10,\n          top: -10,\n          width: 20,\n          height: 20,\n        }}\n      />\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <div style={{ display: 'flex' }}>\n      <PositionStoreProvider>\n        <MovingDot color=\"red\" />\n      </PositionStoreProvider>\n      <PositionStoreProvider>\n        <MovingDot color=\"blue\" />\n      </PositionStoreProvider>\n    </div>\n  )\n}\n```\n\n### Using dynamic scoped (non-global) vanilla stores in React\n\nFirst, we'll create a factory function that generates a store for managing the counter state.\nEach tab will have its own instance of this store.\n\n```ts\nimport { createStore } from 'zustand'\n\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n```\n\nNext, we'll create a factory function that manages the creation and retrieval of counter stores.\nThis allows each tab to have its own independent counter.\n\n```ts\nconst createCounterStoreFactory = (\n  counterStores: Map<string, ReturnType<typeof createCounterStore>>,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n```\n\nNext, we need a way to manage and access these stores throughout our app. We’ll use React’s context\nfor this.\n\n```tsx\nconst CounterStoresContext = createContext(null)\n\nconst CounterStoresProvider = ({ children }) => {\n  const [stores] = useState(\n    () => new Map<string, ReturnType<typeof createCounterStore>>(),\n  )\n\n  return (\n    <CounterStoresContext.Provider value={stores}>\n      {children}\n    </CounterStoresContext.Provider>\n  )\n}\n```\n\nNow, we’ll create a custom hook, `useCounterStore`, that lets us access the correct store for a\ngiven tab.\n\n```tsx\nconst useCounterStore = <U>(\n  currentTabIndex: number,\n  selector: (state: CounterStore) => U,\n) => {\n  const stores = useContext(CounterStoresContext)\n\n  if (stores === undefined) {\n    throw new Error('useCounterStore must be used within CounterStoresProvider')\n  }\n\n  const getOrCreateCounterStoreByKey = useCallback(\n    () => createCounterStoreFactory(stores),\n    [stores],\n  )\n\n  return useStore(getOrCreateCounterStoreByKey(`tab-${currentTabIndex}`))\n}\n```\n\nNow, let’s build the Tabs component, where users can switch between tabs and increment each tab’s\ncounter.\n\n```tsx\nfunction Tabs() {\n  const [currentTabIndex, setCurrentTabIndex] = useState(0)\n  const counterState = useCounterStore(\n    `tab-${currentTabIndex}`,\n    (state) => state,\n  )\n\n  return (\n    <div style={{ fontFamily: 'monospace' }}>\n      <div\n        style={{\n          display: 'flex',\n          gap: '0.5rem',\n          borderBottom: '1px solid salmon',\n          paddingBottom: 4,\n        }}\n      >\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(0)}\n        >\n          Tab 1\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(1)}\n        >\n          Tab 2\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(2)}\n        >\n          Tab 3\n        </button>\n      </div>\n      <div style={{ padding: 4 }}>\n        Content of Tab {currentTabIndex + 1}\n        <br /> <br />\n        <button type=\"button\" onClick={() => counterState.increment()}>\n          Count: {counterState.count}\n        </button>\n      </div>\n    </div>\n  )\n}\n```\n\nFinally, we'll create the `App` component, which renders the tabs and their respective counters.\nThe counter state is managed independently for each tab.\n\n```tsx\nexport default function App() {\n  return (\n    <CounterStoresProvider>\n      <Tabs />\n    </CounterStoresProvider>\n  )\n}\n```\n\nHere is what the code should look like:\n\n```tsx\nimport {\n  type ReactNode,\n  useState,\n  useCallback,\n  useContext,\n  createContext,\n} from 'react'\nimport { createStore, useStore } from 'zustand'\n\ntype CounterState = {\n  count: number\n}\n\ntype CounterActions = { increment: () => void }\n\ntype CounterStore = CounterState & CounterActions\n\nconst createCounterStore = () => {\n  return createStore<CounterStore>()((set) => ({\n    count: 0,\n    increment: () => {\n      set((state) => ({ count: state.count + 1 }))\n    },\n  }))\n}\n\nconst createCounterStoreFactory = (\n  counterStores: Map<string, ReturnType<typeof createCounterStore>>,\n) => {\n  return (counterStoreKey: string) => {\n    if (!counterStores.has(counterStoreKey)) {\n      counterStores.set(counterStoreKey, createCounterStore())\n    }\n    return counterStores.get(counterStoreKey)!\n  }\n}\n\nconst CounterStoresContext = createContext<Map<\n  string,\n  ReturnType<typeof createCounterStore>\n> | null>(null)\n\nconst CounterStoresProvider = ({ children }: { children: ReactNode }) => {\n  const [stores] = useState(\n    () => new Map<string, ReturnType<typeof createCounterStore>>(),\n  )\n\n  return (\n    <CounterStoresContext.Provider value={stores}>\n      {children}\n    </CounterStoresContext.Provider>\n  )\n}\n\nconst useCounterStore = <U,>(\n  key: string,\n  selector: (state: CounterStore) => U,\n) => {\n  const stores = useContext(CounterStoresContext)\n\n  if (stores === undefined) {\n    throw new Error('useCounterStore must be used within CounterStoresProvider')\n  }\n\n  const getOrCreateCounterStoreByKey = useCallback(\n    (key: string) => createCounterStoreFactory(stores!)(key),\n    [stores],\n  )\n\n  return useStore(getOrCreateCounterStoreByKey(key), selector)\n}\n\nfunction Tabs() {\n  const [currentTabIndex, setCurrentTabIndex] = useState(0)\n  const counterState = useCounterStore(\n    `tab-${currentTabIndex}`,\n    (state) => state,\n  )\n\n  return (\n    <div style={{ fontFamily: 'monospace' }}>\n      <div\n        style={{\n          display: 'flex',\n          gap: '0.5rem',\n          borderBottom: '1px solid salmon',\n          paddingBottom: 4,\n        }}\n      >\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(0)}\n        >\n          Tab 1\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(1)}\n        >\n          Tab 2\n        </button>\n        <button\n          type=\"button\"\n          style={{\n            border: '1px solid salmon',\n            backgroundColor: '#fff',\n            cursor: 'pointer',\n          }}\n          onClick={() => setCurrentTabIndex(2)}\n        >\n          Tab 3\n        </button>\n      </div>\n      <div style={{ padding: 4 }}>\n        Content of Tab {currentTabIndex + 1}\n        <br /> <br />\n        <button type=\"button\" onClick={() => counterState.increment()}>\n          Count: {counterState.count}\n        </button>\n      </div>\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <CounterStoresProvider>\n      <Tabs />\n    </CounterStoresProvider>\n  )\n}\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/index.md",
    "content": "---\ntitle: Reference\ndescription: API-first reference for stores, hooks, middlewares, and integrations.\n---\n\n## APIs\n\nCore functions for creating and configuring stores.\n\n- [`create`](./apis/create.md) — Create a store bound to React via hooks.\n- [`createStore`](./apis/create-store.md) — Create a standalone store without React.\n- [`createWithEqualityFn`](./apis/create-with-equality-fn.md) — Like `create`, but with a custom equality function.\n- [`shallow`](./apis/shallow.md) — Utility for shallow comparison of objects and arrays.\n\n## Hooks\n\nReact hooks for reading and subscribing to store state.\n\n- [`useStore`](./hooks/use-store.md) — Access and subscribe to a vanilla store from a React component.\n- [`useStoreWithEqualityFn`](./hooks/use-store-with-equality-fn.md) — Like `useStore`, but with a custom equality function.\n- [`useShallow`](./hooks/use-shallow.md) — Derive a stable reference from a selector using shallow comparison.\n\n## Middlewares\n\nComposable middleware functions for extending store behavior.\n\n- [`persist`](./middlewares/persist.md) — Persist and rehydrate state using `localStorage` or a custom storage engine.\n- [`devtools`](./middlewares/devtools.md) — Connect a store to Redux DevTools for time-travel debugging.\n- [`redux`](./middlewares/redux.md) — Use a reducer and dispatch pattern similar to Redux.\n- [`immer`](./middlewares/immer.md) — Write state updates with mutable syntax using Immer.\n- [`combine`](./middlewares/combine.md) — Combine separate state slices into a single store with inferred types.\n- [`subscribeWithSelector`](./middlewares/subscribe-with-selector.md) — Subscribe to a slice of state with selector and equality support.\n\n## Integrations\n\nIn-depth guides for using Zustand alongside third-party libraries.\n\n- [Persisting store data](./integrations/persisting-store-data.md) — Detailed guide to the `persist` middleware and storage adapters.\n- [Immer middleware](./integrations/immer-middleware.md) — Detailed guide to the `immer` middleware.\n- [Third-party libraries](./integrations/third-party-libraries.md) — Using Zustand with other libraries in the ecosystem.\n\n## Migrations\n\nUpgrade guides between major versions.\n\n- [Migrating to v5](./migrations/migrating-to-v5.md) — How to upgrade from Zustand v4.\n- [Migrating to v4](./migrations/migrating-to-v4.md) — How to upgrade from Zustand v3.\n\n## Previous versions\n\nAPIs that existed in older versions of Zustand and are no longer recommended for new code.\n\n- [createContext (v3)](./previous-versions/zustand-v3-create-context.md) — The `createContext` export from `zustand/context`, deprecated in v4 and removed in v5.\n"
  },
  {
    "path": "docs/reference/integrations/immer-middleware.md",
    "content": "---\ntitle: Immer middleware\nnav: 35\n---\n\nThe [Immer](https://github.com/immerjs/immer) middleware enables you\nto use immutable state in a more convenient way.\nAlso, with Immer, you can simplify handling\nimmutable data structures in Zustand.\n\n## Installation\n\nIn order to use the Immer middleware in Zustand,\nyou will need to install Immer as a direct dependency.\n\n```bash\nnpm install immer\n```\n\n## Usage\n\n(Notice the extra parentheses after the type parameter as mentioned in the [Advanced Typescript Guide](../../learn/guides/advanced-typescript.md)).\n\nUpdating simple states\n\n```ts\nimport { create } from 'zustand'\nimport { immer } from 'zustand/middleware/immer'\n\ntype State = {\n  count: number\n}\n\ntype Actions = {\n  increment: (qty: number) => void\n  decrement: (qty: number) => void\n}\n\nexport const useCountStore = create<State & Actions>()(\n  immer((set) => ({\n    count: 0,\n    increment: (qty: number) =>\n      set((state) => {\n        state.count += qty\n      }),\n    decrement: (qty: number) =>\n      set((state) => {\n        state.count -= qty\n      }),\n  })),\n)\n```\n\nUpdating complex states\n\n```ts\nimport { create } from 'zustand'\nimport { immer } from 'zustand/middleware/immer'\n\ninterface Todo {\n  id: string\n  title: string\n  done: boolean\n}\n\ntype State = {\n  todos: Record<string, Todo>\n}\n\ntype Actions = {\n  toggleTodo: (todoId: string) => void\n}\n\nexport const useTodoStore = create<State & Actions>()(\n  immer((set) => ({\n    todos: {\n      '82471c5f-4207-4b1d-abcb-b98547e01a3e': {\n        id: '82471c5f-4207-4b1d-abcb-b98547e01a3e',\n        title: 'Learn Zustand',\n        done: false,\n      },\n      '354ee16c-bfdd-44d3-afa9-e93679bda367': {\n        id: '354ee16c-bfdd-44d3-afa9-e93679bda367',\n        title: 'Learn Jotai',\n        done: false,\n      },\n      '771c85c5-46ea-4a11-8fed-36cc2c7be344': {\n        id: '771c85c5-46ea-4a11-8fed-36cc2c7be344',\n        title: 'Learn Valtio',\n        done: false,\n      },\n      '363a4bac-083f-47f7-a0a2-aeeee153a99c': {\n        id: '363a4bac-083f-47f7-a0a2-aeeee153a99c',\n        title: 'Learn Signals',\n        done: false,\n      },\n    },\n    toggleTodo: (todoId: string) =>\n      set((state) => {\n        state.todos[todoId].done = !state.todos[todoId].done\n      }),\n  })),\n)\n```\n\n## Gotchas\n\nIn this section you will find some things\nthat you need to keep in mind when using Zustand with Immer.\n\n### My subscriptions aren't being called\n\nIf you are using Immer,\nmake sure you are actually following\n[the rules of Immer](https://immerjs.github.io/immer/pitfalls).\n\nFor example, you have to add `[immerable] = true` for\n[class objects](https://immerjs.github.io/immer/complex-objects) to work.\nIf you don't do this, Immer will still mutate the object,\nbut not as a proxy, so it will also update the current state.\nZustand checks if the state has actually changed,\nso since both the current state and the next state are\nequal (if you don't do it correctly),\nZustand will skip calling the subscriptions.\n\n## Demos\n\n- Basic: https://stackblitz.com/edit/vitejs-vite-3sgc4ejy\n- Advanced: https://stackblitz.com/edit/vitejs-vite-jxxtuyj3\n"
  },
  {
    "path": "docs/reference/integrations/persisting-store-data.md",
    "content": "---\ntitle: Persisting store data\nnav: 34\n---\n\nThe Persist middleware enables you to store\nyour Zustand state in a storage\n(e.g., `localStorage`, `AsyncStorage`, `IndexedDB`, etc.),\nthus persisting its data.\n\nNote that this middleware supports both\nsynchronous storages, like `localStorage`,\nand asynchronous storages, like `AsyncStorage`,\nbut using an asynchronous storage does come with a cost.\nSee [Hydration and asynchronous storages](#hydration-and-asynchronous-storages)\nfor more details.\n\n## Simple example\n\n```ts\nimport { create } from 'zustand'\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\nexport const useBearStore = create()(\n  persist(\n    (set, get) => ({\n      bears: 0,\n      addABear: () => set({ bears: get().bears + 1 }),\n    }),\n    {\n      name: 'food-storage', // name of the item in the storage (must be unique)\n      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used\n    },\n  ),\n)\n```\n\n## Typescript simple example\n\n```ts\nimport { create } from 'zustand'\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\ntype BearStore = {\n  bears: number\n  addABear: () => void\n}\n\nexport const useBearStore = create<BearStore>()(\n  persist(\n    (set, get) => ({\n      bears: 0,\n      addABear: () => set({ bears: get().bears + 1 }),\n    }),\n    {\n      name: 'food-storage', // name of the item in the storage (must be unique)\n      storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used\n    },\n  ),\n)\n```\n\n## Options\n\n### `name`\n\nThis is the only required option.\nThe given name is going to be the key\nused to store your Zustand state in the storage,\nso it must be unique.\n\n### `storage`\n\n> Type: `() => StateStorage`\n\nThe `StateStorage` can be imported with:\n\n```ts\nimport { StateStorage } from 'zustand/middleware'\n```\n\n> Default: `createJSONStorage(() => localStorage)`\n\nEnables you to use your own storage. Simply pass a function that returns the storage you want to use. It's recommended to use the [`createJSONStorage`](#createjsonstorage) helper function to create a `storage` object that is compliant with the `StateStorage` interface.\n\nExample:\n\n```ts\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      // ...\n    }),\n    {\n      // ...\n      storage: createJSONStorage(() => AsyncStorage),\n    },\n  ),\n)\n```\n\n### `partialize`\n\n> Type: `(state: Object) => Object`\n\n> Default: `(state) => state`\n\nEnables you to pick some of the state's fields to be stored in the storage.\n\nYou could omit multiple fields using the following:\n\n```ts\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      foo: 0,\n      bar: 1,\n    }),\n    {\n      // ...\n      partialize: (state) =>\n        Object.fromEntries(\n          Object.entries(state).filter(([key]) => !['foo'].includes(key)),\n        ),\n    },\n  ),\n)\n```\n\nOr you could allow only specific fields using the following:\n\n```ts\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      foo: 0,\n      bar: 1,\n    }),\n    {\n      // ...\n      partialize: (state) => ({ foo: state.foo }),\n    },\n  ),\n)\n```\n\n### `onRehydrateStorage`\n\n> Type: `(state: Object) => ((state?: Object, error?: Error) => void) | void`\n\nThis option enables you to pass a listener function\nthat will be called when the storage is hydrated.\n\nExample:\n\n```ts\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      // ...\n    }),\n    {\n      // ...\n      onRehydrateStorage: (state) => {\n        console.log('hydration starts')\n\n        // optional\n        return (state, error) => {\n          if (error) {\n            console.log('an error happened during hydration', error)\n          } else {\n            console.log('hydration finished')\n          }\n        }\n      },\n    },\n  ),\n)\n```\n\n### `version`\n\n> Type: `number`\n\n> Default: `0`\n\nIf you want to introduce a breaking change in your storage\n(e.g. renaming a field), you can specify a new version number.\nBy default, if the version in the storage\ndoes not match the version in the code,\nthe stored value won't be used.\nYou can use the [migrate](#migrate) function (see below)\nto handle breaking changes in order to persist previously stored data.\n\n### `migrate`\n\n> Type: `(persistedState: Object, version: number) => Object | Promise<Object>`\n\n> Default: `(persistedState) => persistedState`\n\nYou can use this option to handle versions migration.\nThe migrate function takes the persisted state\nand the version number as arguments.\nIt must return a state that is compliant\nto the latest version (the version in the code).\n\nFor instance, if you want to rename a field, you can use the following:\n\n```ts\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      newField: 0, // let's say this field was named otherwise in version 0\n    }),\n    {\n      // ...\n      version: 1, // a migration will be triggered if the version in the storage mismatches this one\n      migrate: (persistedState, version) => {\n        if (version === 0) {\n          // if the stored value is in version 0, we rename the field to the new name\n          persistedState.newField = persistedState.oldField\n          delete persistedState.oldField\n        }\n\n        return persistedState\n      },\n    },\n  ),\n)\n```\n\n### `merge`\n\n> Type: `(persistedState: Object, currentState: Object) => Object`\n\n> Default: `(persistedState, currentState) => ({ ...currentState, ...persistedState })`\n\nIn some cases, you might want to use a custom merge function\nto merge the persisted value with the current state.\n\nBy default, the middleware does a shallow merge.\nThe shallow merge might not be enough\nif you have partially persisted nested objects.\nFor instance, if the storage contains the following:\n\n```ts\n{\n  foo: {\n    bar: 0,\n  }\n}\n```\n\nBut your Zustand store contains:\n\n```ts\n{\n  foo: {\n    bar: 0,\n    baz: 1,\n  }\n}\n```\n\nThe shallow merge will erase the `baz` field from the `foo` object.\nOne way to fix this would be to give a custom deep merge function:\n\n```ts\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      foo: {\n        bar: 0,\n        baz: 1,\n      },\n    }),\n    {\n      // ...\n      merge: (persistedState, currentState) =>\n        deepMerge(currentState, persistedState),\n    },\n  ),\n)\n```\n\n### `skipHydration`\n\n> Type: `boolean | undefined`\n\n> Default: `undefined`\n\nBy default the store will be hydrated on initialization.\n\nIn some applications you may need to control when the first hydration occurs.\nFor example, in server-rendered apps.\n\nIf you set `skipHydration`, the initial call for hydration isn't called,\nand it is left up to you to manually call `rehydrate()`.\n\n```ts\nexport const useBoundStore = create(\n  persist(\n    () => ({\n      count: 0,\n      // ...\n    }),\n    {\n      // ...\n      skipHydration: true,\n    },\n  ),\n)\n```\n\n```tsx\nimport { useBoundStore } from './path-to-store';\n\nexport function StoreConsumer() {\n  // hydrate persisted store after on mount\n  useEffect(() => {\n    useBoundStore.persist.rehydrate();\n  }, [])\n\n  return (\n    //...\n  )\n}\n```\n\n## API\n\n> Version: >=3.6.3\n\nThe Persist API enables you to do a number of interactions\nwith the Persist middleware\nfrom inside or outside of a React component.\n\n### `getOptions`\n\n> Type: `() => Partial<PersistOptions>`\n\n> Returns: Options of the Persist middleware\n\nFor example, it can be used to obtain the storage name:\n\n```ts\nuseBoundStore.persist.getOptions().name\n```\n\n### `setOptions`\n\n> Type: `(newOptions: Partial<PersistOptions>) => void`\n\nChanges the middleware options.\nNote that the new options will be merged with the current ones.\n\nFor instance, this can be used to change the storage name:\n\n```ts\nuseBoundStore.persist.setOptions({\n  name: 'new-name',\n})\n```\n\nOr even to change the storage engine:\n\n```ts\nuseBoundStore.persist.setOptions({\n  storage: createJSONStorage(() => sessionStorage),\n})\n```\n\n### `clearStorage`\n\n> Type: `() => void`\n\nClears everything stored under the [name](#name) key.\n\n```ts\nuseBoundStore.persist.clearStorage()\n```\n\n### `rehydrate`\n\n> Type: `() => Promise<void>`\n\nIn some cases, you might want to trigger the rehydration manually.\nThis can be done by calling the `rehydrate` method.\n\n```ts\nawait useBoundStore.persist.rehydrate()\n```\n\n### `hasHydrated`\n\n> Type: `() => boolean`\n\nThis is a non-reactive getter to check\nif the storage has been hydrated\n(note that it updates when calling [`rehydrate`](#rehydrate)).\n\n```ts\nuseBoundStore.persist.hasHydrated()\n```\n\n### `onHydrate`\n\n> Type: `(listener: (state) => void) => () => void`\n\n> Returns: Unsubscribe function\n\nThis listener will be called when the hydration process starts.\n\n```ts\nconst unsub = useBoundStore.persist.onHydrate((state) => {\n  console.log('hydration starts')\n})\n\n// later on...\nunsub()\n```\n\n### `onFinishHydration`\n\n> Type: `(listener: (state) => void) => () => void`\n\n> Returns: Unsubscribe function\n\nThis listener will be called when the hydration process ends.\n\n```ts\nconst unsub = useBoundStore.persist.onFinishHydration((state) => {\n  console.log('hydration finished')\n})\n\n// later on...\nunsub()\n```\n\n### `createJSONStorage`\n\n> Type: `(getStorage: () => StateStorage, options?: JsonStorageOptions) => StateStorage`\n\n> Returns: `PersistStorage`\n\nThis helper function enables you to create a [`storage`](#storage) object which is useful when you want to use a custom storage engine.\n\n`getStorage` is a function that returns the storage engine with the properties `getItem`, `setItem`, and `removeItem`.\n\n`options` is an optional object that can be used to customize the serialization and deserialization of the data. `options.reviver` is a function that is passed to `JSON.parse` to deserialize the data. `options.replacer` is a function that is passed to `JSON.stringify` to serialize the data.\n\n```ts\nimport { createJSONStorage } from 'zustand/middleware'\n\nconst storage = createJSONStorage(() => sessionStorage, {\n  reviver: (key, value) => {\n    if (value && value.type === 'date') {\n      return new Date(value)\n    }\n    return value\n  },\n  replacer: (key, value) => {\n    // NOTE: the result of `.toJSON()` is passed to the\n    // replacer function as value if is available so\n    // a Date is always a `string` at this point\n    if (key === 'someDate') return { type: 'date', value }\n    return value\n  },\n})\n```\n\n## Hydration and asynchronous storages\n\nTo explain what is the \"cost\" of asynchronous storages,\nyou need to understand what is hydration.\n\nIn a nutshell, hydration is a process\nof retrieving persisted state from the storage\nand merging it with the current state.\n\nThe Persist middleware does two kinds of hydration:\nsynchronous and asynchronous.\nIf the given storage is synchronous (e.g., `localStorage`),\nhydration will be done synchronously.\nOn the other hand, if the given storage is asynchronous (e.g., `AsyncStorage`),\nhydration will be done asynchronously (shocking, I know!).\n\nBut what's the catch?\nWith synchronous hydration,\nthe Zustand store will already have been hydrated at its creation.\nIn contrast, with asynchronous hydration,\nthe Zustand store will be hydrated later on, in a microtask.\n\nWhy does it matter?\nAsynchronous hydration can cause some unexpected behaviors.\nFor instance, if you use Zustand in a React app,\nthe store will **not** be hydrated at the initial render.\nIn cases where your app depends on the persisted value at page load,\nyou might want to wait until\nthe store has been hydrated before showing anything.\nFor example, your app might think the user\nis not logged in because it's the default,\nbut in reality the store has not been hydrated yet.\n\nIf your app does depends on the persisted state at page load,\nsee [_How can I check if my store has been hydrated_](#how-can-i-check-if-my-store-has-been-hydrated)\nin the [FAQ](#faq) section below.\n\n### Usage in Next.js\n\nNextJS uses Server Side Rendering, and it will compare the rendered component on the server with the one rendered on client.\nBut since you are using data from browser to change your component, the two renders will differ and Next will throw a warning at you.\n\nThe errors usually are:\n\n- Text content does not match server-rendered HTML\n- Hydration failed because the initial UI does not match what was rendered on the server\n- There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering\n\nTo solve these errors, create a custom hook so that Zustand waits a little before changing your components.\n\nCreate a file with the following:\n\n```ts\n// useStore.ts\nimport { useState, useEffect } from 'react'\n\nconst useStore = <T, F>(\n  store: (callback: (state: T) => unknown) => unknown,\n  callback: (state: T) => F,\n) => {\n  const result = store(callback) as F\n  const [data, setData] = useState<F>()\n\n  useEffect(() => {\n    setData(result)\n  }, [result])\n\n  return data\n}\n\nexport default useStore\n```\n\nNow in your pages, you will use the hook a little bit differently:\n\n```ts\n// useBearStore.ts\n\nimport { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\n\n// the store itself does not need any change\nexport const useBearStore = create(\n  persist(\n    (set, get) => ({\n      bears: 0,\n      addABear: () => set({ bears: get().bears + 1 }),\n    }),\n    {\n      name: 'food-storage',\n    },\n  ),\n)\n```\n\n```ts\n// yourComponent.tsx\n\nimport useStore from './useStore'\nimport { useBearStore } from './stores/useBearStore'\n\nconst bears = useStore(useBearStore, (state) => state.bears)\n```\n\nCredits: [This reply to an issue](https://github.com/pmndrs/zustand/issues/938#issuecomment-1481801942), which points to [this blog post](https://dev.to/abdulsamad/how-to-use-zustands-persist-middleware-in-nextjs-4lb5).\n\n## FAQ\n\n### How can I check if my store has been hydrated\n\nThere are a few different ways to do this.\n\nYou can use the [`onRehydrateStorage`](#onrehydratestorage)\nlistener function to update a field in the store:\n\n```ts\nconst useBoundStore = create(\n  persist(\n    (set, get) => ({\n      // ...\n      _hasHydrated: false,\n      setHasHydrated: (state) => {\n        set({\n          _hasHydrated: state\n        });\n      }\n    }),\n    {\n      // ...\n      onRehydrateStorage: (state) => {\n        return () => state.setHasHydrated(true)\n      }\n    }\n  )\n);\n\nexport default function App() {\n  const hasHydrated = useBoundStore(state => state._hasHydrated);\n\n  if (!hasHydrated) {\n    return <p>Loading...</p>\n  }\n\n  return (\n    // ...\n  );\n}\n```\n\nYou can also create a custom `useHydration` hook:\n\n```ts\nconst useBoundStore = create(persist(...))\n\nconst useHydration = () => {\n  const [hydrated, setHydrated] = useState(false)\n\n  useEffect(() => {\n    // Note: This is just in case you want to take into account manual rehydration.\n    // You can remove the following line if you don't need it.\n    const unsubHydrate = useBoundStore.persist.onHydrate(() => setHydrated(false))\n\n    const unsubFinishHydration = useBoundStore.persist.onFinishHydration(() => setHydrated(true))\n\n    setHydrated(useBoundStore.persist.hasHydrated())\n\n    return () => {\n      unsubHydrate()\n      unsubFinishHydration()\n    }\n  }, [])\n\n  return hydrated\n}\n```\n\n### How can I use a custom storage engine\n\nIf the storage you want to use does not match the expected API, you can create your own storage:\n\n```ts\nimport { create } from 'zustand'\nimport { persist, createJSONStorage, StateStorage } from 'zustand/middleware'\nimport { get, set, del } from 'idb-keyval' // can use anything: IndexedDB, Ionic Storage, etc.\n\n// Custom storage object\nconst storage: StateStorage = {\n  getItem: async (name: string): Promise<string | null> => {\n    console.log(name, 'has been retrieved')\n    return (await get(name)) || null\n  },\n  setItem: async (name: string, value: string): Promise<void> => {\n    console.log(name, 'with value', value, 'has been saved')\n    await set(name, value)\n  },\n  removeItem: async (name: string): Promise<void> => {\n    console.log(name, 'has been deleted')\n    await del(name)\n  },\n}\n\nexport const useBoundStore = create(\n  persist(\n    (set, get) => ({\n      bears: 0,\n      addABear: () => set({ bears: get().bears + 1 }),\n    }),\n    {\n      name: 'food-storage', // unique name\n      storage: createJSONStorage(() => storage),\n    },\n  ),\n)\n```\n\nIf you're using a type that `JSON.stringify()` doesn't support, you'll need to write your own serialization/deserialization code. However, if this is tedious, you can use third-party libraries to serialize and deserialize different types of data.\n\nFor example, [Superjson](https://github.com/blitz-js/superjson) can serialize data along with its type, allowing the data to be parsed back to its original type upon deserialization\n\n```ts\nimport superjson from 'superjson' //  can use anything: serialize-javascript, devalue, etc.\nimport { PersistStorage } from 'zustand/middleware'\n\ninterface BearState {\n  bear: Map<string, string>\n  fish: Set<string>\n  time: Date\n  query: RegExp\n}\n\nconst storage: PersistStorage<BearState> = {\n  getItem: (name) => {\n    const str = localStorage.getItem(name)\n    if (!str) return null\n    return superjson.parse(str)\n  },\n  setItem: (name, value) => {\n    localStorage.setItem(name, superjson.stringify(value))\n  },\n  removeItem: (name) => localStorage.removeItem(name),\n}\n\nconst initialState: BearState = {\n  bear: new Map(),\n  fish: new Set(),\n  time: new Date(),\n  query: new RegExp(''),\n}\n\nexport const useBearStore = create<BearState>()(\n  persist(\n    (set) => ({\n      ...initialState,\n      // ...\n    }),\n    {\n      name: 'food-storage',\n      storage,\n    },\n  ),\n)\n```\n\n### How can I rehydrate on storage event\n\nYou can use the Persist API to create your own implementation,\nsimilar to the example below:\n\n```ts\ntype StoreWithPersist = Mutate<StoreApi<State>, [[\"zustand/persist\", unknown]]>\n\nexport const withStorageDOMEvents = (store: StoreWithPersist) => {\n  const storageEventCallback = (e: StorageEvent) => {\n    if (e.key === store.persist.getOptions().name && e.newValue) {\n      store.persist.rehydrate()\n    }\n  }\n\n  window.addEventListener('storage', storageEventCallback)\n\n  return () => {\n    window.removeEventListener('storage', storageEventCallback)\n  }\n}\n\nconst useBoundStore = create(persist(...))\nwithStorageDOMEvents(useBoundStore)\n```\n\n### How do I use it with TypeScript\n\nBasic typescript usage doesn't require anything special\nexcept for writing `create<State>()(...)` instead of `create(...)`.\n\n```tsx\nimport { create } from 'zustand'\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\ninterface MyState {\n  bears: number\n  addABear: () => void\n}\n\nexport const useBearStore = create<MyState>()(\n  persist(\n    (set, get) => ({\n      bears: 0,\n      addABear: () => set({ bears: get().bears + 1 }),\n    }),\n    {\n      name: 'food-storage', // name of item in the storage (must be unique)\n      storage: createJSONStorage(() => sessionStorage), // (optional) by default the 'localStorage' is used\n      partialize: (state) => ({ bears: state.bears }),\n    },\n  ),\n)\n```\n\n### How do I use it with Map and Set\n\nIn order to persist object types such as `Map` and `Set`, they will need to be converted to JSON-serializable types such as an `Array` which can be done by defining a custom `storage` engine.\n\nLet's say your state uses `Map` to handle a list of `transactions`,\nthen you can convert the `Map` into an `Array` in the `storage` prop which is shown below:\n\n```ts\n\ninterface BearState {\n  .\n  .\n  .\n  transactions: Map<any>\n}\n\n  storage: {\n    getItem: (name) => {\n      const str = localStorage.getItem(name);\n      if (!str) return null;\n      const existingValue = JSON.parse(str);\n      return {\n        ...existingValue,\n        state: {\n          ...existingValue.state,\n          transactions: new Map(existingValue.state.transactions),\n        }\n      }\n    },\n    setItem: (name, newValue: StorageValue<BearState>) => {\n      // functions cannot be JSON encoded\n      const str = JSON.stringify({\n        ...newValue,\n        state: {\n          ...newValue.state,\n          transactions: Array.from(newValue.state.transactions.entries()),\n        },\n      })\n      localStorage.setItem(name, str)\n    },\n    removeItem: (name) => localStorage.removeItem(name),\n  },\n```\n"
  },
  {
    "path": "docs/reference/integrations/third-party-libraries.md",
    "content": "---\ntitle: Third-party Libraries\nnav: 36\n---\n\nZustand provides bear necessities for state management.\nAlthough it is great for most projects,\nsome users wish to extend the library's feature set.\nThis can be done using third-party libraries created by the community.\n\n> Disclaimer: These libraries may have bugs, limited maintenance,\n> or other limitations, and are not officially recommended\n> by pmndrs or the Zustand maintainers.\n> This list aims to provide a good starting point\n> for someone looking to extend Zustand's feature set.\n\n- [@colorfy-software/zfy](https://colorfy-software.gitbook.io/zfy/) — 🧸 Useful helpers for state management in React with Zustand.\n- [@csark0812/zustand-expo-devtools](https://github.com/csark0812/zustand-expo-devtools) — 🧭 Connect Zustand to Redux DevTools in Expo + React Native using the official Expo DevTools plugin system.\n- [@csark0812/zustand-getters](https://github.com/csark0812/zustand-getters) — 🔄 Make JavaScript object getters reactive in Zustand stores — define derived values with `get propertyName()` and they automatically trigger subscription updates when accessed.\n- [@davstack/store](https://www.npmjs.com/package/@davstack/store) — A zustand store factory that auto generates selectors with get/set/use methods, supports inferred types, and makes global / local state management easy.\n- [@dhmk/zustand-lens](https://github.com/dhmk083/dhmk-zustand-lens) — Lens support for Zustand.\n- [@hpkv/zustand-multiplayer](https://github.com/hpkv-io/zustand-multiplayer/tree/main/packages/zustand-multiplayer) — HPKV multiplayer middleware for building realtime collaborative applications\n- [@liveblocks/zustand](https://github.com/liveblocks/liveblocks/tree/main/packages/liveblocks-zustand) — Liveblocks middleware to make your application multiplayer.\n- [@prncss-xyz/zustand-optics](https://github.com/prncss-xyz/zustand-optics) — An adapter for [optics-ts](https://github.com/akheron/optics-ts).\n- [auto-zustand-selectors-hook](https://github.com/Albert-Gao/auto-zustand-selectors-hook) — Automatic generation of Zustand hooks with Typescript support.\n- [derive-zustand](https://github.com/zustandjs/derive-zustand) — A function to create a derived Zustand store from other Zustand stores.\n- [geschichte](https://github.com/BowlingX/geschichte) — Zustand and Immer-based hook to manage query parameters.\n- [leiten-zustand](https://github.com/hecmatyar/leiten-zustand) — Cleans your store from boilerplate for requests and data transformation.\n- [leo-query](https://github.com/steaks/leo-query) — A simple library to connect async queries to Zustand stores.\n- [mobz](https://github.com/2A5F/Mobz) — Zustand-style MobX API.\n- [ngx-zustand](https://github.com/JoaoPauloLousada/ngx-zustand) — A Zustand adapter for Angular.\n- [persist-and-sync](https://github.com/mayank1513/persist-and-sync) — Zustand middleware to easily persist and sync Zustand state between tabs/windows/iframes with same origin.\n- [shared-zustand](https://github.com/Tom-Julux/shared-zustand) — Cross-tab state sharing for Zustand.\n- [simple-zustand-devtools](https://github.com/beerose/simple-zustand-devtools) — 🐻⚛️ Inspect your Zustand store in React DevTools.\n- [solid-zustand](https://github.com/wobsoriano/solid-zustand) — State management in Solid using Zustand.\n- [treeshakable](https://github.com/react18-tools/treeshakable) — A wrapper for library creators to avoid redundant store creation.\n- [use-broadcast-ts](https://github.com/Romainlg29/use-broadcast) — Zustand middleware to share state between tabs.\n- [use-post-message-ts](https://github.com/paulschoen/use-post-message) — Zustand middleware for sharing state between cross-origin iframes via postMessage browser method.\n- [use-zustand](https://github.com/zustandjs/use-zustand) — Another custom hook to use Zustand vanilla store.\n- [vue-zustand](https://github.com/wobsoriano/vue-zustand) — State management solution for Vue based on Zustand.\n- [zoov](https://github.com/InfiniteXyy/zoov) — State management solution based on Zustand with Module-like API.\n- [zubridge](https://github.com/goosewobbler/zubridge) — Use Zustand in cross-platform apps, seamlessly. Supports Electron & Tauri.\n- [zukeeper](https://github.com/oslabs-beta/Zukeeper) — Native devtools with state and action tracking, diffing, tree display, and time travel\n- [zundo](https://github.com/charkour/zundo) — 🍜 Undo and redo middleware for Zustand, enabling time-travel in your apps.\n- [zustand-ards](https://github.com/ivoilic/zustand-ards) — 💁 Simple opinionated utilities for example alternative selector formats and default shallow hooks\n- [zustand-async-slice](https://github.com/mym0404/zustand-async-slice) — Simple Zustand utility to create Async Slice. TypeScript Fully Supported 🖖\n- [zustand-boilerplate](https://github.com/sagiereder/zustand-boilerplate) — A tool that automatically generates getters, setters and more for your zustand store.\n- [zustand-computed](https://github.com/chrisvander/zustand-computed) — A Zustand middleware to create computed states.\n- [zustand-computed-state](https://github.com/yasintz/zustand-computed-state) — Simple middleware to add computed states.\n- [zustand-constate](https://github.com/ntvinhit/zustand-constate) — Context-based state management based on Zustand and taking ideas from Constate.\n- [zustand-context](https://github.com/fredericoo/zustand-context) — Create a zustand store in React Context, containing an initial value, or use it in your components with isolated, mockable instances.\n- [zustand-create-setter-fn](https://www.npmjs.com/package/zustand-create-setter-fn) — A fully type safe utility for Zustand that allows you to easily update state using React style `setState` functions (framework agnostic, doesn't require React).\n- [zustand-di](https://github.com/charkour/zustand-di) — use react props to init zustand stores\n- [zustand-forms](https://github.com/Conduct/zustand-forms) — Fast, type safe form states as Zustand stores.\n- [zustand-hash-storage](https://github.com/MartinGamesCZ/zustand-hash-storage) — Zustand middleware for saving state into URL hash, b64 encoded (can be configured) and debounce timer.\n- [zustand-injectors](https://github.com/zustandjs/zustand-injectors) — A sweet way to lazy load slices\n- [zustand-interval-persist](https://www.npmjs.com/package/zustand-interval-persist) — An enhancement for zustand that enables automatic saving of the store's state to the specified storage at regular interval.\n- [zustand-lit](https://github.com/ennjin/zustand-lit) — A zustand adapter for lit.js (LitElement)\n- [zustand-middleware-computed-state](https://github.com/cmlarsen/zustand-middleware-computed-state) — A dead simple middleware for adding computed state to Zustand.\n- [zustand-middleware-xstate](https://github.com/biowaffeln/zustand-middleware-xstate) — A middleware for putting XState state machines into a global Zustand store.\n- [zustand-middleware-yjs](https://github.com/joebobmiles/zustand-middleware-yjs) — A middleware for synchronizing Zustand stores with Yjs.\n- [zustand-mmkv-storage](https://github.com/1mehdifaraji/zustand-mmkv-storage) — Fast, lightweight MMKV storage adapter for Zustand persist middleware in React Native.\n- [zustand-multi-persist](https://github.com/mooalot/zustand-multi-persist) — A middleware for persisting and rehydrating state to multiple storage engines.\n- [zustand-mutable](https://github.com/zustandjs/zustand-mutable) — A sweet way to use immer-like mutable updates.\n- [zustand-namespaces](https://github.com/mooalot/zustand-namespaces) — One store to rule them all. Namespaced Zustand stores.\n- [zustand-persist](https://github.com/roadmanfong/zustand-persist) — A middleware for persisting and rehydrating state.\n- [zustand-pub](https://github.com/AwesomeDevin/zustand-pub) — Cross-Application/Cross-Framework State Management And Sharing based on zustand and zustand-vue for React/Vue.\n- [zustand-querystring](https://github.com/nitedani/zustand-querystring) — A Zustand middleware that syncs the store with the querystring.\n- [zustand-rx](https://github.com/patdx/zustand-rx) — A Zustand middleware enabling you to subscribe to a store as an RxJS Observable.\n- [zustand-saga](https://github.com/Nowsta/zustand-saga) — A Zustand middleware for redux-saga (minus redux).\n- [zustand-slices](https://github.com/zustandjs/zustand-slices) — A slice utility for Zustand.\n- [zustand-store-addons](https://github.com/Diablow/zustand-store-addons) — React state management addons for Zustand.\n- [zustand-sync-tabs](https://github.com/mayank1513/zustand-sync-tabs) — Zustand middleware to easily sync Zustand state between tabs/windows/iframes with same origin.\n- [zustand-utils](https://www.npmjs.com/package/zustand-utils) — Utilities for Zustand — a `createContext` replacement, a devtools wrapper, and a store-updater factory function.\n- [zustand-valtio](https://github.com/zustandjs/zustand-valtio) — A sweet combination of Zustand and Valtio\n- [zustand-vue](https://github.com/AwesomeDevin/zustand-vue) — State management for vue (Vue3 / Vue2) based on zustand.\n- [zustand-x](https://github.com/udecode/zustand-x) — Zustand store factory for a best-in-class developer experience.\n- [zustand-xs](https://github.com/zustandjs/zustand-xs) — XState/store compabile middleware for Zustand\n- [zustand-yjs](https://github.com/tandem-pt/zustand-yjs) — Zustand stores for Yjs structures.\n- [zusteller](https://github.com/timkindberg/zusteller) — Your global state savior. \"Just hooks\" + Zustand.\n- [zustorm](https://github.com/mooalot/zustorm) — A simple and powerful form library for Zustand.\n- [zusty](https://github.com/oslabs-beta/Zusty) — Zustand tool to assist debugging with time travel, action logs, state snapshots, store view, render time metrics and state component tree.\n"
  },
  {
    "path": "docs/reference/middlewares/combine.md",
    "content": "---\ntitle: combine\ndescription: How to create a store and get types automatically inferred\nnav: 32\n---\n\n# combine\n\n`combine` middleware lets you create a cohesive state by merging an initial state with a state\ncreator function that adds new state slices and actions. This is really helpful as it automatically\ninfers types, so there’s no need for explicit type definitions.\n\n> [!TIP]\n> This makes state management more straightforward and efficient by making curried version of\n> `create` and `createStore` not necessary for middleware usage.\n\n```js\nconst nextStateCreatorFn = combine(initialState, additionalStateCreatorFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Creating a store with inferred types](#creating-a-store-with-inferred-types)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\ncombine<T, U>(initialState: T, additionalStateCreatorFn: StateCreator<T, [], [], U>): StateCreator<Omit<T, keyof U> & U, [], []>\n```\n\n## Reference\n\n### `combine(initialState, additionalStateCreatorFn)`\n\n#### Parameters\n\n- `initialState`: The value you want the state to be initially. It can be a value of any type,\n  except a function.\n- `additionalStateCreatorFn`: A function that takes `set` function, `get` function and `store` as\n  arguments. Usually, you will return an object with the methods you want to expose.\n\n#### Returns\n\n`combine` returns a state creator function.\n\n## Usage\n\n### Creating a store with inferred types\n\nThis example shows you how you can create a store and get types automatically inferred, so you\ndon’t need to define them explicitly.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { combine } from 'zustand/middleware'\n\nconst positionStore = createStore(\n  combine({ position: { x: 0, y: 0 } }, (set) => ({\n    setPosition: (position) => set({ position }),\n  })),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getInitialState(), positionStore.getInitialState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/middlewares/devtools.md",
    "content": "---\ntitle: devtools\ndescription: How to time-travel debug your store\nnav: 29\n---\n\n# devtools\n\n`devtools` middleware lets you use [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools)\nwithout Redux. Read more about the benefits of using [Redux DevTools for debugging](https://redux.js.org/style-guide/#use-the-redux-devtools-extension-for-debugging).\n\n> [!IMPORTANT]\n> In order to use `devtools` from `zustand/middleware` you need to install\n> `@redux-devtools/extension` library.\n\n```js\nconst nextStateCreatorFn = devtools(stateCreatorFn, devtoolsOptions)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n  - [Mutator](#mutator)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Debugging a store](#debugging-a-store)\n  - [Debugging a Slices pattern based store](#debugging-a-slices-pattern-based-store)\n  - [Filtering actions with actionsDenylist](#filtering-actions-with-actionsdenylist)\n  - [Cleanup](#cleanup)\n- [Troubleshooting](#troubleshooting)\n  - [Only one store is displayed](#only-one-store-is-displayed)\n  - [Action names are labeled as 'anonymous'](#all-action-names-are-labeled-as-'anonymous')\n\n## Types\n\n### Signature\n\n```ts\ndevtools<T>(stateCreatorFn: StateCreator<T, [], []>, devtoolsOptions?: DevtoolsOptions): StateCreator<T, [['zustand/devtools', never]], []>\n```\n\n### Mutator\n\n```ts\n;['zustand/devtools', never]\n```\n\n## Reference\n\n### `devtools(stateCreatorFn, devtoolsOptions)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n- **optional** `devtoolsOptions`: An object to define `Redux Devtools` options.\n  - **optional** `name`: A custom identifier for the connection in the Redux DevTools.\n  - **optional** `enabled`: Defaults to `true` when is on development mode, and defaults to `false`\n    when is on production mode. Enables or disables the Redux DevTools integration\n    for this store.\n  - **optional** `anonymousActionType`: Defaults to the inferred action type or `anonymous` if\n    unavailable. A string to use as the action type for anonymous mutations in the Redux DevTools.\n  - **optional** `store`: A custom identifier for the store in the Redux DevTools.\n  - **optional** `actionsDenylist`: A string or array of strings (regex patterns) that specify which\n    actions should be filtered out from Redux DevTools. This option is passed directly to Redux DevTools\n    for filtering. For example, `['secret.*']` will filter out all actions starting with \"secret\".\n\n#### Returns\n\n`devtools` returns a state creator function.\n\n## Usage\n\n### Debugging a store\n\nThis example shows you how you can use `Redux Devtools` to debug a store\n\n```ts\nimport { create, StateCreator } from 'zustand'\nimport { devtools } from 'zustand/middleware'\n\ntype JungleStore = {\n  bears: number\n  addBear: () => void\n  fishes: number\n  addFish: () => void\n}\n\nconst useJungleStore = create<JungleStore>()(\n  devtools((set) => ({\n    bears: 0,\n    addBear: () =>\n      set((state) => ({ bears: state.bears + 1 }), undefined, 'jungle/addBear'),\n    fishes: 0,\n    addFish: () =>\n      set(\n        (state) => ({ fishes: state.fishes + 1 }),\n        undefined,\n        'jungle/addFish',\n      ),\n  })),\n)\n```\n\n### Debugging a Slices pattern based store\n\nThis example shows you how you can use `Redux Devtools` to debug a Slices pattern based store\n\n```ts\nimport { create, StateCreator } from 'zustand'\nimport { devtools } from 'zustand/middleware'\n\ntype BearSlice = {\n  bears: number\n  addBear: () => void\n}\n\ntype FishSlice = {\n  fishes: number\n  addFish: () => void\n}\n\ntype JungleStore = BearSlice & FishSlice\n\nconst createBearSlice: StateCreator<\n  JungleStore,\n  [['zustand/devtools', never]],\n  [],\n  BearSlice\n> = (set) => ({\n  bears: 0,\n  addBear: () =>\n    set(\n      (state) => ({ bears: state.bears + 1 }),\n      undefined,\n      'jungle:bear/addBear',\n    ),\n})\n\nconst createFishSlice: StateCreator<\n  JungleStore,\n  [['zustand/devtools', never]],\n  [],\n  FishSlice\n> = (set) => ({\n  fishes: 0,\n  addFish: () =>\n    set(\n      (state) => ({ fishes: state.fishes + 1 }),\n      undefined,\n      'jungle:fish/addFish',\n    ),\n})\n\nconst useJungleStore = create<JungleStore>()(\n  devtools((...args) => ({\n    ...createBearSlice(...args),\n    ...createFishSlice(...args),\n  })),\n)\n```\n\n### Filtering actions with actionsDenylist\n\nYou can filter out specific actions from Redux DevTools using the `actionsDenylist` option. This is useful for hiding internal or sensitive actions from the DevTools timeline.\n\n```ts\nimport { create } from 'zustand'\nimport { devtools } from 'zustand/middleware'\n\ntype Store = {\n  user: string | null\n  token: string | null\n  login: (user: string, token: string) => void\n  logout: () => void\n  updateData: () => void\n}\n\nconst useStore = create<Store>()(\n  devtools(\n    (set) => ({\n      user: null,\n      token: null,\n      login: (user, token) => set({ user, token }, undefined, 'auth/login'),\n      logout: () => set({ user: null, token: null }, undefined, 'auth/logout'),\n      updateData: () =>\n        set({ user: 'updated' }, undefined, 'internal/updateData'),\n    }),\n    {\n      name: 'AuthStore',\n      // Filter out actions matching these regex patterns\n      actionsDenylist: ['internal/.*'], // Hides all 'internal/*' actions\n    },\n  ),\n)\n```\n\nYou can also use a single regex string:\n\n```ts\nconst useStore = create<Store>()(\n  devtools(\n    (set) => ({\n      // ... state and actions\n    }),\n    {\n      name: 'MyStore',\n      actionsDenylist: 'secret.*', // Hides all actions starting with 'secret'\n    },\n  ),\n)\n```\n\n> [!NOTE]\n> The `actionsDenylist` option uses regex pattern matching and is handled directly by Redux DevTools Extension.\n> All actions are still sent to DevTools, but matching actions are filtered from the display.\n\n### Cleanup\n\nWhen a store is no longer needed, you can clean up the Redux DevTools connection by calling the `cleanup` method on the store:\n\n```ts\nimport { create } from 'zustand'\nimport { devtools } from 'zustand/middleware'\n\nconst useStore = create(\n  devtools((set) => ({\n    count: 0,\n    increment: () => set((state) => ({ count: state.count + 1 })),\n  })),\n)\n\n// When you're done with the store, clean it up\nuseStore.devtools.cleanup()\n```\n\nThis is particularly useful in applications that wrap store in context or create multiple stores dynamically.\n\n## Troubleshooting\n\n### Only one store is displayed\n\nBy default, `Redux Devtools` only show one store at a time, so in order to see other stores you\nneed to use store selector and choose a different store.\n\n### All action names are labeled as 'anonymous'\n\nIf an action type name is not provided, it is defaulted to \"anonymous\". You can customize this\ndefault value by providing a `anonymousActionType` parameter:\n\nFor instance the next example doesn't have action type name:\n\n```ts\nimport { create, StateCreator } from 'zustand'\nimport { devtools } from 'zustand/middleware'\n\ntype BearSlice = {\n  bears: number\n  addBear: () => void\n}\n\ntype FishSlice = {\n  fishes: number\n  addFish: () => void\n}\n\ntype JungleStore = BearSlice & FishSlice\n\nconst createBearSlice: StateCreator<\n  JungleStore,\n  [['zustand/devtools', never]],\n  [],\n  BearSlice\n> = (set) => ({\n  bears: 0,\n  addBear: () => set((state) => ({ bears: state.bears + 1 })),\n  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),\n})\n\nconst createFishSlice: StateCreator<\n  JungleStore,\n  [['zustand/devtools', never]],\n  [],\n  FishSlice\n> = (set) => ({\n  fishes: 0,\n  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),\n})\n\nconst useJungleStore = create<JungleStore>()(\n  devtools((...args) => ({\n    ...createBearSlice(...args),\n    ...createFishSlice(...args),\n  })),\n)\n```\n\nIn order to fix the previous example, we need to provide an action type name as the third parameter.\nAdditionally, to preserve the default behavior of the replacement logic, the second parameter\nshould be set to `undefined`.\n\nHere's the fixed previous example\n\n```ts\nimport { create, StateCreator } from 'zustand'\n\ntype BearSlice = {\n  bears: number\n  addBear: () => void\n}\n\ntype FishSlice = {\n  fishes: number\n  addFish: () => void\n}\n\ntype JungleStore = BearSlice & FishSlice\n\nconst createBearSlice: StateCreator<\n  JungleStore,\n  [['zustand/devtools', never]],\n  [],\n  BearSlice\n> = (set) => ({\n  bears: 0,\n  addBear: () =>\n    set((state) => ({ bears: state.bears + 1 }), undefined, 'bear/addBear'),\n})\n\nconst createFishSlice: StateCreator<\n  JungleStore,\n  [['zustand/devtools', never]],\n  [],\n  FishSlice\n> = (set) => ({\n  fishes: 0,\n  addFish: () =>\n    set((state) => ({ fishes: state.fishes + 1 }), undefined, 'fish/addFish'),\n})\n\nconst useJungleStore = create<JungleStore>()(\n  devtools((...args) => ({\n    ...createBearSlice(...args),\n    ...createFishSlice(...args),\n  })),\n)\n```\n\n> [!IMPORTANT]\n> Do not set the second parameter to `true` or `false` unless you want to override the default\n> replacement logic\n"
  },
  {
    "path": "docs/reference/middlewares/immer.md",
    "content": "---\ntitle: immer\ndescription: How to perform immutable updates in a store without boilerplate code\nnav: 31\n---\n\n# immer\n\n`immer` middleware lets you perform immutable updates.\n\n> [!IMPORTANT]\n> In order to use `immer` from `zustand/middleware/immer` you need to install\n> `immer` library.\n\n```js\nconst nextStateCreatorFn = immer(stateCreatorFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n  - [Mutator](#mutator)\n- [Reference](#reference)\n- [Usage](#usage)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\nimmer<T>(stateCreatorFn: StateCreator<T, [], []>): StateCreator<T, [['zustand/immer', never]], []>\n```\n\n### Mutator\n\n```ts\n;['zustand/immer', never]\n```\n\n## Reference\n\n### `immer(stateCreatorFn)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n\n#### Returns\n\n`immer` returns a state creator function.\n\n## Usage\n\n### Updating state without boilerplate code\n\nIn the next example, we're going to update the `person` object. Since it's a nested object, we need\nto create a copy of the entire object before making the update.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (\n    nextPerson: (\n      person: PersonStoreState['person'],\n    ) => PersonStoreState['person'] | PersonStoreState['person'],\n  ) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst personStore = createStore<PersonStore>()((set) => ({\n  person: {\n    firstName: 'Barbara',\n    lastName: 'Hepworth',\n    email: 'bhepworth@sculpture.com',\n  },\n  setPerson: (nextPerson) =>\n    set((state) => ({\n      person:\n        typeof nextPerson === 'function'\n          ? nextPerson(state.person)\n          : nextPerson,\n    })),\n}))\n\nconst $firstNameInput = document.getElementById(\n  'first-name',\n) as HTMLInputElement\nconst $lastNameInput = document.getElementById('last-name') as HTMLInputElement\nconst $emailInput = document.getElementById('email') as HTMLInputElement\nconst $result = document.getElementById('result') as HTMLDivElement\n\nfunction handleFirstNameChange(event: Event) {\n  personStore.getState().setPerson((person) => ({\n    ...person,\n    firstName: (event.target as any).value,\n  }))\n}\n\nfunction handleLastNameChange(event: Event) {\n  personStore.getState().setPerson((person) => ({\n    ...person,\n    lastName: (event.target as any).value,\n  }))\n}\n\nfunction handleEmailChange(event: Event) {\n  personStore.getState().setPerson((person) => ({\n    ...person,\n    email: (event.target as any).value,\n  }))\n}\n\n$firstNameInput.addEventListener('input', handleFirstNameChange)\n$lastNameInput.addEventListener('input', handleLastNameChange)\n$emailInput.addEventListener('input', handleEmailChange)\n\nconst render: Parameters<typeof personStore.subscribe>[0] = (state) => {\n  $firstNameInput.value = state.person.firstName\n  $lastNameInput.value = state.person.lastName\n  $emailInput.value = state.person.email\n\n  $result.innerHTML = `${state.person.firstName} ${state.person.lastName} (${state.person.email})`\n}\n\nrender(personStore.getInitialState(), personStore.getInitialState())\n\npersonStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<label style=\"display: block\">\n  First name:\n  <input id=\"first-name\" />\n</label>\n<label style=\"display: block\">\n  Last name:\n  <input id=\"last-name\" />\n</label>\n<label style=\"display: block\">\n  Email:\n  <input id=\"email\" />\n</label>\n<p id=\"result\"></p>\n```\n\nTo avoid manually copying the entire object before making updates, we'll use the `immer`\nmiddleware.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { immer } from 'zustand/middleware/immer'\n\ntype PersonStoreState = {\n  person: { firstName: string; lastName: string; email: string }\n}\n\ntype PersonStoreActions = {\n  setPerson: (\n    nextPerson: (\n      person: PersonStoreState['person'],\n    ) => PersonStoreState['person'] | PersonStoreState['person'],\n  ) => void\n}\n\ntype PersonStore = PersonStoreState & PersonStoreActions\n\nconst personStore = createStore<PersonStore>()(\n  immer((set) => ({\n    person: {\n      firstName: 'Barbara',\n      lastName: 'Hepworth',\n      email: 'bhepworth@sculpture.com',\n    },\n    setPerson: (nextPerson) =>\n      set((state) => {\n        state.person =\n          typeof nextPerson === 'function'\n            ? nextPerson(state.person)\n            : nextPerson\n      }),\n  })),\n)\n\nconst $firstNameInput = document.getElementById(\n  'first-name',\n) as HTMLInputElement\nconst $lastNameInput = document.getElementById('last-name') as HTMLInputElement\nconst $emailInput = document.getElementById('email') as HTMLInputElement\nconst $result = document.getElementById('result') as HTMLDivElement\n\nfunction handleFirstNameChange(event: Event) {\n  personStore.getState().setPerson((person) => {\n    person.firstName = (event.target as any).value\n  })\n}\n\nfunction handleLastNameChange(event: Event) {\n  personStore.getState().setPerson((person) => {\n    person.lastName = (event.target as any).value\n  })\n}\n\nfunction handleEmailChange(event: Event) {\n  personStore.getState().setPerson((person) => {\n    person.email = (event.target as any).value\n  })\n}\n\n$firstNameInput.addEventListener('input', handleFirstNameChange)\n$lastNameInput.addEventListener('input', handleLastNameChange)\n$emailInput.addEventListener('input', handleEmailChange)\n\nconst render: Parameters<typeof personStore.subscribe>[0] = (state) => {\n  $firstNameInput.value = state.person.firstName\n  $lastNameInput.value = state.person.lastName\n  $emailInput.value = state.person.email\n\n  $result.innerHTML = `${state.person.firstName} ${state.person.lastName} (${state.person.email})`\n}\n\nrender(personStore.getInitialState(), personStore.getInitialState())\n\npersonStore.subscribe(render)\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/middlewares/persist.md",
    "content": "---\ntitle: persist\ndescription: How to persist a store\nnav: 28\n---\n\n# persist\n\n`persist` middleware lets you persist a store's state across page reloads or application\nrestarts.\n\n```js\nconst nextStateCreatorFn = persist(stateCreatorFn, persistOptions)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n  - [Mutator](#mutator)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Persisting a state](#persisting-a-state)\n  - [Persisting a state partially](#persisting-a-state-partially)\n  - [Persisting a state with custom storage](#persisting-a-state-with-custom-storage)\n  - [Persisting a state through versioning and migrations](#persisting-a-state-through-versioning-and-migrations)\n  - [Persisting a state with nested objects](#persisting-a-state-with-nested-objects)\n  - [Persisting a state and hydrate it manually](#persisting-a-state-and-hydrate-it-manually)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\npersist<T, U>(stateCreatorFn: StateCreator<T, [], []>, persistOptions?: PersistOptions<T, U>): StateCreator<T, [['zustand/persist', U]], []>\n```\n\n### Mutator\n\n```ts\n;['zustand/persist', U]\n```\n\n## Reference\n\n### `persist(stateCreatorFn)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n- `persistOptions`: An object to define storage options.\n  - `name`: A unique name of the item for your store in the storage.\n  - **optional** `storage`: Defaults to `createJSONStorage(() => localStorage)`.\n  - **optional** `partialize`: A function to filter state fields before persisting it.\n  - **optional** `onRehydrateStorage`: A function or function returning a function that allows\n    custom logic before and after state rehydration.\n  - **optional** `version`: A version number for the persisted state. If the stored state version\n    doesn't match, it won't be used.\n  - **optional** `migrate`: A function to migrate persisted state if the version mismatch occurs.\n  - **optional** `merge`: A function for custom logic when merging persisted state with the current\n    state during rehydration. Defaults to a shallow merge.\n  - **optional** `skipHydration`: Defaults to `false`. If `true`, the middleware won't\n    automatically rehydrate the state on initialization. Use `rehydrate` function manually in this\n    case. This is useful for server-side rendering (SSR) applications.\n\n#### Returns\n\n`persist` returns a state creator function.\n\n## Usage\n\n### Persisting a state\n\nIn this tutorial, we'll create a simple position tracker using vanilla store and the `persist`\nmiddleware. The example tracks the `position` of the mouse as it moves within a container and\nstores the `position` in local storage, so it persists even when the page reloads.\n\nWe start by setting up a vanilla store that holds the position (an object with `x` and `y`\ncoordinates) and an action to update it. We'll also use the `persist` middleware to store the\nposition in `localStorage`.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    { name: 'position-storage' },\n  ),\n)\n```\n\nNext, we'll track the mouse movements inside a div and update the store with the new position.\n\n```ts\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n```\n\nWe want to reflect the position updates on the screen by moving a div element\n(representing the dot) to the new coordinates.\n\n```ts\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere’s the complete code.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    { name: 'position-storage' },\n  ),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Persisting a state partially\n\nIn this tutorial, we'll create a simple position tracker using vanilla store and the `persist`\nmiddleware. Additionally, we'll show you how to persist only part of the state\n(partial persistence), which can be useful when you don’t want to store the entire state in\n`localStorage`.\n\nWe’ll first create a vanilla store that holds the position state and actions to update it. We'll\nuse the `persist` middleware to persist only the relevant part of the state (in this case, the\ncontext containing the position).\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\ntype PositionStoreState = {\n  context: {\n    position: { x: number; y: number }\n  }\n}\n\ntype PositionStoreActions = {\n  actions: {\n    setPosition: (\n      nextPosition: PositionStoreState['context']['position'],\n    ) => void\n  }\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      context: {\n        position: { x: 0, y: 0 },\n      },\n      actions: {\n        setPosition: (position) => set({ context: { position } }),\n      },\n    }),\n    {\n      name: 'position-storage',\n      partialize: (state) => ({ context: state.context }),\n    },\n  ),\n)\n```\n\nNext, we'll track the mouse movements inside a div and update the store with the new position.\n\n```ts\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().actions.setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n```\n\nWe want to reflect the position updates on the screen by moving a div element\n(representing the dot) to the new coordinates.\n\n```ts\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.context.position.x}px, ${state.context.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere’s the full code to create a dot that follows your mouse movement inside a container and\npersists the `context` in `localStorage`.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\ntype PositionStoreState = {\n  context: {\n    position: { x: number; y: number }\n  }\n}\n\ntype PositionStoreActions = {\n  actions: {\n    setPosition: (\n      nextPosition: PositionStoreState['context']['position'],\n    ) => void\n  }\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      context: {\n        position: { x: 0, y: 0 },\n      },\n      actions: {\n        setPosition: (position) => set({ context: { position } }),\n      },\n    }),\n    {\n      name: 'position-storage',\n      partialize: (state) => ({ context: state.context }),\n    },\n  ),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().actions.setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.context.position.x}px, ${state.context.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Persisting a state with custom storage\n\nIn this mini tutorial, we’ll create a simple position-tracking system using vanilla store, where\nthe position state is persisted in the URL's search parameters. This approach allows state\npersistence directly in the browser's URL, which can be useful for maintaining state across page\nreloads or sharing links with state embedded.\n\nWe need to implement functions to manipulate URL search parameters as if they were a storage\nmechanism. This includes retrieving, setting, and removing parameters.\n\n```ts\nconst getSearchParams = () => {\n  return new URL(location.href).searchParams\n}\n\nconst updateSearchParams = (searchParams: URLSearchParams) => {\n  window.history.replaceState(\n    {},\n    '',\n    `${location.pathname}?${searchParams.toString()}`,\n  )\n}\n\nconst getSearchParam = (key: string) => {\n  const searchParams = getSearchParams()\n  return searchParams.get(key)\n}\n\nconst updateSearchParam = (key: string, value: string) => {\n  const searchParams = getSearchParams()\n  searchParams.set(key, value)\n\n  updateSearchParams(searchParams)\n}\n\nconst removeSearchParam = (key: string) => {\n  const searchParams = getSearchParams()\n  searchParams.delete(key)\n\n  updateSearchParams(searchParams)\n}\n```\n\nTo use the URL search parameters as storage, we define a `searchParamsStorage` object with\n`getItem`, `setItem`, and `removeItem` methods. These methods map to our custom functions that\nmanipulate search parameters.\n\n```ts\nconst searchParamsStorage = {\n  getItem: (key: string) => getSearchParam(key),\n  setItem: (key: string, value: string) => updateSearchParam(key, value),\n  removeItem: (key: string) => removeSearchParam(key),\n}\n```\n\nNow, we initialize the vanilla store using the `persist` middleware, specifying that we want to use\nour custom storage. Instead of the default `localStorage` or `sessionStorage`, we’ll persist the\nposition data in the URL search parameters.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      storage: createJSONStorage(() => searchParamsStorage),\n    },\n  ),\n)\n```\n\nNext, we'll track the mouse movements inside a div and update the store with the new position.\n\n```ts\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n```\n\nWe want to reflect the position updates on the screen by moving a div element\n(representing the dot) to the new coordinates.\n\n```ts\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere’s the full code to create a dot that follows your mouse movement inside a container and\npersists the position in URL's search parameters.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist, createJSONStorage } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst getSearchParams = () => {\n  return new URL(location.href).searchParams\n}\n\nconst updateSearchParams = (searchParams: URLSearchParams) => {\n  window.history.replaceState(\n    {},\n    '',\n    `${location.pathname}?${searchParams.toString()}`,\n  )\n}\n\nconst getSearchParam = (key: string) => {\n  const searchParams = getSearchParams()\n  return searchParams.get(key)\n}\n\nconst updateSearchParam = (key: string, value: string) => {\n  const searchParams = getSearchParams()\n  searchParams.set(key, value)\n\n  updateSearchParams(searchParams)\n}\n\nconst removeSearchParam = (key: string) => {\n  const searchParams = getSearchParams()\n  searchParams.delete(key)\n\n  updateSearchParams(searchParams)\n}\n\nconst searchParamsStorage = {\n  getItem: (key: string) => getSearchParam(key),\n  setItem: (key: string, value: string) => updateSearchParam(key, value),\n  removeItem: (key: string) => removeSearchParam(key),\n}\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      storage: createJSONStorage(() => searchParamsStorage),\n    },\n  ),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Persisting a state through versioning and migrations\n\nIn this tutorial, we’ll explore how to manage state persistence using versioning and migration.\nWe will demonstrate how to evolve your state schema across versions without breaking existing\npersisted data.\n\nBefore moving to versioned state management, we simulate an initial state for `version` 0. This is\ndone by manually setting a `version` 0 state in `localStorage` if it doesn't already exist. The\n`version` 0 state saves the coordinates as `x` and `y` fields.\n\n```ts\n// For tutorial purposes only\nif (!localStorage.getItem('position-storage')) {\n  localStorage.setItem(\n    'position-storage',\n    JSON.stringify({\n      state: { x: 100, y: 100 }, // version 0 structure\n      version: 0,\n    }),\n  )\n}\n```\n\nNext, we use `persist` middleware to handle state persistence. We also add a migration function to\nhandle changes between versions. In this example, we `migrate` the state from `version` 0 (where\n`x` and `y` are separate) to `version` 1, where they are combined into a `position` object.\n\n```ts\nmigrate: (persisted: any, version) => {\n  if (version === 0) {\n    persisted.position = { x: persisted.x, y: persisted.y }\n    delete persisted.x\n    delete persisted.y\n  }\n\n  return persisted\n}\n```\n\nNext, we'll track the mouse movements inside a div and update the store with the new position.\n\n```ts\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n```\n\nWe want to reflect the position updates on the screen by moving a div element\n(representing the dot) to the new coordinates.\n\n```ts\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere’s the complete code.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\n// For tutorial purposes only\nif (!localStorage.getItem('position-storage')) {\n  localStorage.setItem(\n    'position-storage',\n    JSON.stringify({\n      state: { x: 100, y: 100 },\n      version: 0,\n    }),\n  )\n}\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 }, // version 0: just x: 0, y: 0\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      version: 1,\n      migrate: (persisted: any, version) => {\n        if (version === 0) {\n          persisted.position = { x: persisted.x, y: persisted.y }\n          delete persisted.x\n          delete persisted.y\n        }\n\n        return persisted\n      },\n    },\n  ),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Persisting a state with nested objects\n\nIn this tutorial, we’ll create a vanilla store that keeps track of a position represented by `x`\nand `y` coordinates. We will also implement persistence using `localStorage` and demonstrate how to\nhandle merging of state with potentially missing fields.\n\nTo simulate an initial state for the tutorial, we will check if our position data exists in\n`localStorage`. If it doesn't, we’ll set it up.\n\n```ts\nif (!localStorage.getItem('position-storage')) {\n  localStorage.setItem(\n    'position-storage',\n    JSON.stringify({\n      state: { position: { y: 100 } }, // missing `x` field\n      version: 0,\n    }),\n  )\n}\n```\n\nNow, we will create the store and configure it to use persistence and deep merging.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\nimport createDeepMerge from '@fastify/deepmerge'\n\nconst deepMerge = createDeepMerge({ all: true })\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      merge: (persisted, current) => deepMerge(current, persisted) as never,\n    },\n  ),\n)\n```\n\nNext, we'll track the mouse movements inside a div and update the store with the new position.\n\n```ts\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n```\n\nWe want to reflect the position updates on the screen by moving a div element\n(representing the dot) to the new coordinates.\n\n```ts\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere’s the complete code.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\nimport createDeepMerge from '@fastify/deepmerge'\n\nconst deepMerge = createDeepMerge({ all: true })\n\n// For tutorial purposes only\nif (!localStorage.getItem('position-storage')) {\n  localStorage.setItem(\n    'position-storage',\n    JSON.stringify({\n      state: { position: { y: 100 } }, // missing `x` field\n      version: 0,\n    }),\n  )\n}\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      merge: (persisted, current) => deepMerge(current, persisted) as never,\n    },\n  ),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  console.log({ state })\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n### Persisting a state and hydrate it manually\n\nIn this tutorial, we’ll create a vanilla store that keeps track of a position represented by `x`\nand `y` coordinates. We will also implement persistence using `localStorage` and explore how to\nskip the hydration process and manually trigger rehydration after a delay.\n\nWe start by setting up a vanilla store that holds the position (an object with `x` and `y`\ncoordinates) and an action to update it. Furthermore, we'll also use the `persist` middleware to\nstore the position in `localStorage` but skipping hydration.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      skipHydration: true,\n    },\n  ),\n)\n```\n\nSince we skipped hydration in the initial setup, we will manually rehydrate the state. Here, we’re\nusing `setTimeout` to simulate a delayed rehydration.\n\n```ts\nsetTimeout(() => {\n  positionStore.persist.rehydrate()\n}, 2000)\n```\n\nNext, we'll track the mouse movements inside a div and update the store with the new position.\n\n```ts\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n```\n\nWe want to reflect the position updates on the screen by moving a div element\n(representing the dot) to the new coordinates.\n\n```ts\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere’s the complete code.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { persist } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  persist(\n    (set) => ({\n      position: { x: 0, y: 0 },\n      setPosition: (position) => set({ position }),\n    }),\n    {\n      name: 'position-storage',\n      skipHydration: true,\n    },\n  ),\n)\n\nconst $dotContainer = document.getElementById('dot-container') as HTMLDivElement\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dotContainer.addEventListener('pointermove', (event) => {\n  positionStore.getState().setPosition({\n    x: event.clientX,\n    y: event.clientY,\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nsetTimeout(() => {\n  positionStore.persist.rehydrate()\n}, 2000)\n\nrender(positionStore.getState(), positionStore.getState())\n\npositionStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/middlewares/redux.md",
    "content": "---\ntitle: redux\ndescription: How to use actions and reducers in a store\nnav: 30\n---\n\n# redux\n\n`redux` middleware lets you update a store through actions and reducers just like redux.\n\n```js\nconst nextStateCreatorFn = redux(reducerFn, initialState)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n  - [Mutator](#mutator)\n- [Reference](#reference)\n- [Usage](#usage)\n  - [Updating state through actions and reducers](#updating-state-through-actions-and-reducers)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\nredux<T, A>(reducerFn: (state: T, action: A) => T, initialState: T): StateCreator<T & { dispatch: (action: A) => A }, [['zustand/redux', A]], []>\n```\n\n### Mutator\n\n```ts\n;['zustand/redux', A]\n```\n\n## Reference\n\n### `redux(reducerFn, initialState)`\n\n#### Parameters\n\n- `reducerFn`: It should be pure and should take the current state of your application and an action\n  object as arguments, and returns the new state resulting from applying the action.\n- `initialState`: The value you want the state to be initially. It can be a value of any type,\n  except a function.\n\n#### Returns\n\n`redux` returns a state creator function.\n\n## Usage\n\n### Updating state through actions and reducers\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { redux } from 'zustand/middleware'\n\ntype PersonStoreState = {\n  firstName: string\n  lastName: string\n  email: string\n}\n\ntype PersonStoreAction =\n  | { type: 'person/setFirstName'; firstName: string }\n  | { type: 'person/setLastName'; lastName: string }\n  | { type: 'person/setEmail'; email: string }\n\ntype PersonStore = PersonStoreState & {\n  dispatch: (action: PersonStoreAction) => PersonStoreAction\n}\n\nconst personStoreReducer = (\n  state: PersonStoreState,\n  action: PersonStoreAction,\n) => {\n  switch (action.type) {\n    case 'person/setFirstName': {\n      return { ...state, firstName: action.firstName }\n    }\n    case 'person/setLastName': {\n      return { ...state, lastName: action.lastName }\n    }\n    case 'person/setEmail': {\n      return { ...state, email: action.email }\n    }\n    default: {\n      return state\n    }\n  }\n}\n\nconst personStoreInitialState: PersonStoreState = {\n  firstName: 'Barbara',\n  lastName: 'Hepworth',\n  email: 'bhepworth@sculpture.com',\n}\n\nconst personStore = createStore<PersonStore>()(\n  redux(personStoreReducer, personStoreInitialState),\n)\n\nconst $firstNameInput = document.getElementById(\n  'first-name',\n) as HTMLInputElement\nconst $lastNameInput = document.getElementById('last-name') as HTMLInputElement\nconst $emailInput = document.getElementById('email') as HTMLInputElement\nconst $result = document.getElementById('result') as HTMLDivElement\n\nfunction handleFirstNameChange(event: Event) {\n  personStore.dispatch({\n    type: 'person/setFirstName',\n    firstName: (event.target as any).value,\n  })\n}\n\nfunction handleLastNameChange(event: Event) {\n  personStore.dispatch({\n    type: 'person/setLastName',\n    lastName: (event.target as any).value,\n  })\n}\n\nfunction handleEmailChange(event: Event) {\n  personStore.dispatch({\n    type: 'person/setEmail',\n    email: (event.target as any).value,\n  })\n}\n\n$firstNameInput.addEventListener('input', handleFirstNameChange)\n$lastNameInput.addEventListener('input', handleLastNameChange)\n$emailInput.addEventListener('input', handleEmailChange)\n\nconst render: Parameters<typeof personStore.subscribe>[0] = (person) => {\n  $firstNameInput.value = person.firstName\n  $lastNameInput.value = person.lastName\n  $emailInput.value = person.email\n\n  $result.innerHTML = `${person.firstName} ${person.lastName} (${person.email})`\n}\n\nrender(personStore.getInitialState(), personStore.getInitialState())\n\npersonStore.subscribe(render)\n```\n\nHere's the `html` code\n\n```html\n<label style=\"display: block\">\n  First name:\n  <input id=\"first-name\" />\n</label>\n<label style=\"display: block\">\n  Last name:\n  <input id=\"last-name\" />\n</label>\n<label style=\"display: block\">\n  Email:\n  <input id=\"email\" />\n</label>\n<p id=\"result\"></p>\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/middlewares/subscribe-with-selector.md",
    "content": "---\ntitle: subscribeWithSelector\ndescription: How to subscribe to granular store updates in a store\nnav: 33\n---\n\n# subscribeWithSelector\n\n`subscribeWithSelector` middleware lets you subscribe to specific data based on current state.\n\n```js\nconst nextStateCreatorFn = subscribeWithSelector(stateCreatorFn)\n```\n\n- [Types](#types)\n  - [Signature](#signature)\n  - [Mutator](#mutator)\n- [Reference](#reference)\n- [Usage](#usage)\n- [Troubleshooting](#troubleshooting)\n\n## Types\n\n### Signature\n\n```ts\nsubscribeWithSelector<T>(stateCreatorFn: StateCreator<T, [], []>): StateCreator<T, [['zustand/subscribeWithSelector', never]], []>\n```\n\n### Mutator\n\n```ts\n;['zustand/subscribeWithSelector', never]\n```\n\n## Reference\n\n### `subscribeWithSelector(stateCreatorFn)`\n\n#### Parameters\n\n- `stateCreatorFn`: A function that takes `set` function, `get` function and `store` as arguments.\n  Usually, you will return an object with the methods you want to expose.\n\n#### Returns\n\n`subscribeWithSelector` returns a state creator function.\n\n## Usage\n\n### Subscribing partial state updates\n\nBy subscribing to partial state updates, you register a callback that fires whenever the store's\npartial state updates. We can use `subscribe` for external state management.\n\n```ts\nimport { createStore } from 'zustand/vanilla'\nimport { subscribeWithSelector } from 'zustand/middleware'\n\ntype PositionStoreState = { position: { x: number; y: number } }\n\ntype PositionStoreActions = {\n  setPosition: (nextPosition: PositionStoreState['position']) => void\n}\n\ntype PositionStore = PositionStoreState & PositionStoreActions\n\nconst positionStore = createStore<PositionStore>()(\n  subscribeWithSelector((set) => ({\n    position: { x: 0, y: 0 },\n    setPosition: (position) => set({ position }),\n  })),\n)\n\nconst $dot = document.getElementById('dot') as HTMLDivElement\n\n$dot.addEventListener('mouseenter', (event) => {\n  const parent = event.currentTarget.parentElement\n  const parentWidth = parent.clientWidth\n  const parentHeight = parent.clientHeight\n\n  positionStore.getState().setPosition({\n    x: Math.ceil(Math.random() * parentWidth),\n    y: Math.ceil(Math.random() * parentHeight),\n  })\n})\n\nconst render: Parameters<typeof positionStore.subscribe>[0] = (state) => {\n  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`\n}\n\nrender(positionStore.getInitialState(), positionStore.getInitialState())\n\npositionStore.subscribe((state) => state.position, render)\n\nconst logger: Parameters<typeof positionStore.subscribe>[0] = (x) => {\n  console.log('new x position', { x })\n}\n\npositionStore.subscribe((state) => state.position.x, logger)\n```\n\nHere's the `html` code\n\n```html\n<div\n  id=\"dot-container\"\n  style=\"position: relative; width: 100vw; height: 100vh;\"\n>\n  <div\n    id=\"dot\"\n    style=\"position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;\"\n  ></div>\n</div>\n```\n\n## Troubleshooting\n\nTBD\n"
  },
  {
    "path": "docs/reference/migrations/migrating-to-v4.md",
    "content": "---\ntitle: Migrating to v4\nnav: 38\n---\n\nThe only breaking changes are in types.\nIf you are using Zustand with TypeScript\nor JSDoc type annotations,\nthis guide applies.\nOtherwise, no migration is required.\n\nAlso, it's recommended to first read\nthe new [TypeScript Guide](../../learn/guides/advanced-typescript.md),\nso that the migration is easier to understand.\n\nIn addition to this migration guide,\nyou can also check the\n[diff](https://github.com/pmndrs/zustand/compare/v3.7.2...v4.0.0?short_path=37e5b4c#diff-c21e24854115b390eccde717da83f91feb2d5927a76c1485e5f0fdd0135c2afa)\nof the test files in the Zustand repository from v3 to v4.\n\n## `create`\n\n**Applicable imports**\n\n```ts\nimport create from 'zustand'\nimport create from 'zustand/vanilla'\n```\n\n**Change**\n\n```diff\n- create:\n-   < State\n-   , StoreSetState = StoreApi<State>[\"set\"]\n-   , StoreGetState = StoreApi<State>[\"get\"]\n-   , Store = StoreApi<State>\n-   >\n-     (f: ...) => ...\n+ create:\n+   { <State>(): (f: ...) => ...\n+   , <State, Mutators>(f: ...) => ...\n+   }\n```\n\n**Migration**\n\nIf you are not passing any type parameters to `create`,\nno migration is required.\n\nIf you are using a \"leaf\" middleware like `combine` or `redux`,\nremove all type parameters from `create`.\n\nElse, replace `create<T, ...>(...)` with `create<T>()(...)`.\n\n## `StateCreator`\n\n**Applicable imports**\n\n```ts\nimport type { StateCreator } from 'zustand'\nimport type { StateCreator } from 'zustand/vanilla'\n```\n\n**Change**\n\n```diff\n- type StateCreator\n-   < State\n-   , StoreSetState = StoreApi<State>[\"set\"]\n-   , StoreGetState = StoreApi<State>[\"get\"]\n-   , Store = StoreApi<State>\n-   > =\n-     ...\n+ type StateCreator\n+   < State\n+   , InMutators extends [StoreMutatorIdentifier, unknown][] = []\n+   , OutMutators extends [StoreMutatorIdentifier, unknown][] = []\n+   , Return = State\n+   > =\n+     ...\n```\n\n**Migration**\n\nIf you are using `StateCreator`,\nyou are likely authoring a middleware\nor using the \"slices\" pattern.\nFor that check the\n[Authoring middlewares and advanced usage](../../learn/guides/advanced-typescript.md#authoring-middlewares-and-advanced-usage)\nand [Common recipes](../../learn/guides/advanced-typescript.md#common-recipes)\nsections of the TypeScript Guide.\n\n## `PartialState`\n\n**Applicable imports**\n\n```ts\nimport type { PartialState } from 'zustand'\nimport type { PartialState } from 'zustand/vanilla'\n```\n\n**Change**\n\n```diff\n- type PartialState\n-   < T extends State\n-   , K1 extends keyof T = keyof T\n-   , K2 extends keyof T = K1\n-   , K3 extends keyof T = K2\n-   , K4 extends keyof T = K3\n-   > =\n-   | (Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)\n-   | ((state: T) => Pick<T, K1> | Pick<T, K2> | Pick<T, K3> | Pick<T, K4> | T)\n+ type PartialState<T> =\n+   | Partial<T>\n+   | ((state: T) => Partial<T>)\n```\n\n**Migration**\n\nReplace `PartialState<T, ...>` with `PartialState<T>`\nand preferably turn on [`exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes)\nin your `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"exactOptionalPropertyTypes\": true\n  }\n}\n```\n\nWe're no longer using the trick to disallow `{ foo: undefined }`\nto be assigned to `Partial<{ foo: string }>`.\nInstead, we're relying on the users to turn on `exactOptionalPropertyTypes`.\n\n## `useStore`\n\n**Applicable imports**\n\n```ts\nimport { useStore } from 'zustand'\nimport { useStore } from 'zustand/react'\n```\n\n**Change**\n\n```diff\n- useStore:\n-   { <State>(store: StoreApi<State>): State\n-   , <State, StateSlice>\n-       ( store: StoreApi<State>\n-       , selector: StateSelector<State, StateSlice>,\n-       , equals?: EqualityChecker<StateSlice>\n-       ): StateSlice\n-   }\n+ useStore:\n+   <Store, StateSlice = ExtractState<Store>>\n+     ( store: Store\n+     , selector?: StateSelector<State, StateSlice>,\n+     , equals?: EqualityChecker<StateSlice>\n+     )\n+       => StateSlice\n```\n\n**Migration**\n\nIf you are not passing any type parameters to `useStore`,\nno migration is required.\n\nIf you are,\nit's recommended to remove all the type parameters,\nor pass the **store** type instead of the **state** type as the first parameter.\n\n## `UseBoundStore`\n\n**Applicable imports**\n\n```ts\nimport type { UseBoundStore } from 'zustand'\nimport type { UseBoundStore } from 'zustand/react'\n```\n\n**Change**\n\n```diff\n- type UseBoundStore<\n-   State,\n-   Store = StoreApi<State>\n- > =\n-   & { (): T\n-     , <StateSlice>\n-         ( selector: StateSelector<State, StateSlice>\n-         , equals?: EqualityChecker<StateSlice>\n-         ): U\n-     }\n-   & Store\n+ type UseBoundStore<Store> =\n+   & (<StateSlice = ExtractState<S>>\n+       ( selector?: (state: ExtractState<S>) => StateSlice\n+       , equals?: (a: StateSlice, b: StateSlice) => boolean\n+       ) => StateSlice\n+     )\n+   & S\n```\n\n**Migration**\n\nReplace `UseBoundStore<T>` with `UseBoundStore<StoreApi<T>>`,\nand `UseBoundStore<T, S>` with `UseBoundStore<S>`\n\n## `UseContextStore`\n\n**Applicable imports**\n\n```ts\nimport type { UseContextStore } from 'zustand/context'\n```\n\n**Change**\n\n```diff\n- type UseContextStore\n```\n\n**Migration**\n\nUse `typeof MyContext.useStore` instead\n\n## `createContext`\n\n**Applicable imports**\n\n```ts\nimport createContext from 'zustand/context'\n```\n\n**Change**\n\n```diff\n  createContext:\n-   <State, Store = StoreApi<State>>() => ...\n+   <Store>() => ...\n```\n\n**Migration**\n\nReplace `createContext<T>()` with `createContext<StoreApi<T>>()`,\nand `createContext<T, S>()` with `createContext<S>()`.\n\n## `combine`, `devtools`, `subscribeWithSelector`\n\n**Applicable imports**\n\n```ts\nimport { combine } from 'zustand/middleware'\nimport { devtools } from 'zustand/middleware'\nimport { subscribeWithSelector } from 'zustand/middleware'\n```\n\n**Change**\n\n```diff\n- combine:\n-   <T, U>(...) => ...\n+ combine:\n+   <T, U, Mps, Mcs>(...) => ...\n\n- devtools:\n-   <T>(...) => ...\n+ devtools:\n+   <T, Mps, Mcs>(...) => ...\n\n- subscribeWithSelector:\n-   <T>(...) => ...\n+ subscribeWithSelector:\n+   <T, Mps, Mcs>(...) => ...\n```\n\n**Migration**\n\nIf you are not passing any type parameters\nto `combine`, `devtools`, or `subscribeWithSelector`,\nno migration is required.\n\nIf you are,\nremove all the type parameters,\nas they are inferred automatically.\n\n## `persist`\n\n**Applicable imports**\n\n```ts\nimport { persist } from 'zustand/middleware'\n```\n\n**Change**\n\n```diff\n- persist:\n-   <T, U = Partial<T>>(...) => ...\n+ persist:\n+   <T, Mps, Mcs, U = T>(...) => ...\n```\n\n**Migration**\n\nIf you are passing any type parameters,\nremove them as they are inferred automatically.\n\nNext, if you are passing the `partialize` option,\nthere is no further steps required for migration.\n\nIf you are **not** passing the `partialize` option,\nyou might see some compilation errors.\nIf you do not see any,\nthere is no further migration required.\n\nThe type of partialized state is now `T` instead of `Partial<T>`,\nwhich aligns with the runtime behavior of the default `partialize`,\nwhich is an identity (`s => s`).\n\nIf you see some compilation errors,\nyou have to find and fix the errors yourself,\nbecause they might be indicative of unsound code.\nAlternatively, the workaround will be passing\n`s => s as Partial<typeof s>` to `partialize`.\nIf your partialized state is truly `Partial<T>`,\nyou should not encounter any bugs.\n\nThe runtime behavior has not changed,\nonly the types are now correct.\n\n## `redux`\n\n**Applicable imports**\n\n```ts\nimport { redux } from 'zustand/middleware'\n```\n\n**Change**\n\n```diff\n- redux:\n-   <T, A>(...) => ...\n+ redux:\n+   <T, A, Mps, Mcs>(...) => ...\n```\n\n**Migration**\n\nIf you are not passing any type parameters to `redux`,\nno migration is required.\n\nIf you are,\nremove all the type parameters,\nand annotate only the second (action) parameter.\nThat is, replace `redux<T, A>((state, action) => ..., ...)`\nwith `redux((state, action: A) => ..., ...)`.\n"
  },
  {
    "path": "docs/reference/migrations/migrating-to-v5.md",
    "content": "---\ntitle: How to Migrate to v5 from v4\nnav: 37\n---\n\n# How to Migrate to v5 from v4\n\nWe highly recommend to update to the latest version of v4, before migrating to v5. It will show all deprecation warnings without breaking your app.\n\n## Changes in v5\n\n- Drop default exports\n- Drop deprecated features\n- Make React 18 the minimum required version\n- Make use-sync-external-store a peer dependency (required for `createWithEqualityFn` and `useStoreWithEqualityFn` in `zustand/traditional`)\n- Make TypeScript 4.5 the minimum required version\n- Drop UMD/SystemJS support\n- Organize entry points in the package.json\n- Drop ES5 support\n- Stricter types when setState's replace flag is set\n- Persist middleware behavioral change\n- Other small improvements (technically breaking changes)\n\n## Migration Guide\n\n### Using custom equality functions such as `shallow`\n\nThe `create` function in v5 does not support customizing equality function.\n\nIf you use custom equality function such as `shallow`,\nthe easiest migration is to use `createWithEqualityFn`.\n\n```js\n// v4\nimport { create } from 'zustand'\nimport { shallow } from 'zustand/shallow'\n\nconst useCountStore = create((set) => ({\n  count: 0,\n  text: 'hello',\n  // ...\n}))\n\nconst Component = () => {\n  const { count, text } = useCountStore(\n    (state) => ({\n      count: state.count,\n      text: state.text,\n    }),\n    shallow,\n  )\n  // ...\n}\n```\n\nThat can be done with `createWithEqualityFn` in v5:\n\n```bash\nnpm install use-sync-external-store\n```\n\n```js\n// v5\nimport { createWithEqualityFn as create } from 'zustand/traditional'\n\n// The rest is the same as v4\n```\n\nAlternatively, for the `shallow` use case, you can use `useShallow` hook:\n\n```js\n// v5\nimport { create } from 'zustand'\nimport { useShallow } from 'zustand/shallow'\n\nconst useCountStore = create((set) => ({\n  count: 0,\n  text: 'hello',\n  // ...\n}))\n\nconst Component = () => {\n  const { count, text } = useCountStore(\n    useShallow((state) => ({\n      count: state.count,\n      text: state.text,\n    })),\n  )\n  // ...\n}\n```\n\n### Requiring stable selector outputs\n\nThere is a behavioral change in v5 to match React default behavior.\nIf a selector returns a new reference, it may cause infinite loops.\n\nFor example, this may cause infinite loops.\n\n```js\n// v4\nconst [searchValue, setSearchValue] = useStore((state) => [\n  state.searchValue,\n  state.setSearchValue,\n])\n```\n\nThe error message will be something like this:\n\n```plaintext\nUncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.\n```\n\nTo fix it, use the `useShallow` hook, which will return a stable reference.\n\n```js\n// v5\nimport { useShallow } from 'zustand/shallow'\n\nconst [searchValue, setSearchValue] = useStore(\n  useShallow((state) => [state.searchValue, state.setSearchValue]),\n)\n```\n\nHere's another example that may cause infinite loops.\n\n```js\n// v4\nconst action = useMainStore((state) => {\n  return state.action ?? () => {}\n})\n```\n\nTo fix it, make sure the selector function returns a stable reference.\n\n```js\n// v5\n\nconst FALLBACK_ACTION = () => {}\n\nconst action = useMainStore((state) => {\n  return state.action ?? FALLBACK_ACTION\n})\n```\n\nAlternatively, if you need v4 behavior, `createWithEqualityFn` will do.\n\n```js\n// v5\nimport { createWithEqualityFn as create } from 'zustand/traditional'\n```\n\n### Stricter types when setState's replace flag is set (Typescript only)\n\n```diff\n- setState:\n-   (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: boolean | undefined) => void;\n+ setState:\n+   (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false) => void;\n+   (state: T | ((state: T) => T), replace: true) => void;\n```\n\nIf you are not using the `replace` flag, no migration is required.\n\nIf you are using the `replace` flag and it's set to `true`, you must provide a complete state object.\nThis change ensures that `store.setState({}, true)` (which results in an invalid state) is no longer considered valid.\n\n**Examples:**\n\n```ts\n// Partial state update (valid)\nstore.setState({ key: 'value' })\n\n// Complete state replacement (valid)\nstore.setState({ key: 'value' }, true)\n\n// Incomplete state replacement (invalid)\nstore.setState({}, true) // Error\n```\n\n#### Handling Dynamic `replace` Flag\n\nIf the value of the `replace` flag is dynamic and determined at runtime, you might face issues. To handle this, you can use a workaround by annotating the `replace` parameter with the parameters of the `setState` function:\n\n```ts\nconst replaceFlag = Math.random() > 0.5\nconst args = [{ bears: 5 }, replaceFlag] as Parameters<\n  typeof useBearStore.setState\n>\nstore.setState(...args)\n```\n\n#### Persist middleware no longer stores item at store creation\n\nPreviously, the `persist` middleware stored the initial state during store creation. This behavior has been removed in v5 (and v4.5.5).\n\nFor example, in the following code, the initial state is stored in the storage.\n\n```js\n// v4\nimport { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\n\nconst useCountStore = create(\n  persist(\n    () => ({\n      count: Math.floor(Math.random() * 1000),\n    }),\n    {\n      name: 'count',\n    },\n  ),\n)\n```\n\nIn v5, this is no longer the case, and you need to explicitly set the state after store creation.\n\n```js\n// v5\nimport { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\n\nconst useCountStore = create(\n  persist(\n    () => ({\n      count: 0,\n    }),\n    {\n      name: 'count',\n    },\n  ),\n)\nuseCountStore.setState({\n  count: Math.floor(Math.random() * 1000),\n})\n```\n\n## Links\n\n- https://github.com/pmndrs/zustand/pull/2138\n- https://github.com/pmndrs/zustand/pull/2580\n"
  },
  {
    "path": "docs/reference/previous-versions/zustand-v3-create-context.md",
    "content": "---\ntitle: createContext from zustand/context\nnav: 39\n---\n\nA special `createContext` is provided since v3.5,\nwhich avoids misusing the store hook.\n\n> **Note**: This function is deprecated in v4 and will be removed in v5. See [Migration](#migration).\n\n```jsx\nimport create from 'zustand'\nimport createContext from 'zustand/context'\n\nconst { Provider, useStore } = createContext()\n\nconst createStore = () => create(...)\n\nconst App = () => (\n  <Provider createStore={createStore}>\n    ...\n  </Provider>\n)\n\nconst Component = () => {\n  const state = useStore()\n  const slice = useStore(selector)\n  ...\n```\n\n## createContext usage in real components\n\n```jsx\nimport create from \"zustand\";\nimport createContext from \"zustand/context\";\n\n// Best practice: You can move the below createContext() and createStore to a separate file(store.js) and import the Provider, useStore here/wherever you need.\n\nconst { Provider, useStore } = createContext();\n\nconst createStore = () =>\n  create((set) => ({\n    bears: 0,\n    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),\n    removeAllBears: () => set({ bears: 0 })\n  }));\n\nconst Button = () => {\n  return (\n      {/** store() - This will create a store for each time using the Button component instead of using one store for all components **/}\n    <Provider createStore={createStore}>\n      <ButtonChild />\n    </Provider>\n  );\n};\n\nconst ButtonChild = () => {\n  const state = useStore();\n  return (\n    <div>\n      {state.bears}\n      <button\n        onClick={() => {\n          state.increasePopulation();\n        }}\n      >\n        +\n      </button>\n    </div>\n  );\n};\n\nexport default function App() {\n  return (\n    <div className=\"App\">\n      <Button />\n      <Button />\n    </div>\n  );\n}\n```\n\n## createContext usage with initialization from props\n\n```tsx\nimport create from 'zustand'\nimport createContext from 'zustand/context'\n\nconst { Provider, useStore } = createContext()\n\nexport default function App({ initialBears }) {\n  return (\n    <Provider\n      createStore={() =>\n        create((set) => ({\n          bears: initialBears,\n          increase: () => set((state) => ({ bears: state.bears + 1 })),\n        }))\n      }\n    >\n      <Button />\n    </Provider>\n  )\n}\n```\n\n## Migration\n\nDiscussion: https://github.com/pmndrs/zustand/discussions/1276\n\nHere's the new context usage with v4 API.\n\n```jsx\nimport { createContext, useContext, useRef } from 'react'\nimport { createStore, useStore } from 'zustand'\n\nconst StoreContext = createContext(null)\n\nconst StoreProvider = ({ children }) => {\n  const storeRef = useRef()\n  if (storeRef.current === null) {\n    storeRef.current = createStore((set) => ({\n      // ...\n    }))\n  }\n  return (\n    <StoreContext.Provider value={storeRef.current}>\n      {children}\n    </StoreContext.Provider>\n  )\n}\n\nconst useStoreInContext = (selector) => {\n  const store = useContext(StoreContext)\n  if (!store) {\n    throw new Error('Missing StoreProvider')\n  }\n  return useStore(store, selector)\n}\n```\n\nOr reach out to some third-party libraries that provide Zustand v3-like APIs:\n\n- <https://github.com/charkour/zustand-di>\n- <https://github.com/arvinxx/zustand-utils>\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import eslint from '@eslint/js'\nimport vitest from '@vitest/eslint-plugin'\nimport { defineConfig, globalIgnores } from 'eslint/config'\nimport importPlugin from 'eslint-plugin-import'\nimport jestDom from 'eslint-plugin-jest-dom'\nimport react from 'eslint-plugin-react'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport testingLibrary from 'eslint-plugin-testing-library'\nimport tseslint from 'typescript-eslint'\n\nexport default defineConfig(\n  globalIgnores(['dist/', 'examples/', 'website/', 'coverage/']),\n  eslint.configs.recommended,\n  importPlugin.flatConfigs.recommended,\n  tseslint.configs.recommended,\n  react.configs.flat.recommended,\n  react.configs.flat['jsx-runtime'],\n  reactHooks.configs.flat.recommended,\n  {\n    languageOptions: {\n      parserOptions: {\n        project: true,\n      },\n    },\n    settings: {\n      react: {\n        version: 'detect',\n      },\n      'import/resolver': {\n        typescript: true,\n      },\n    },\n    rules: {\n      eqeqeq: 'error',\n      curly: ['warn', 'multi-line', 'consistent'],\n      'sort-imports': [\n        'error',\n        {\n          ignoreDeclarationSort: true,\n        },\n      ],\n      'import/no-unresolved': ['error', { commonjs: true, amd: true }],\n      'import/named': 'off',\n      'import/namespace': 'off',\n      'import/no-named-as-default-member': 'off',\n      'import/no-duplicates': 'error',\n      'import/extensions': ['error', 'always', { ignorePackages: true }],\n      'import/order': [\n        'error',\n        {\n          alphabetize: { order: 'asc', caseInsensitive: true },\n          groups: [\n            'builtin',\n            'external',\n            'internal',\n            'parent',\n            'sibling',\n            'index',\n            'object',\n          ],\n          'newlines-between': 'never',\n          pathGroups: [\n            {\n              pattern: 'react',\n              group: 'builtin',\n              position: 'before',\n            },\n          ],\n          pathGroupsExcludedImportTypes: ['builtin'],\n        },\n      ],\n      '@typescript-eslint/no-explicit-any': 'off',\n      '@typescript-eslint/no-unused-vars': [\n        'error',\n        { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },\n      ],\n    },\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    ...testingLibrary.configs['flat/react'],\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    ...jestDom.configs['flat/recommended'],\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    ...vitest.configs.recommended,\n    settings: { vitest: { typecheck: true } },\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    rules: {\n      'import/extensions': ['error', 'never'],\n      'vitest/consistent-test-it': [\n        'error',\n        { fn: 'it', withinDescribe: 'it' },\n      ],\n    },\n  },\n  {\n    files: ['*.config.*'],\n    languageOptions: {\n      parserOptions: {\n        project: null,\n      },\n    },\n  },\n)\n"
  },
  {
    "path": "examples/demo/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/demo/eslint.config.js",
    "content": "import eslint from '@eslint/js'\nimport react from 'eslint-plugin-react'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport reactRefresh from 'eslint-plugin-react-refresh'\nimport globals from 'globals'\n\nexport default [\n  {\n    ignores: ['dist'],\n  },\n  eslint.configs.recommended,\n  react.configs.flat.recommended,\n  react.configs.flat['jsx-runtime'],\n  {\n    languageOptions: {\n      globals: {\n        ...globals.browser,\n        ...globals.es2020,\n      },\n      parserOptions: {\n        ecmaVersion: 'latest',\n        sourceType: 'module',\n      },\n    },\n    plugins: {\n      'react-hooks': reactHooks,\n      'react-refresh': reactRefresh,\n    },\n    settings: {\n      react: {\n        version: 'detect',\n      },\n    },\n    rules: {\n      ...reactHooks.configs.recommended.rules,\n      'react-refresh/only-export-components': [\n        'warn',\n        { allowConstantExport: true },\n      ],\n      'react/prop-types': 'off',\n      'react/no-unknown-property': ['off'],\n    },\n  },\n]\n"
  },
  {
    "path": "examples/demo/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <meta\n      name=\"description\"\n      content=\"🐻 Bear necessities for state management in React\"\n    />\n    <link rel=\"apple-touch-icon\" href=\"/logo192.png\" />\n    <meta name=\"og:image\" content=\"/ogimage.jpg\" />\n    <link rel=\"manifest\" href=\"/manifest.json\" />\n    <title>Zustand</title>\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.jsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/demo/package.json",
    "content": "{\n  \"name\": \"demo\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"prettier\": {\n    \"semi\": false,\n    \"singleQuote\": true\n  },\n  \"dependencies\": {\n    \"@react-three/drei\": \"^9.78.2\",\n    \"@react-three/fiber\": \"^8.13.7\",\n    \"@react-three/postprocessing\": \"^2.14.13\",\n    \"@types/three\": \"^0.155.0\",\n    \"meshline\": \"^3.1.6\",\n    \"postprocessing\": \"^6.35.4\",\n    \"prism-react-renderer\": \"^2.0.6\",\n    \"prismjs\": \"^1.29.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"three\": \"^0.154.0\",\n    \"zustand\": \"^4.3.9\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.17.0\",\n    \"@types/react\": \"^18.2.14\",\n    \"@types/react-dom\": \"^18.2.6\",\n    \"@vitejs/plugin-react-swc\": \"^3.3.2\",\n    \"eslint\": \"^9.17.0\",\n    \"eslint-plugin-react\": \"^7.37.2\",\n    \"eslint-plugin-react-hooks\": \"^5.1.0\",\n    \"eslint-plugin-react-refresh\": \"^0.4.16\",\n    \"globals\": \"^15.14.0\",\n    \"vite\": \"^4.4.0\"\n  }\n}\n"
  },
  {
    "path": "examples/demo/public/manifest.json",
    "content": "{\n  \"short_name\": \"Zustand\",\n  \"name\": \"🐻 Bear necessities for state management in React\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    },\n    {\n      \"src\": \"logo192.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"192x192\"\n    },\n    {\n      \"src\": \"logo512.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "examples/demo/public/robots.txt",
    "content": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "examples/demo/src/App.jsx",
    "content": "import { create } from 'zustand'\nimport CodePreview from './components/CodePreview'\nimport Details from './components/Details'\nimport Scene from './components/Scene'\n\nconst useStore = create((set) => ({\n  count: 1,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n}))\n\nfunction Counter() {\n  const { count, inc } = useStore()\n  return (\n    <div className=\"counter\">\n      <span>{count}</span>\n      <button onClick={inc}>one up</button>\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <>\n      <Scene />\n      <div className=\"main\">\n        <div className=\"code\">\n          <div className=\"code-container\">\n            <CodePreview />\n            <Counter />\n          </div>\n        </div>\n        <Details />\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/components/CodePreview.jsx",
    "content": "import { create } from 'zustand'\nimport { Highlight } from 'prism-react-renderer'\nimport CopyButton from './CopyButton'\nimport SnippetLang from './SnippetLang'\nimport javascriptCode from '../resources/javascript-code'\nimport typescriptCode from '../resources/typescript-code'\n\nconst useStore = create((set, get) => ({\n  lang: 'javascript',\n  setLang: (lang) => set(() => ({ lang })),\n  getCode: () =>\n    get().lang === 'javascript' ? javascriptCode : typescriptCode,\n}))\n\nexport default function CodePreview() {\n  const { lang, setLang, getCode } = useStore()\n  const code = getCode()\n\n  return (\n    <Highlight code={code} language=\"tsx\" theme={undefined}>\n      {({ className, style, tokens, getLineProps, getTokenProps }) => (\n        // define how each line is to be rendered in the code block,\n        // position is set to relative so the copy button can align to bottom right\n        <pre className={className} style={{ ...style, position: 'relative' }}>\n          {tokens.map((line, i) => (\n            <div {...getLineProps({ line })} key={i}>\n              {line.map((token, key) => (\n                <span {...getTokenProps({ token })} key={key} />\n              ))}\n            </div>\n          ))}\n          <div className=\"snippet-container\">\n            <SnippetLang lang={lang} setLang={setLang} />\n            <CopyButton code={code} />\n          </div>\n        </pre>\n      )}\n    </Highlight>\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/components/CopyButton.jsx",
    "content": "import { useState, useCallback, useRef } from 'react'\nimport { copyToClipboard } from '../utils/copy-to-clipboard'\n\n/*\nIsolated logic for the entire copy functionality instead\nof a separate button component and with the added utility\n*/\nexport default function CopyButton({ code, ...props }) {\n  const [isCopied, setIsCopied] = useState(false)\n  const timer = useRef()\n\n  const handleCopy = useCallback(() => {\n    clearTimeout(timer.current)\n    copyToClipboard(code).then(() => {\n      setIsCopied(true)\n      timer.current = setTimeout(() => setIsCopied(false), 3000)\n    })\n  }, [code])\n\n  return (\n    <>\n      <button className=\"copy-button\" onClick={handleCopy} {...props}>\n        {isCopied ? (\n          'Copied!'\n        ) : (\n          <>\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width={16}\n              height={16}\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth={2}\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              {...props}\n            >\n              <rect x={9} y={9} width={13} height={13} rx={2} ry={2} />\n              <path d=\"M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1\" />\n            </svg>\n          </>\n        )}\n      </button>\n    </>\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/components/Details.jsx",
    "content": "export default function Details() {\n  return (\n    <>\n      <nav className=\"nav\">\n        <a href=\"https://zustand.docs.pmnd.rs/\">Documentation</a>\n        <a href=\"https://github.com/pmndrs/zustand\">Github</a>\n      </nav>\n      <div className=\"bottom\">\n        <a href=\"https://www.instagram.com/tina.henschel/\">\n          Illustrations @ Tina Henschel\n        </a>\n        <div className=\"bottom-links\">\n          <a href=\"https://github.com/pmndrs/zustand/tree/main/examples/demo\">\n            {'<Source />'}\n          </a>\n          <a href=\"https://stackblitz.com/github/pmndrs/zustand/tree/main/examples/starter?file=src%2Findex.tsx\">\n            {'<Playground />'}\n          </a>\n        </div>\n      </div>\n      <span className=\"header-left\">Zustand</span>\n    </>\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/components/Fireflies.jsx",
    "content": "import { Vector3, CatmullRomCurve3 } from 'three'\nimport { useRef, useMemo } from 'react'\nimport { extend, useFrame } from '@react-three/fiber'\nimport * as meshline from 'meshline'\n\nextend(meshline)\n\nconst r = () => Math.max(0.2, Math.random())\n\nfunction Fatline({ curve, color }) {\n  const material = useRef()\n  useFrame(\n    (state, delta) =>\n      (material.current.uniforms.dashOffset.value -= delta / 100),\n  )\n  return (\n    <mesh>\n      <meshLineGeometry points={curve} />\n      <meshLineMaterial\n        ref={material}\n        transparent\n        lineWidth={0.01}\n        color={color}\n        dashArray={0.1}\n        dashRatio={0.99}\n      />\n    </mesh>\n  )\n}\n\nexport default function Fireflies({ count, colors, radius = 10 }) {\n  const lines = useMemo(\n    () =>\n      new Array(count).fill().map(() => {\n        const pos = new Vector3(\n          Math.sin(0) * radius * r(),\n          Math.cos(0) * radius * r(),\n          0,\n        )\n        const points = new Array(30).fill().map((_, index) => {\n          const angle = (index / 20) * Math.PI * 2\n          return pos\n            .add(\n              new Vector3(\n                Math.sin(angle) * radius * r(),\n                Math.cos(angle) * radius * r(),\n                0,\n              ),\n            )\n            .clone()\n        })\n        const curve = new CatmullRomCurve3(points).getPoints(100)\n        return {\n          color: colors[parseInt(colors.length * Math.random())],\n          curve,\n        }\n      }),\n    [count, radius, colors],\n  )\n  return (\n    <group position={[-radius * 2, -radius, 0]}>\n      {lines.map((props, index) => (\n        <Fatline key={index} {...props} />\n      ))}\n    </group>\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/components/Scene.jsx",
    "content": "import { Mesh, PlaneGeometry, Group, Vector3, MathUtils } from 'three'\nimport { useRef, useState, useLayoutEffect } from 'react'\nimport { createRoot, events, extend, useFrame } from '@react-three/fiber'\nimport { Plane, useAspect, useTexture } from '@react-three/drei'\nimport {\n  EffectComposer,\n  DepthOfField,\n  Vignette,\n} from '@react-three/postprocessing'\nimport { MaskFunction } from 'postprocessing'\nimport Fireflies from './Fireflies'\nimport bgUrl from '../resources/bg.jpg'\nimport starsUrl from '../resources/stars.png'\nimport groundUrl from '../resources/ground.png'\nimport bearUrl from '../resources/bear.png'\nimport leaves1Url from '../resources/leaves1.png'\nimport leaves2Url from '../resources/leaves2.png'\nimport '../materials/layerMaterial'\n\nfunction Experience() {\n  const scaleN = useAspect(1600, 1000, 1.05)\n  const scaleW = useAspect(2200, 1000, 1.05)\n  const textures = useTexture([\n    bgUrl,\n    starsUrl,\n    groundUrl,\n    bearUrl,\n    leaves1Url,\n    leaves2Url,\n  ])\n  const group = useRef()\n  const layersRef = useRef([])\n  const [movement] = useState(() => new Vector3())\n  const [temp] = useState(() => new Vector3())\n  const layers = [\n    { texture: textures[0], x: 0, y: 0, z: 0, factor: 0.005, scale: scaleW },\n    { texture: textures[1], x: 0, y: 0, z: 10, factor: 0.005, scale: scaleW },\n    { texture: textures[2], x: 0, y: 0, z: 20, scale: scaleW },\n    {\n      texture: textures[3],\n      x: 0,\n      y: 0,\n      z: 30,\n      scaleFactor: 0.83,\n      scale: scaleN,\n    },\n    {\n      texture: textures[4],\n      x: 0,\n      y: 0,\n      z: 40,\n      factor: 0.03,\n      scaleFactor: 1,\n      wiggle: 0.6,\n      scale: scaleW,\n    },\n    {\n      texture: textures[5],\n      x: -20,\n      y: -20,\n      z: 49,\n      factor: 0.04,\n      scaleFactor: 1.3,\n      wiggle: 1,\n      scale: scaleW,\n    },\n  ]\n\n  useFrame((state, delta) => {\n    movement.lerp(temp.set(state.pointer.x, state.pointer.y * 0.2, 0), 0.2)\n    group.current.position.x = MathUtils.lerp(\n      group.current.position.x,\n      state.pointer.x * 20,\n      0.05,\n    )\n    group.current.rotation.x = MathUtils.lerp(\n      group.current.rotation.x,\n      state.pointer.y / 20,\n      0.05,\n    )\n    group.current.rotation.y = MathUtils.lerp(\n      group.current.rotation.y,\n      -state.pointer.x / 2,\n      0.05,\n    )\n    layersRef.current[4].uniforms.time.value =\n      layersRef.current[5].uniforms.time.value += delta\n  }, 1)\n\n  return (\n    <group ref={group}>\n      <Fireflies count={20} radius={80} colors={['orange']} />\n      {layers.map(\n        (\n          {\n            scale,\n            texture,\n            ref,\n            factor = 0,\n            scaleFactor = 1,\n            wiggle = 0,\n            x,\n            y,\n            z,\n          },\n          i,\n        ) => (\n          <Plane\n            scale={scale}\n            args={[1, 1, wiggle ? 10 : 1, wiggle ? 10 : 1]}\n            position={[x, y, z]}\n            key={i}\n            ref={ref}\n          >\n            <layerMaterial\n              movement={movement}\n              textr={texture}\n              factor={factor}\n              ref={(el) => (layersRef.current[i] = el)}\n              wiggle={wiggle}\n              scale={scaleFactor}\n            />\n          </Plane>\n        ),\n      )}\n    </group>\n  )\n}\n\nfunction Effects() {\n  const ref = useRef()\n  useLayoutEffect(() => {\n    const maskMaterial = ref.current.maskPass.getFullscreenMaterial()\n    maskMaterial.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA\n  })\n  return (\n    <EffectComposer disableNormalPass multisampling={0}>\n      <DepthOfField\n        ref={ref}\n        target={[0, 0, 30]}\n        bokehScale={8}\n        focalLength={0.1}\n        width={1024}\n      />\n      <Vignette />\n    </EffectComposer>\n  )\n}\n\nfunction FallbackScene() {\n  return (\n    <div\n      style={{\n        position: 'absolute',\n        top: 0,\n        left: 0,\n        width: '100%',\n        height: '100%',\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        background: '#010101',\n      }}\n    >\n      <img\n        src=\"/ogimage.jpg\"\n        alt=\"Zustand Bear\"\n        style={{\n          width: '100%',\n          height: '100%',\n          objectFit: 'cover',\n        }}\n      />\n    </div>\n  )\n}\n\nexport default function Scene() {\n  const [error, setError] = useState(null)\n\n  if (error) {\n    return <FallbackScene />\n  }\n\n  return (\n    <Canvas onError={setError}>\n      <Experience />\n      <Effects />\n    </Canvas>\n  )\n}\n\nfunction Canvas({ children, onError }) {\n  extend({ Mesh, PlaneGeometry, Group })\n  const canvas = useRef(null)\n  const root = useRef(null)\n  useLayoutEffect(() => {\n    try {\n      if (!root.current) {\n        root.current = createRoot(canvas.current).configure({\n          events,\n          orthographic: true,\n          gl: { antialias: false },\n          camera: { zoom: 5, position: [0, 0, 200], far: 300, near: 50 },\n          onCreated: (state) => {\n            state.events.connect(document.getElementById('root'))\n            state.setEvents({\n              compute: (event, state) => {\n                state.pointer.set(\n                  (event.clientX / state.size.width) * 2 - 1,\n                  -(event.clientY / state.size.height) * 2 + 1,\n                )\n                state.raycaster.setFromCamera(state.pointer, state.camera)\n              },\n            })\n          },\n        })\n      }\n      const resize = () =>\n        root.current.configure({\n          width: window.innerWidth,\n          height: window.innerHeight,\n        })\n      window.addEventListener('resize', resize)\n      root.current.render(children)\n      return () => window.removeEventListener('resize', resize)\n    } catch (e) {\n      onError?.(e)\n    }\n  }, [children, onError])\n\n  return (\n    <canvas\n      ref={canvas}\n      style={{\n        position: 'relative',\n        width: '100%',\n        height: '100%',\n        overflow: 'hidden',\n        display: 'block',\n      }}\n    />\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/components/SnippetLang.jsx",
    "content": "export default function SnippetLang({ lang, setLang }) {\n  return (\n    <select\n      className=\"snippet-lang\"\n      value={lang}\n      onChange={(e) => setLang(e.currentTarget.value)}\n    >\n      <option value=\"javascript\">JavaScript</option>\n      <option value=\"typescript\">TypeScript</option>\n    </select>\n  )\n}\n"
  },
  {
    "path": "examples/demo/src/main.jsx",
    "content": "import { createRoot } from 'react-dom/client'\nimport './styles.css'\nimport './pmndrs.css'\nimport App from './App'\n\ncreateRoot(document.getElementById('root')).render(<App />)\n"
  },
  {
    "path": "examples/demo/src/materials/layerMaterial.js",
    "content": "import { shaderMaterial } from '@react-three/drei'\nimport { extend } from '@react-three/fiber'\n\n// This material takes care of wiggling and punches a hole into\n// alpha regions so that the depth-of-field effect can process the layers.\n// Credit: Gianmarco Simone https://twitter.com/ggsimm\n\nconst LayerMaterial = shaderMaterial(\n  { textr: null, movement: [0, 0, 0], scale: 1, factor: 0, wiggle: 0, time: 0 },\n  ` uniform float time;\n    uniform vec2 resolution;\n    uniform float wiggle;\n    varying vec2 vUv;\n    varying vec3 vNormal;\n    void main()\t{\n      vUv = uv;\n      vec3 transformed = vec3(position);\n      if (wiggle > 0.) {\n        float theta = sin(time + position.y) / 2.0 * wiggle;\n        float c = cos(theta);\n        float s = sin(theta);\n        mat3 m = mat3(c, 0, s, 0, 1, 0, -s, 0, c);\n        transformed = transformed * m;\n        vNormal = vNormal * m;\n      }\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.);\n    }`,\n  ` uniform float time;\n    uniform vec2 resolution;\n    uniform float factor;\n    uniform float scale;\n    uniform vec3 movement;\n    uniform sampler2D textr;\n    varying vec2 vUv;\n    void main()\t{\n      vec2 uv = vUv / scale + movement.xy * factor;\n      vec4 color = texture2D(textr, uv);\n      if (color.a < 0.1) discard;\n      gl_FragColor = vec4(color.rgb, .1);\n      #include <tonemapping_fragment>\n      #include <colorspace_fragment>\n    }`,\n)\n\nextend({ LayerMaterial })\n"
  },
  {
    "path": "examples/demo/src/pmndrs.css",
    "content": "/**\n * Pmndrs theme for JavaScript, CSS and HTML\n * Loosely based on https://marketplace.visualstudio.com/items?itemName=pmndrs.pmndrs\n * @author Paul Henschel\n */\n\ncode[class*='language-'],\npre[class*='language-'] {\n  color: #e4f0fb !important;\n  background: none !important;\n  text-shadow: 0 1px rgba(0, 0, 0, 0.3) !important;\n  font-family: Menlo, Monaco, 'Courier New', monospace !important;\n  font-size: 0.95em !important;\n  text-align: left !important;\n  white-space: pre !important;\n  word-spacing: normal !important;\n  word-break: normal !important;\n  word-wrap: normal !important;\n  line-height: 1.5 !important;\n\n  -moz-tab-size: 4 !important;\n  -o-tab-size: 4 !important;\n  tab-size: 4 !important;\n\n  -webkit-hyphens: none !important;\n  -moz-hyphens: none !important;\n  -ms-hyphens: none !important;\n  hyphens: none !important;\n}\n\n/* Code blocks */\npre[class*='language-'] {\n  padding: 3.75em !important;\n  margin: -2.5em 0 !important;\n  overflow: auto !important;\n  border-radius: 0.75em !important;\n}\n\n:not(pre) > code[class*='language-'],\npre[class*='language-'] {\n  background: #252b37 !important;\n}\n\n/* Inline code */\n:not(pre) > code[class*='language-'] {\n  padding: 0.1em !important;\n  border-radius: 0.3em !important;\n  white-space: normal !important;\n}\n\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #a6accd !important;\n}\n\n.token.punctuation {\n  color: #e4f0fb !important;\n}\n\n.token.namespace {\n  opacity: 0.7 !important;\n}\n\n.token.property,\n.token.tag,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #e4f0fb !important;\n}\n\n.token.boolean,\n.token.number {\n  color: #5de4c7 !important;\n}\n\n.token.selector,\n.token.attr-value,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #5de4c7 !important;\n}\n\n.token.attr-name,\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string,\n.token.variable {\n  color: #add7ff !important;\n}\n\n.token.atrule,\n.token.function,\n.token.class-name {\n  color: #5de4c7 !important;\n}\n\n.token.keyword {\n  color: #add7ff !important;\n}\n\n.token.regex,\n.token.important {\n  color: #fffac2 !important;\n}\n\n.token.important,\n.token.bold {\n  font-weight: bold !important;\n}\n.token.italic {\n  font-style: italic !important;\n}\n\n.token.entity {\n  cursor: help !important;\n}\n"
  },
  {
    "path": "examples/demo/src/resources/javascript-code.js",
    "content": "export default `import { create } from 'zustand'\n\nconst useStore = create((set) => ({\n  count: 1,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n}))\n\nfunction Counter() {\n  const { count, inc } = useStore()\n  return (\n    <div>\n      <span>{count}</span>\n      <button onClick={inc}>one up</button>\n    </div>\n  )\n}`\n"
  },
  {
    "path": "examples/demo/src/resources/typescript-code.js",
    "content": "export default `import { create } from 'zustand'\n\ntype Store = {\n  count: number\n  inc: () => void\n}\n\nconst useStore = create<Store>()((set) => ({\n  count: 1,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n}))\n\nfunction Counter() {\n  const { count, inc } = useStore()\n  return (\n    <div>\n      <span>{count}</span>\n      <button onClick={inc}>one up</button>\n    </div>\n  )\n}`\n"
  },
  {
    "path": "examples/demo/src/styles.css",
    "content": "* {\n  box-sizing: border-box;\n}\n\nhtml,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  -webkit-touch-callout: none;\n  overflow: hidden;\n  background: #010101;\n}\n\n#root {\n  overflow: hidden;\n}\n\nbody {\n  font-family:\n    -apple-system,\n    BlinkMacSystemFont,\n    avenir next,\n    avenir,\n    helvetica neue,\n    helvetica,\n    ubuntu,\n    roboto,\n    noto,\n    segoe ui,\n    arial,\n    sans-serif;\n}\n\n.main {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  color: white;\n}\n\n.main > .code {\n  position: absolute;\n  right: 10vw;\n  margin-right: -60px;\n  width: 640px;\n  max-width: 80%;\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.code-container {\n  position: relative;\n  margin-bottom: -60px;\n}\n\n.counter {\n  position: absolute;\n  top: -100px;\n  right: -20px;\n  color: white;\n  background: #394a52;\n  padding: 40px;\n  border-radius: 10px;\n  box-shadow: 0 16px 40px -5px rgba(0, 0, 0, 0.5);\n  width: 120px;\n  height: 120px;\n  font-size: 3em;\n}\n\n.counter > span {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  margin-top: -15px;\n  transform: translate3d(-50%, -50%, 0);\n}\n\n.counter > button {\n  margin: 10px;\n  padding: 5px 10px;\n  position: absolute;\n  left: 0;\n  bottom: 0;\n  width: 100px;\n  border-radius: 5px;\n  border: solid 2px white;\n  outline: none;\n  background: transparent;\n  color: white;\n  cursor: pointer;\n}\n\n.code-container pre[class*='language-'] {\n  margin-top: -50px;\n  display: inline-block;\n  width: auto !important;\n  padding: 40px 50px 40px 45px;\n  font-size: 0.8rem !important;\n  border-radius: 10px !important;\n  box-shadow: 0 16px 40px -5px rgba(0, 0, 0, 1);\n  white-space: pre-wrap !important;\n}\n\nspan.header-left {\n  font-weight: 700;\n  text-transform: uppercase;\n  position: absolute;\n  display: inline-block;\n  top: 40px;\n  left: 40px;\n  font-size: 3em;\n  color: white;\n  line-height: 1em;\n}\n\na {\n  font-family:\n    -apple-system,\n    BlinkMacSystemFont,\n    avenir next,\n    avenir,\n    helvetica neue,\n    helvetica,\n    ubuntu,\n    roboto,\n    noto,\n    segoe ui,\n    arial,\n    sans-serif;\n  font-weight: 400;\n  font-size: 16px;\n  color: inherit;\n  position: absolute;\n  display: inline;\n  text-decoration: none;\n}\n\n.nav {\n  align-items: center;\n  display: flex;\n  gap: 16px;\n  justify-content: flex-end;\n  left: 40px;\n  position: fixed;\n  right: 40px;\n  top: 40px;\n}\n\n.nav a {\n  position: relative;\n  flex: 0 0 auto;\n}\n\n.bottom {\n  position: fixed;\n  bottom: 40px;\n  left: 40px;\n  right: 40px;\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n}\n\n.bottom a {\n  position: static;\n}\n\n.bottom-links {\n  display: flex;\n  gap: 16px;\n  align-items: center;\n}\n\n.snippet-container {\n  display: flex;\n  align-items: center;\n  gap: 4px;\n  position: absolute;\n  bottom: 0;\n  right: 0;\n  padding: 5px;\n}\n\n.snippet-lang {\n  background-color: #272822;\n  color: #fff;\n  outline: 0;\n  border: 0;\n}\n\n.copy-button {\n  box-shadow: none;\n  text-decoration: none;\n  font-size: 14px;\n  font-family: sans-serif;\n  line-height: 1;\n  padding: 12px;\n  width: auto;\n  border-radius: 5px;\n  border: 0;\n  outline: none;\n  background: transparent;\n  color: #f8f9fa;\n  cursor: pointer;\n}\n\n.copy-button:hover {\n  background-color: #5f5e5d;\n}\n\n@media only screen and (max-width: 700px) {\n  span.header-left {\n    font-size: 1em;\n  }\n  .main > .code {\n    margin-right: -0px;\n  }\n  .code-container > pre[class*='language-'] {\n    font-size: 0.6rem !important;\n    border-radius: 10px 10px 0 0 !important;\n  }\n  .counter {\n    position: absolute;\n    top: -120px;\n  }\n}\n"
  },
  {
    "path": "examples/demo/src/utils/copy-to-clipboard.js",
    "content": "export const copyToClipboard = (str) => {\n  return navigator.clipboard.writeText(str)\n}\n"
  },
  {
    "path": "examples/demo/vite.config.js",
    "content": "import react from '@vitejs/plugin-react-swc'\nimport { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/starter/README.md",
    "content": "# Starter [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/zustand/tree/main/examples/starter)\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/zustand\n\n# install project dependencies & build the library\ncd zustand && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/starter && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/zustand/tree/main/examples/starter\n"
  },
  {
    "path": "examples/starter/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta name=\"description\" content=\"zustand examples\" />\n    <title>Zustand Examples | Starter</title>\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/starter/package.json",
    "content": "{\n  \"name\": \"starter\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"zustand\": \"^5.0.2\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react-swc\": \"^3.5.0\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^5.3.4\"\n  }\n}\n"
  },
  {
    "path": "examples/starter/src/index.css",
    "content": "html,\nbody,\n#root {\n  height: 100%;\n}\n\n#root {\n  display: flex;\n  place-items: center;\n  justify-content: center;\n\n  color: #fff;\n  background-color: #131311;\n}\n"
  },
  {
    "path": "examples/starter/src/index.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { create } from 'zustand'\n\nimport mascot from './assets/zustand-mascot.svg'\n\nimport './index.css'\n\ntype Store = {\n  count: number\n  inc: () => void\n}\n\nconst useStore = create<Store>((set) => ({\n  count: 0,\n  inc: () => set((state) => ({ count: state.count + 1 })),\n}))\n\nconst Counter = () => {\n  const count = useStore((s) => s.count)\n  const inc = useStore((s) => s.inc)\n\n  return (\n    <>\n      <span className=\"text-3xl\">{count}</span>\n      <button\n        className=\"bg-[#252b37] font-bold py-2 px-4 rounded\"\n        onClick={inc}\n      >\n        +1\n      </button>\n    </>\n  )\n}\n\nfunction App() {\n  return (\n    <div className=\"grid place-items-center gap-6\">\n      <a href=\"https://zustand-demo.pmnd.rs/\" target=\"_blank\" rel=\"noreferrer\">\n        <img\n          src={mascot}\n          alt=\"Zustand mascot\"\n          className=\"w-36\"\n          style={{\n            filter: 'drop-shadow(0 0 2em #582d3e)',\n          }}\n        />\n      </a>\n\n      <h1 className=\"text-5xl font-bold\">Zustand Starter</h1>\n\n      <Counter />\n    </div>\n  )\n}\n\ncreateRoot(document.getElementById('root')!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/starter/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/starter/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2019\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"moduleResolution\": \"node\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"jsx\": \"react-jsx\",\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"module\": \"esnext\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"vite.config.ts\", \"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/starter/vite.config.ts",
    "content": "import react from '@vitejs/plugin-react-swc'\nimport { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"zustand\",\n  \"description\": \"🐻 Bear necessities for state management in React\",\n  \"private\": true,\n  \"type\": \"commonjs\",\n  \"version\": \"5.0.12\",\n  \"main\": \"./index.js\",\n  \"types\": \"./index.d.ts\",\n  \"typesVersions\": {\n    \">=4.5\": {\n      \"esm/*\": [\n        \"esm/*\"\n      ],\n      \"*\": [\n        \"*\"\n      ]\n    },\n    \"*\": {\n      \"esm/*\": [\n        \"ts_version_4.5_and_above_is_required.d.ts\"\n      ],\n      \"*\": [\n        \"ts_version_4.5_and_above_is_required.d.ts\"\n      ]\n    }\n  },\n  \"exports\": {\n    \"./package.json\": \"./package.json\",\n    \".\": {\n      \"react-native\": {\n        \"types\": \"./index.d.ts\",\n        \"default\": \"./index.js\"\n      },\n      \"import\": {\n        \"types\": \"./esm/index.d.mts\",\n        \"default\": \"./esm/index.mjs\"\n      },\n      \"default\": {\n        \"types\": \"./index.d.ts\",\n        \"default\": \"./index.js\"\n      }\n    },\n    \"./*\": {\n      \"react-native\": {\n        \"types\": \"./*.d.ts\",\n        \"default\": \"./*.js\"\n      },\n      \"import\": {\n        \"types\": \"./esm/*.d.mts\",\n        \"default\": \"./esm/*.mjs\"\n      },\n      \"default\": {\n        \"types\": \"./*.d.ts\",\n        \"default\": \"./*.js\"\n      }\n    }\n  },\n  \"files\": [\n    \"**\"\n  ],\n  \"sideEffects\": false,\n  \"scripts\": {\n    \"prebuild\": \"shx rm -rf dist\",\n    \"build\": \"pnpm run prebuild && pnpm run \\\"/^build:.*/\\\" && pnpm run postbuild\",\n    \"build-watch\": \"pnpm run \\\"/^build:.*/\\\" --watch\",\n    \"build:base\": \"rollup -c\",\n    \"build:vanilla\": \"rollup -c --config-vanilla\",\n    \"build:react\": \"rollup -c --config-react\",\n    \"build:middleware\": \"rollup -c --config-middleware\",\n    \"build:middleware:immer\": \"rollup -c --config-middleware_immer\",\n    \"build:shallow\": \"rollup -c --config-shallow\",\n    \"build:vanilla:shallow\": \"rollup -c --config-vanilla_shallow\",\n    \"build:react:shallow\": \"rollup -c --config-react_shallow\",\n    \"build:traditional\": \"rollup -c --config-traditional\",\n    \"postbuild\": \"pnpm run patch-d-ts && pnpm run copy && pnpm run patch-old-ts && pnpm run patch-esm-ts\",\n    \"fix\": \"pnpm run fix:lint && pnpm run fix:format\",\n    \"fix:format\": \"prettier . --write\",\n    \"fix:lint\": \"eslint . --fix\",\n    \"test\": \"pnpm run \\\"/^test:.*/\\\"\",\n    \"test:format\": \"prettier . --list-different\",\n    \"test:types\": \"tsc --noEmit\",\n    \"test:lint\": \"eslint .\",\n    \"test:spec\": \"vitest run\",\n    \"patch-d-ts\": \"node --input-type=module -e \\\"import { entries } from './rollup.config.mjs'; import shelljs from 'shelljs'; const { find, sed } = shelljs; find('dist/**/*.d.ts').forEach(f => { entries.forEach(({ find, replacement }) => { sed('-i', new RegExp(' from \\\\'' + find.source.slice(0, -1) + '\\\\';$'), ' from \\\\'' + replacement + '\\\\';', f); }); sed('-i', / from '(\\\\.[^']+)\\\\.ts';$/, ' from \\\\'\\\\$1\\\\';', f); });\\\"\",\n    \"copy\": \"shx cp -r dist/src/* dist/esm && shx cp -r dist/src/* dist && shx rm -rf dist/src && shx rm -rf dist/{src,tests} && shx cp package.json README.md LICENSE dist && json -I -f dist/package.json -e \\\"this.private=false; this.devDependencies=undefined; this.optionalDependencies=undefined; this.scripts=undefined; this.prettier=undefined;\\\"\",\n    \"patch-old-ts\": \"shx touch dist/ts_version_4.5_and_above_is_required.d.ts\",\n    \"patch-esm-ts\": \"node -e \\\"require('shelljs').find('dist/esm/**/*.d.ts').forEach(f=>{var f2=f.replace(/\\\\.ts$/,'.mts');require('fs').renameSync(f,f2);require('shelljs').sed('-i',/ from '(\\\\.[^']+)';$/,' from \\\\'\\\\$1.mjs\\\\';',f2);require('shelljs').sed('-i',/^declare module '(\\\\.[^']+)'/,'declare module \\\\'\\\\$1.mjs\\\\'',f2)})\\\"\"\n  },\n  \"engines\": {\n    \"node\": \">=12.20.0\"\n  },\n  \"prettier\": {\n    \"semi\": false,\n    \"singleQuote\": true\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/pmndrs/zustand.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"state\",\n    \"manager\",\n    \"management\",\n    \"redux\",\n    \"store\"\n  ],\n  \"author\": \"Paul Henschel\",\n  \"contributors\": [\n    \"Jeremy Holcomb (https://github.com/JeremyRH)\",\n    \"Daishi Kato (https://github.com/dai-shi)\"\n  ],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/pmndrs/zustand/issues\"\n  },\n  \"homepage\": \"https://github.com/pmndrs/zustand\",\n  \"packageManager\": \"pnpm@10.18.3\",\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.39.2\",\n    \"@redux-devtools/extension\": \"^4.0.0\",\n    \"@rollup/plugin-alias\": \"^6.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.3\",\n    \"@rollup/plugin-replace\": \"^6.0.3\",\n    \"@rollup/plugin-typescript\": \"^12.3.0\",\n    \"@testing-library/jest-dom\": \"^6.9.1\",\n    \"@testing-library/react\": \"^16.3.2\",\n    \"@types/node\": \"^25.5.0\",\n    \"@types/react\": \"^19.2.14\",\n    \"@types/react-dom\": \"^19.2.3\",\n    \"@types/use-sync-external-store\": \"^1.5.0\",\n    \"@vitest/coverage-v8\": \"^4.1.0\",\n    \"@vitest/eslint-plugin\": \"^1.6.12\",\n    \"@vitest/ui\": \"^4.1.0\",\n    \"esbuild\": \"^0.27.4\",\n    \"eslint\": \"^9.39.2\",\n    \"eslint-import-resolver-typescript\": \"^4.4.4\",\n    \"eslint-plugin-import\": \"^2.32.0\",\n    \"eslint-plugin-jest-dom\": \"^5.5.0\",\n    \"eslint-plugin-react\": \"^7.37.5\",\n    \"eslint-plugin-react-hooks\": \"^7.0.1\",\n    \"eslint-plugin-testing-library\": \"^7.16.0\",\n    \"immer\": \"^11.1.4\",\n    \"jsdom\": \"^27.4.0\",\n    \"json\": \"^11.0.0\",\n    \"prettier\": \"^3.8.1\",\n    \"react\": \"^19.2.4\",\n    \"react-dom\": \"^19.2.4\",\n    \"redux\": \"^5.0.1\",\n    \"rollup\": \"^4.59.0\",\n    \"rollup-plugin-esbuild\": \"^6.2.1\",\n    \"shelljs\": \"^0.10.0\",\n    \"shx\": \"^0.4.0\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.57.0\",\n    \"use-sync-external-store\": \"^1.6.0\",\n    \"vitest\": \"^4.1.0\"\n  },\n  \"peerDependencies\": {\n    \"@types/react\": \">=18.0.0\",\n    \"immer\": \">=9.0.6\",\n    \"react\": \">=18.0.0\",\n    \"use-sync-external-store\": \">=1.2.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/react\": {\n      \"optional\": true\n    },\n    \"immer\": {\n      \"optional\": true\n    },\n    \"react\": {\n      \"optional\": true\n    },\n    \"use-sync-external-store\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - .\nminimumReleaseAge: 1440\n"
  },
  {
    "path": "rollup.config.mjs",
    "content": "/* global process*/\nimport path from 'path'\nimport alias from '@rollup/plugin-alias'\nimport resolve from '@rollup/plugin-node-resolve'\nimport replace from '@rollup/plugin-replace'\nimport typescript from '@rollup/plugin-typescript'\nimport esbuild from 'rollup-plugin-esbuild'\n\nconst extensions = ['.js', '.ts', '.tsx']\nconst { root } = path.parse(process.cwd())\nexport const entries = [\n  { find: /.*\\/vanilla\\/shallow\\.ts$/, replacement: 'zustand/vanilla/shallow' },\n  { find: /.*\\/react\\/shallow\\.ts$/, replacement: 'zustand/react/shallow' },\n  { find: /.*\\/vanilla\\.ts$/, replacement: 'zustand/vanilla' },\n  { find: /.*\\/react\\.ts$/, replacement: 'zustand/react' },\n]\n\nfunction external(id) {\n  return !id.startsWith('.') && !id.startsWith(root)\n}\n\nfunction getEsbuild() {\n  return esbuild({\n    target: 'es2018',\n    supported: { 'import-meta': true },\n    tsconfig: path.resolve('./tsconfig.json'),\n  })\n}\n\nfunction createDeclarationConfig(input, output) {\n  return {\n    input,\n    output: {\n      dir: output,\n    },\n    external,\n    plugins: [\n      typescript({\n        declaration: true,\n        emitDeclarationOnly: true,\n        outDir: output,\n      }),\n    ],\n  }\n}\n\nfunction createESMConfig(input, output) {\n  return {\n    input,\n    output: { file: output, format: 'esm' },\n    external,\n    plugins: [\n      alias({ entries: entries.filter((entry) => !entry.find.test(input)) }),\n      resolve({ extensions }),\n      replace({\n        ...(output.endsWith('.js')\n          ? {\n              'import.meta.env?.MODE': 'process.env.NODE_ENV',\n            }\n          : {\n              'import.meta.env?.MODE':\n                '(import.meta.env ? import.meta.env.MODE : undefined)',\n            }),\n        // a workaround for #829\n        'use-sync-external-store/shim/with-selector':\n          'use-sync-external-store/shim/with-selector.js',\n        delimiters: ['\\\\b', '\\\\b(?!(\\\\.|/))'],\n        preventAssignment: true,\n      }),\n      getEsbuild(),\n    ],\n  }\n}\n\nfunction createCommonJSConfig(input, output) {\n  return {\n    input,\n    output: { file: output, format: 'cjs' },\n    external,\n    plugins: [\n      alias({ entries: entries.filter((entry) => !entry.find.test(input)) }),\n      resolve({ extensions }),\n      replace({\n        'import.meta.env?.MODE': 'process.env.NODE_ENV',\n        delimiters: ['\\\\b', '\\\\b(?!(\\\\.|/))'],\n        preventAssignment: true,\n      }),\n      getEsbuild(),\n    ],\n  }\n}\n\nexport default function (args) {\n  let c = Object.keys(args).find((key) => key.startsWith('config-'))\n  if (c) {\n    c = c.slice('config-'.length).replace(/_/g, '/')\n  } else {\n    c = 'index'\n  }\n  return [\n    ...(c === 'index' ? [createDeclarationConfig(`src/${c}.ts`, 'dist')] : []),\n    createCommonJSConfig(`src/${c}.ts`, `dist/${c}.js`),\n    createESMConfig(`src/${c}.ts`, `dist/esm/${c}.mjs`),\n  ]\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './vanilla.ts'\nexport * from './react.ts'\n"
  },
  {
    "path": "src/middleware/combine.ts",
    "content": "import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.ts'\n\ntype Write<T, U> = Omit<T, keyof U> & U\n\nexport function combine<\n  T extends object,\n  U extends object,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  initialState: T,\n  create: StateCreator<T, Mps, Mcs, U>,\n): StateCreator<Write<T, U>, Mps, Mcs> {\n  return (...args) => Object.assign({}, initialState, (create as any)(...args))\n}\n"
  },
  {
    "path": "src/middleware/devtools.ts",
    "content": "import type {} from '@redux-devtools/extension'\n\nimport type {\n  StateCreator,\n  StoreApi,\n  StoreMutatorIdentifier,\n} from '../vanilla.ts'\n\ntype Config = Parameters<\n  (Window extends { __REDUX_DEVTOOLS_EXTENSION__?: infer T }\n    ? T\n    : { connect: (param: any) => unknown })['connect']\n>[0]\n\ndeclare module '../vanilla' {\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  interface StoreMutators<S, A> {\n    'zustand/devtools': WithDevtools<S>\n  }\n}\n\n// FIXME https://github.com/reduxjs/redux-devtools/issues/1097\ntype Message = {\n  type: string\n  payload?: any\n  state?: any\n}\n\ntype WithDispatch = {\n  dispatch: (...args: unknown[]) => void\n  dispatchFromDevtools: unknown\n}\n\nconst shouldDispatchFromDevtools = (api: unknown): api is WithDispatch =>\n  !!(api as WithDispatch).dispatchFromDevtools &&\n  typeof (api as WithDispatch).dispatch === 'function'\n\ntype Cast<T, U> = T extends U ? T : U\ntype Write<T, U> = Omit<T, keyof U> & U\ntype TakeTwo<T> = T extends { length: 0 }\n  ? [undefined, undefined]\n  : T extends { length: 1 }\n    ? [...args0: Cast<T, unknown[]>, arg1: undefined]\n    : T extends { length: 0 | 1 }\n      ? [...args0: Cast<T, unknown[]>, arg1: undefined]\n      : T extends { length: 2 }\n        ? T\n        : T extends { length: 1 | 2 }\n          ? T\n          : T extends { length: 0 | 1 | 2 }\n            ? T\n            : T extends [infer A0, infer A1, ...unknown[]]\n              ? [A0, A1]\n              : T extends [infer A0, (infer A1)?, ...unknown[]]\n                ? [A0, A1?]\n                : T extends [(infer A0)?, (infer A1)?, ...unknown[]]\n                  ? [A0?, A1?]\n                  : never\n\ntype WithDevtools<S> = Write<S, StoreDevtools<S>>\n\ntype Action =\n  | string\n  | {\n      type: string\n      [x: string | number | symbol]: unknown\n    }\ntype StoreDevtools<S> = S extends {\n  setState: {\n    // capture both overloads of setState\n    (...args: infer Sa1): infer Sr1\n    (...args: infer Sa2): infer Sr2\n  }\n}\n  ? {\n      setState(...args: [...args: TakeTwo<Sa1>, action?: Action]): Sr1\n      setState(...args: [...args: TakeTwo<Sa2>, action?: Action]): Sr2\n      devtools: {\n        cleanup: () => void\n      }\n    }\n  : never\n\nexport interface DevtoolsOptions extends Config {\n  name?: string\n  enabled?: boolean\n  anonymousActionType?: string\n  store?: string\n}\n\ntype Devtools = <\n  T,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n  U = T,\n>(\n  initializer: StateCreator<T, [...Mps, ['zustand/devtools', never]], Mcs, U>,\n  devtoolsOptions?: DevtoolsOptions,\n) => StateCreator<T, Mps, [['zustand/devtools', never], ...Mcs]>\n\ndeclare module '../vanilla' {\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  interface StoreMutators<S, A> {\n    'zustand/devtools': WithDevtools<S>\n  }\n}\n\ntype DevtoolsImpl = <T>(\n  storeInitializer: StateCreator<T, [], []>,\n  devtoolsOptions?: DevtoolsOptions,\n) => StateCreator<T, [], []>\n\nexport type NamedSet<T> = WithDevtools<StoreApi<T>>['setState']\n\ntype Connection = ReturnType<\n  NonNullable<Window['__REDUX_DEVTOOLS_EXTENSION__']>['connect']\n>\ntype ConnectionName = string | undefined\ntype StoreName = string\ntype StoreInformation = StoreApi<unknown>\ntype ConnectionInformation = {\n  connection: Connection\n  stores: Record<StoreName, StoreInformation>\n}\n\nconst trackedConnections: Map<ConnectionName, ConnectionInformation> = new Map()\n\nconst getTrackedConnectionState = (\n  name: string | undefined,\n): Record<string, any> => {\n  const api = trackedConnections.get(name)\n  if (!api) return {}\n  return Object.fromEntries(\n    Object.entries(api.stores).map(([key, api]) => [key, api.getState()]),\n  )\n}\n\nconst extractConnectionInformation = (\n  store: string | undefined,\n  extensionConnector: NonNullable<\n    (typeof window)['__REDUX_DEVTOOLS_EXTENSION__']\n  >,\n  options: Omit<DevtoolsOptions, 'enabled' | 'anonymousActionType' | 'store'>,\n) => {\n  if (store === undefined) {\n    return {\n      type: 'untracked' as const,\n      connection: extensionConnector.connect(options),\n    }\n  }\n  const existingConnection = trackedConnections.get(options.name)\n  if (existingConnection) {\n    return { type: 'tracked' as const, store, ...existingConnection }\n  }\n  const newConnection: ConnectionInformation = {\n    connection: extensionConnector.connect(options),\n    stores: {},\n  }\n  trackedConnections.set(options.name, newConnection)\n  return { type: 'tracked' as const, store, ...newConnection }\n}\n\nconst removeStoreFromTrackedConnections = (\n  name: string | undefined,\n  store: string | undefined,\n) => {\n  if (store === undefined) return\n  const connectionInfo = trackedConnections.get(name)\n  if (!connectionInfo) return\n  delete connectionInfo.stores[store]\n  if (Object.keys(connectionInfo.stores).length === 0) {\n    trackedConnections.delete(name)\n  }\n}\n\nconst findCallerName = (stack: string | undefined) => {\n  if (!stack) return undefined\n  const traceLines = stack.split('\\n')\n  const apiSetStateLineIndex = traceLines.findIndex((traceLine) =>\n    traceLine.includes('api.setState'),\n  )\n  if (apiSetStateLineIndex < 0) return undefined\n  const callerLine = traceLines[apiSetStateLineIndex + 1]?.trim() || ''\n  return /.+ (.+) .+/.exec(callerLine)?.[1]\n}\n\nconst devtoolsImpl: DevtoolsImpl =\n  (fn, devtoolsOptions = {}) =>\n  (set, get, api) => {\n    const { enabled, anonymousActionType, store, ...options } = devtoolsOptions\n\n    type S = ReturnType<typeof fn> & {\n      [store: string]: ReturnType<typeof fn>\n    }\n    type PartialState = Partial<S> | ((s: S) => Partial<S>)\n\n    let extensionConnector:\n      | (typeof window)['__REDUX_DEVTOOLS_EXTENSION__']\n      | false\n    try {\n      extensionConnector =\n        (enabled ?? import.meta.env?.MODE !== 'production') &&\n        window.__REDUX_DEVTOOLS_EXTENSION__\n    } catch {\n      // ignored\n    }\n\n    if (!extensionConnector) {\n      return fn(set, get, api)\n    }\n\n    const { connection, ...connectionInformation } =\n      extractConnectionInformation(store, extensionConnector, options)\n\n    let isRecording = true\n    api.setState = ((state, replace, nameOrAction: Action) => {\n      const r = set(state, replace as any)\n      if (!isRecording) return r\n      const action: { type: string } =\n        nameOrAction === undefined\n          ? {\n              type:\n                anonymousActionType ||\n                findCallerName(new Error().stack) ||\n                'anonymous',\n            }\n          : typeof nameOrAction === 'string'\n            ? { type: nameOrAction }\n            : nameOrAction\n      if (store === undefined) {\n        connection?.send(action, get())\n        return r\n      }\n      connection?.send(\n        {\n          ...action,\n          type: `${store}/${action.type}`,\n        },\n        {\n          ...getTrackedConnectionState(options.name),\n          [store]: api.getState(),\n        },\n      )\n      return r\n    }) as NamedSet<S>\n    ;(api as StoreApi<S> & StoreDevtools<S>).devtools = {\n      cleanup: () => {\n        if (\n          connection &&\n          typeof (connection as any).unsubscribe === 'function'\n        ) {\n          ;(connection as any).unsubscribe()\n        }\n        removeStoreFromTrackedConnections(options.name, store)\n      },\n    }\n\n    const setStateFromDevtools: StoreApi<S>['setState'] = (...a) => {\n      const originalIsRecording = isRecording\n      isRecording = false\n      set(...(a as Parameters<typeof set>))\n      isRecording = originalIsRecording\n    }\n\n    const initialState = fn(api.setState, get, api)\n    if (connectionInformation.type === 'untracked') {\n      connection?.init(initialState)\n    } else {\n      connectionInformation.stores[connectionInformation.store] = api\n      connection?.init(\n        Object.fromEntries(\n          Object.entries(connectionInformation.stores).map(([key, store]) => [\n            key,\n            key === connectionInformation.store\n              ? initialState\n              : store.getState(),\n          ]),\n        ),\n      )\n    }\n\n    if (shouldDispatchFromDevtools(api)) {\n      let didWarnAboutReservedActionType = false\n      const originalDispatch = api.dispatch\n      api.dispatch = (...args: any[]) => {\n        if (\n          import.meta.env?.MODE !== 'production' &&\n          args[0].type === '__setState' &&\n          !didWarnAboutReservedActionType\n        ) {\n          console.warn(\n            '[zustand devtools middleware] \"__setState\" action type is reserved ' +\n              'to set state from the devtools. Avoid using it.',\n          )\n          didWarnAboutReservedActionType = true\n        }\n        originalDispatch(...args)\n      }\n    }\n\n    ;(\n      connection as unknown as {\n        // FIXME https://github.com/reduxjs/redux-devtools/issues/1097\n        subscribe: (\n          listener: (message: Message) => void,\n        ) => (() => void) | undefined\n      }\n    ).subscribe((message) => {\n      switch (message.type) {\n        case 'ACTION':\n          if (typeof message.payload !== 'string') {\n            console.error(\n              '[zustand devtools middleware] Unsupported action format',\n            )\n            return\n          }\n          return parseJsonThen<{ type: unknown; state?: PartialState }>(\n            message.payload,\n            (action) => {\n              if (action.type === '__setState') {\n                if (store === undefined) {\n                  setStateFromDevtools(action.state as PartialState)\n                  return\n                }\n                if (Object.keys(action.state as S).length !== 1) {\n                  console.error(\n                    `\n                    [zustand devtools middleware] Unsupported __setState action format.\n                    When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n                    and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n                    `,\n                  )\n                }\n                const stateFromDevtools = (action.state as S)[store]\n                if (\n                  stateFromDevtools === undefined ||\n                  stateFromDevtools === null\n                ) {\n                  return\n                }\n                if (\n                  JSON.stringify(api.getState()) !==\n                  JSON.stringify(stateFromDevtools)\n                ) {\n                  setStateFromDevtools(stateFromDevtools)\n                }\n                return\n              }\n\n              if (shouldDispatchFromDevtools(api)) {\n                api.dispatch(action)\n              }\n            },\n          )\n\n        case 'DISPATCH':\n          switch (message.payload.type) {\n            case 'RESET':\n              setStateFromDevtools(initialState as S)\n              if (store === undefined) {\n                return connection?.init(api.getState())\n              }\n              return connection?.init(getTrackedConnectionState(options.name))\n\n            case 'COMMIT':\n              if (store === undefined) {\n                connection?.init(api.getState())\n                return\n              }\n              return connection?.init(getTrackedConnectionState(options.name))\n\n            case 'ROLLBACK':\n              return parseJsonThen<S>(message.state, (state) => {\n                if (store === undefined) {\n                  setStateFromDevtools(state)\n                  connection?.init(api.getState())\n                  return\n                }\n                setStateFromDevtools(state[store] as S)\n                connection?.init(getTrackedConnectionState(options.name))\n              })\n\n            case 'JUMP_TO_STATE':\n            case 'JUMP_TO_ACTION':\n              return parseJsonThen<S>(message.state, (state) => {\n                if (store === undefined) {\n                  setStateFromDevtools(state)\n                  return\n                }\n                if (\n                  JSON.stringify(api.getState()) !==\n                  JSON.stringify(state[store])\n                ) {\n                  setStateFromDevtools(state[store] as S)\n                }\n              })\n\n            case 'IMPORT_STATE': {\n              const { nextLiftedState } = message.payload\n              const lastComputedState =\n                nextLiftedState.computedStates.slice(-1)[0]?.state\n              if (!lastComputedState) return\n              if (store === undefined) {\n                setStateFromDevtools(lastComputedState)\n              } else {\n                setStateFromDevtools(lastComputedState[store])\n              }\n              connection?.send(\n                null as any, // FIXME no-any\n                nextLiftedState,\n              )\n              return\n            }\n\n            case 'PAUSE_RECORDING':\n              return (isRecording = !isRecording)\n          }\n          return\n      }\n    })\n\n    return initialState\n  }\nexport const devtools = devtoolsImpl as unknown as Devtools\n\nconst parseJsonThen = <T>(stringified: string, fn: (parsed: T) => void) => {\n  let parsed: T | undefined\n  try {\n    parsed = JSON.parse(stringified)\n  } catch (e) {\n    console.error(\n      '[zustand devtools middleware] Could not parse the received json',\n      e,\n    )\n  }\n  if (parsed !== undefined) fn(parsed as T)\n}\n"
  },
  {
    "path": "src/middleware/immer.ts",
    "content": "import { produce } from 'immer'\nimport type { Draft } from 'immer'\nimport type { StateCreator, StoreMutatorIdentifier } from '../vanilla.ts'\n\ntype Immer = <\n  T,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n  U = T,\n>(\n  initializer: StateCreator<T, [...Mps, ['zustand/immer', never]], Mcs, U>,\n) => StateCreator<T, Mps, [['zustand/immer', never], ...Mcs], U>\n\ndeclare module '../vanilla' {\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  interface StoreMutators<S, A> {\n    ['zustand/immer']: WithImmer<S>\n  }\n}\n\ntype Write<T, U> = Omit<T, keyof U> & U\ntype SkipTwo<T> = T extends { length: 0 }\n  ? []\n  : T extends { length: 1 }\n    ? []\n    : T extends { length: 0 | 1 }\n      ? []\n      : T extends [unknown, unknown, ...infer A]\n        ? A\n        : T extends [unknown, unknown?, ...infer A]\n          ? A\n          : T extends [unknown?, unknown?, ...infer A]\n            ? A\n            : never\n\ntype SetStateType<T extends unknown[]> = Exclude<T[0], (...args: any[]) => any>\n\ntype WithImmer<S> = Write<S, StoreImmer<S>>\n\ntype StoreImmer<S> = S extends {\n  setState: infer SetState\n}\n  ? SetState extends {\n      (...args: infer A1): infer Sr1\n      (...args: infer A2): infer Sr2\n    }\n    ? {\n        // Ideally, we would want to infer the `nextStateOrUpdater` `T` type from the\n        // `A1` type, but this is infeasible since it is an intersection with\n        // a partial type.\n        setState(\n          nextStateOrUpdater:\n            | SetStateType<A2>\n            | Partial<SetStateType<A2>>\n            | ((state: Draft<SetStateType<A2>>) => void),\n          shouldReplace?: false,\n          ...args: SkipTwo<A1>\n        ): Sr1\n        setState(\n          nextStateOrUpdater:\n            | SetStateType<A2>\n            | ((state: Draft<SetStateType<A2>>) => void),\n          shouldReplace: true,\n          ...args: SkipTwo<A2>\n        ): Sr2\n      }\n    : never\n  : never\n\ntype ImmerImpl = <T>(\n  storeInitializer: StateCreator<T, [], []>,\n) => StateCreator<T, [], []>\n\nconst immerImpl: ImmerImpl = (initializer) => (set, get, store) => {\n  type T = ReturnType<typeof initializer>\n\n  store.setState = (updater, replace, ...args) => {\n    const nextState = (\n      typeof updater === 'function' ? produce(updater as any) : updater\n    ) as ((s: T) => T) | T | Partial<T>\n\n    return set(nextState, replace as any, ...args)\n  }\n\n  return initializer(store.setState, get, store)\n}\n\nexport const immer = immerImpl as unknown as Immer\n"
  },
  {
    "path": "src/middleware/persist.ts",
    "content": "import type {\n  StateCreator,\n  StoreApi,\n  StoreMutatorIdentifier,\n} from '../vanilla.ts'\n\nexport interface StateStorage<R = unknown> {\n  getItem: (name: string) => string | null | Promise<string | null>\n  setItem: (name: string, value: string) => R\n  removeItem: (name: string) => R\n}\n\nexport type StorageValue<S> = {\n  state: S\n  version?: number\n}\n\nexport interface PersistStorage<S, R = unknown> {\n  getItem: (\n    name: string,\n  ) => StorageValue<S> | null | Promise<StorageValue<S> | null>\n  setItem: (name: string, value: StorageValue<S>) => R\n  removeItem: (name: string) => R\n}\n\ntype JsonStorageOptions = {\n  reviver?: (key: string, value: unknown) => unknown\n  replacer?: (key: string, value: unknown) => unknown\n}\n\nexport function createJSONStorage<S, R = unknown>(\n  getStorage: () => StateStorage<R>,\n  options?: JsonStorageOptions,\n): PersistStorage<S, unknown> | undefined {\n  let storage: StateStorage<R> | undefined\n  try {\n    storage = getStorage()\n  } catch {\n    // prevent error if the storage is not defined (e.g. when server side rendering a page)\n    return\n  }\n  const persistStorage: PersistStorage<S, R> = {\n    getItem: (name) => {\n      const parse = (str: string | null) => {\n        if (str === null) {\n          return null\n        }\n        return JSON.parse(str, options?.reviver) as StorageValue<S>\n      }\n      const str = storage.getItem(name) ?? null\n      if (str instanceof Promise) {\n        return str.then(parse)\n      }\n      return parse(str)\n    },\n    setItem: (name, newValue) =>\n      storage.setItem(name, JSON.stringify(newValue, options?.replacer)),\n    removeItem: (name) => storage.removeItem(name),\n  }\n  return persistStorage\n}\n\nexport interface PersistOptions<\n  S,\n  PersistedState = S,\n  PersistReturn = unknown,\n> {\n  /** Name of the storage (must be unique) */\n  name: string\n  /**\n   * Use a custom persist storage.\n   *\n   * Combining `createJSONStorage` helps creating a persist storage\n   * with JSON.parse and JSON.stringify.\n   *\n   * @default createJSONStorage(() => window.localStorage)\n   */\n  storage?: PersistStorage<PersistedState, PersistReturn> | undefined\n  /**\n   * Filter the persisted value.\n   *\n   * @params state The state's value\n   */\n  partialize?: (state: S) => PersistedState\n  /**\n   * A function returning another (optional) function.\n   * The main function will be called before the state rehydration.\n   * The returned function will be called after the state rehydration or when an error occurred.\n   */\n  onRehydrateStorage?: (\n    state: S,\n  ) => ((state?: S, error?: unknown) => void) | void\n  /**\n   * If the stored state's version mismatch the one specified here, the storage will not be used.\n   * This is useful when adding a breaking change to your store.\n   */\n  version?: number\n  /**\n   * A function to perform persisted state migration.\n   * This function will be called when persisted state versions mismatch with the one specified here.\n   */\n  migrate?: (\n    persistedState: unknown,\n    version: number,\n  ) => PersistedState | Promise<PersistedState>\n  /**\n   * A function to perform custom hydration merges when combining the stored state with the current one.\n   * By default, this function does a shallow merge.\n   */\n  merge?: (persistedState: unknown, currentState: S) => S\n\n  /**\n   * An optional boolean that will prevent the persist middleware from triggering hydration on initialization,\n   * This allows you to call `rehydrate()` at a specific point in your apps rendering life-cycle.\n   *\n   * This is useful in SSR application.\n   *\n   * @default false\n   */\n  skipHydration?: boolean\n}\n\ntype PersistListener<S> = (state: S) => void\n\ntype StorePersist<S, Ps, Pr> = S extends {\n  getState: () => infer T\n  setState: {\n    // capture both overloads of setState\n    (...args: infer Sa1): infer Sr1\n    (...args: infer Sa2): infer Sr2\n  }\n}\n  ? {\n      setState(...args: Sa1): Sr1 | Pr\n      setState(...args: Sa2): Sr2 | Pr\n      persist: {\n        setOptions: (options: Partial<PersistOptions<T, Ps, Pr>>) => void\n        clearStorage: () => void\n        rehydrate: () => Promise<void> | void\n        hasHydrated: () => boolean\n        onHydrate: (fn: PersistListener<T>) => () => void\n        onFinishHydration: (fn: PersistListener<T>) => () => void\n        getOptions: () => Partial<PersistOptions<T, Ps, Pr>>\n      }\n    }\n  : never\n\ntype Thenable<Value> = {\n  then<V>(\n    onFulfilled: (value: Value) => V | Promise<V> | Thenable<V>,\n  ): Thenable<V>\n  catch<V>(\n    onRejected: (reason: Error) => V | Promise<V> | Thenable<V>,\n  ): Thenable<V>\n}\n\nconst toThenable =\n  <Result, Input>(\n    fn: (input: Input) => Result | Promise<Result> | Thenable<Result>,\n  ) =>\n  (input: Input): Thenable<Result> => {\n    try {\n      const result = fn(input)\n      if (result instanceof Promise) {\n        return result as Thenable<Result>\n      }\n      return {\n        then(onFulfilled) {\n          return toThenable(onFulfilled)(result as Result)\n        },\n        catch(_onRejected) {\n          return this as Thenable<any>\n        },\n      }\n    } catch (e: any) {\n      return {\n        then(_onFulfilled) {\n          return this as Thenable<any>\n        },\n        catch(onRejected) {\n          return toThenable(onRejected)(e)\n        },\n      }\n    }\n  }\n\nconst persistImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {\n  type S = ReturnType<typeof config>\n  let options = {\n    storage: createJSONStorage<S, void>(() => window.localStorage),\n    partialize: (state: S) => state,\n    version: 0,\n    merge: (persistedState: unknown, currentState: S) => ({\n      ...currentState,\n      ...(persistedState as object),\n    }),\n    ...baseOptions,\n  }\n\n  let hasHydrated = false\n  // Counter to track hydration versions and prevent race conditions\n  // when multiple rehydrate() calls happen concurrently\n  let hydrationVersion = 0\n  const hydrationListeners = new Set<PersistListener<S>>()\n  const finishHydrationListeners = new Set<PersistListener<S>>()\n  let storage = options.storage\n\n  if (!storage) {\n    return config(\n      (...args) => {\n        console.warn(\n          `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`,\n        )\n        set(...(args as Parameters<typeof set>))\n      },\n      get,\n      api,\n    )\n  }\n\n  const setItem = () => {\n    const state = options.partialize({ ...get() })\n    return (storage as PersistStorage<S, unknown>).setItem(options.name, {\n      state,\n      version: options.version,\n    })\n  }\n\n  const savedSetState = api.setState\n\n  api.setState = (state, replace) => {\n    savedSetState(state, replace as any)\n    return setItem()\n  }\n\n  const configResult = config(\n    (...args) => {\n      set(...(args as Parameters<typeof set>))\n      return setItem()\n    },\n    get,\n    api,\n  )\n\n  api.getInitialState = () => configResult\n\n  // a workaround to solve the issue of not storing rehydrated state in sync storage\n  // the set(state) value would be later overridden with initial state by create()\n  // to avoid this, we merge the state from localStorage into the initial state.\n  let stateFromStorage: S | undefined\n\n  // rehydrate initial state with existing stored state\n  const hydrate = () => {\n    if (!storage) return\n\n    // On the first invocation of 'hydrate', state will not yet be defined (this is\n    // true for both the 'asynchronous' and 'synchronous' case). Pass 'configResult'\n    // as a backup  to 'get()' so listeners and 'onRehydrateStorage' are called with\n    // the latest available state.\n\n    // Increment version to invalidate any in-flight hydration\n    const currentVersion = ++hydrationVersion\n    hasHydrated = false\n    hydrationListeners.forEach((cb) => cb(get() ?? configResult))\n\n    const postRehydrationCallback =\n      options.onRehydrateStorage?.(get() ?? configResult) || undefined\n\n    // bind is used to avoid `TypeError: Illegal invocation` error\n    return toThenable(storage.getItem.bind(storage))(options.name)\n      .then((deserializedStorageValue) => {\n        if (deserializedStorageValue) {\n          if (\n            typeof deserializedStorageValue.version === 'number' &&\n            deserializedStorageValue.version !== options.version\n          ) {\n            if (options.migrate) {\n              const migration = options.migrate(\n                deserializedStorageValue.state,\n                deserializedStorageValue.version,\n              )\n              if (migration instanceof Promise) {\n                return migration.then((result) => [true, result] as const)\n              }\n              return [true, migration] as const\n            }\n            console.error(\n              `State loaded from storage couldn't be migrated since no migrate function was provided`,\n            )\n          } else {\n            return [false, deserializedStorageValue.state] as const\n          }\n        }\n        return [false, undefined] as const\n      })\n      .then((migrationResult) => {\n        // Abort if a newer hydration has started\n        if (currentVersion !== hydrationVersion) {\n          return\n        }\n        const [migrated, migratedState] = migrationResult\n        stateFromStorage = options.merge(\n          migratedState as S,\n          get() ?? configResult,\n        )\n\n        set(stateFromStorage as S, true)\n        if (migrated) {\n          return setItem()\n        }\n      })\n      .then(() => {\n        // Abort if a newer hydration has started\n        if (currentVersion !== hydrationVersion) {\n          return\n        }\n        postRehydrationCallback?.(get(), undefined)\n\n        // It's possible that 'postRehydrationCallback' updated the state. To ensure\n        // that isn't overwritten when returning 'stateFromStorage' below\n        // (synchronous-case only), update 'stateFromStorage' to point to the latest\n        // state. In the asynchronous case, 'stateFromStorage' isn't used after this\n        // callback, so there's no harm in updating it to match the latest state.\n        stateFromStorage = get()\n        hasHydrated = true\n        finishHydrationListeners.forEach((cb) => cb(stateFromStorage as S))\n      })\n      .catch((e: Error) => {\n        // Abort if a newer hydration has started\n        if (currentVersion !== hydrationVersion) {\n          return\n        }\n        postRehydrationCallback?.(undefined, e)\n      })\n  }\n\n  ;(api as StoreApi<S> & StorePersist<StoreApi<S>, S, unknown>).persist = {\n    setOptions: (newOptions) => {\n      options = {\n        ...options,\n        ...newOptions,\n      }\n\n      if (newOptions.storage) {\n        storage = newOptions.storage\n      }\n    },\n    clearStorage: () => {\n      storage?.removeItem(options.name)\n    },\n    getOptions: () => options,\n    rehydrate: () => hydrate() as Promise<void>,\n    hasHydrated: () => hasHydrated,\n    onHydrate: (cb) => {\n      hydrationListeners.add(cb)\n\n      return () => {\n        hydrationListeners.delete(cb)\n      }\n    },\n    onFinishHydration: (cb) => {\n      finishHydrationListeners.add(cb)\n\n      return () => {\n        finishHydrationListeners.delete(cb)\n      }\n    },\n  }\n\n  if (!options.skipHydration) {\n    hydrate()\n  }\n\n  return stateFromStorage || configResult\n}\n\ntype Persist = <\n  T,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n  U = T,\n>(\n  initializer: StateCreator<T, [...Mps, ['zustand/persist', unknown]], Mcs>,\n  options: PersistOptions<T, U>,\n) => StateCreator<T, Mps, [['zustand/persist', U], ...Mcs]>\n\ndeclare module '../vanilla' {\n  interface StoreMutators<S, A> {\n    'zustand/persist': WithPersist<S, A>\n  }\n}\n\ntype Write<T, U> = Omit<T, keyof U> & U\n\ntype WithPersist<S, A> = Write<S, StorePersist<S, A, unknown>>\n\ntype PersistImpl = <T>(\n  storeInitializer: StateCreator<T, [], []>,\n  options: PersistOptions<T, T>,\n) => StateCreator<T, [], []>\n\nexport const persist = persistImpl as unknown as Persist\n"
  },
  {
    "path": "src/middleware/redux.ts",
    "content": "import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.ts'\nimport type { NamedSet } from './devtools.ts'\n\ntype Write<T, U> = Omit<T, keyof U> & U\n\ntype Action = { type: string }\n\ntype StoreRedux<A> = {\n  dispatch: (a: A) => A\n  dispatchFromDevtools: true\n}\n\ntype ReduxState<A> = {\n  dispatch: StoreRedux<A>['dispatch']\n}\n\ntype WithRedux<S, A> = Write<S, StoreRedux<A>>\n\ntype Redux = <\n  T,\n  A extends Action,\n  Cms extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  reducer: (state: T, action: A) => T,\n  initialState: T,\n) => StateCreator<Write<T, ReduxState<A>>, Cms, [['zustand/redux', A]]>\n\ndeclare module '../vanilla' {\n  interface StoreMutators<S, A> {\n    'zustand/redux': WithRedux<S, A>\n  }\n}\n\ntype ReduxImpl = <T, A extends Action>(\n  reducer: (state: T, action: A) => T,\n  initialState: T,\n) => StateCreator<T & ReduxState<A>, [], []>\n\nconst reduxImpl: ReduxImpl = (reducer, initial) => (set, _get, api) => {\n  type S = typeof initial\n  type A = Parameters<typeof reducer>[1]\n  ;(api as any).dispatch = (action: A) => {\n    ;(set as NamedSet<S>)((state: S) => reducer(state, action), false, action)\n    return action\n  }\n  ;(api as any).dispatchFromDevtools = true\n\n  return { dispatch: (...args) => (api as any).dispatch(...args), ...initial }\n}\nexport const redux = reduxImpl as unknown as Redux\n"
  },
  {
    "path": "src/middleware/ssrSafe.ts",
    "content": "import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.ts'\n\n// This is experimental middleware. It will be changed before finalizing it.\n// https://github.com/pmndrs/zustand/discussions/2740\n// TODO Not very happy with the middleware name. Will revisit it later.\nexport function ssrSafe<\n  T extends object,\n  U extends object,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  config: StateCreator<T, Mps, Mcs, U>,\n  isSSR: boolean = typeof window === 'undefined',\n): StateCreator<T, Mps, Mcs, U> {\n  return (set, get, api) => {\n    if (!isSSR) {\n      return config(set, get, api)\n    }\n    const ssrSet = () => {\n      throw new Error('Cannot set state of Zustand store in SSR')\n    }\n    api.setState = ssrSet\n    return config(ssrSet as never, get, api)\n  }\n}\n"
  },
  {
    "path": "src/middleware/subscribeWithSelector.ts",
    "content": "import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.ts'\n\ntype SubscribeWithSelector = <\n  T,\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  initializer: StateCreator<\n    T,\n    [...Mps, ['zustand/subscribeWithSelector', never]],\n    Mcs\n  >,\n) => StateCreator<T, Mps, [['zustand/subscribeWithSelector', never], ...Mcs]>\n\ntype Write<T, U> = Omit<T, keyof U> & U\n\ntype WithSelectorSubscribe<S> = S extends { getState: () => infer T }\n  ? Write<S, StoreSubscribeWithSelector<T>>\n  : never\n\ndeclare module '../vanilla' {\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  interface StoreMutators<S, A> {\n    ['zustand/subscribeWithSelector']: WithSelectorSubscribe<S>\n  }\n}\n\ntype StoreSubscribeWithSelector<T> = {\n  subscribe: {\n    (listener: (selectedState: T, previousSelectedState: T) => void): () => void\n    <U>(\n      selector: (state: T) => U,\n      listener: (selectedState: U, previousSelectedState: U) => void,\n      options?: {\n        equalityFn?: (a: U, b: U) => boolean\n        fireImmediately?: boolean\n      },\n    ): () => void\n  }\n}\n\ntype SubscribeWithSelectorImpl = <T extends object>(\n  storeInitializer: StateCreator<T, [], []>,\n) => StateCreator<T, [], []>\n\nconst subscribeWithSelectorImpl: SubscribeWithSelectorImpl =\n  (fn) => (set, get, api) => {\n    type S = ReturnType<typeof fn>\n    type Listener = (state: S, previousState: S) => void\n    const origSubscribe = api.subscribe as (listener: Listener) => () => void\n    api.subscribe = ((selector: any, optListener: any, options: any) => {\n      let listener: Listener = selector // if no selector\n      if (optListener) {\n        const equalityFn = options?.equalityFn || Object.is\n        let currentSlice = selector(api.getState())\n        listener = (state) => {\n          const nextSlice = selector(state)\n          if (!equalityFn(currentSlice, nextSlice)) {\n            const previousSlice = currentSlice\n            optListener((currentSlice = nextSlice), previousSlice)\n          }\n        }\n        if (options?.fireImmediately) {\n          optListener(currentSlice, currentSlice)\n        }\n      }\n      return origSubscribe(listener)\n    }) as any\n    const initialState = fn(set, get, api)\n    return initialState\n  }\nexport const subscribeWithSelector =\n  subscribeWithSelectorImpl as unknown as SubscribeWithSelector\n"
  },
  {
    "path": "src/middleware.ts",
    "content": "export { redux } from './middleware/redux.ts'\nexport {\n  devtools,\n  type DevtoolsOptions,\n  type NamedSet,\n} from './middleware/devtools.ts'\nexport { subscribeWithSelector } from './middleware/subscribeWithSelector.ts'\nexport { combine } from './middleware/combine.ts'\nexport {\n  persist,\n  createJSONStorage,\n  type StateStorage,\n  type StorageValue,\n  type PersistStorage,\n  type PersistOptions,\n} from './middleware/persist.ts'\nexport { ssrSafe as unstable_ssrSafe } from './middleware/ssrSafe.ts'\n"
  },
  {
    "path": "src/react/shallow.ts",
    "content": "import React from 'react'\nimport { shallow } from '../vanilla/shallow.ts'\n\nexport function useShallow<S, U>(selector: (state: S) => U): (state: S) => U {\n  const prev = React.useRef<U>(undefined)\n  return (state) => {\n    const next = selector(state)\n    return shallow(prev.current, next)\n      ? (prev.current as U)\n      : (prev.current = next)\n  }\n}\n"
  },
  {
    "path": "src/react.ts",
    "content": "import React from 'react'\nimport { createStore } from './vanilla.ts'\nimport type {\n  ExtractState,\n  Mutate,\n  StateCreator,\n  StoreApi,\n  StoreMutatorIdentifier,\n} from './vanilla.ts'\n\ntype ReadonlyStoreApi<T> = Pick<\n  StoreApi<T>,\n  'getState' | 'getInitialState' | 'subscribe'\n>\n\nconst identity = <T>(arg: T): T => arg\nexport function useStore<S extends ReadonlyStoreApi<unknown>>(\n  api: S,\n): ExtractState<S>\n\nexport function useStore<S extends ReadonlyStoreApi<unknown>, U>(\n  api: S,\n  selector: (state: ExtractState<S>) => U,\n): U\n\nexport function useStore<TState, StateSlice>(\n  api: ReadonlyStoreApi<TState>,\n  selector: (state: TState) => StateSlice = identity as any,\n) {\n  const slice = React.useSyncExternalStore(\n    api.subscribe,\n    React.useCallback(() => selector(api.getState()), [api, selector]),\n    React.useCallback(() => selector(api.getInitialState()), [api, selector]),\n  )\n  React.useDebugValue(slice)\n  return slice\n}\n\nexport type UseBoundStore<S extends ReadonlyStoreApi<unknown>> = {\n  (): ExtractState<S>\n  <U>(selector: (state: ExtractState<S>) => U): U\n} & S\n\ntype Create = {\n  <T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(\n    initializer: StateCreator<T, [], Mos>,\n  ): UseBoundStore<Mutate<StoreApi<T>, Mos>>\n  <T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(\n    initializer: StateCreator<T, [], Mos>,\n  ) => UseBoundStore<Mutate<StoreApi<T>, Mos>>\n}\n\nconst createImpl = <T>(createState: StateCreator<T, [], []>) => {\n  const api = createStore(createState)\n\n  const useBoundStore: any = (selector?: any) => useStore(api, selector)\n\n  Object.assign(useBoundStore, api)\n\n  return useBoundStore\n}\n\nexport const create = (<T>(createState: StateCreator<T, [], []> | undefined) =>\n  createState ? createImpl(createState) : createImpl) as Create\n"
  },
  {
    "path": "src/shallow.ts",
    "content": "export { shallow } from './vanilla/shallow.ts'\nexport { useShallow } from './react/shallow.ts'\n"
  },
  {
    "path": "src/traditional.ts",
    "content": "import React from 'react'\nimport useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector'\nimport { createStore } from './vanilla.ts'\nimport type {\n  ExtractState,\n  Mutate,\n  StateCreator,\n  StoreApi,\n  StoreMutatorIdentifier,\n} from './vanilla.ts'\n\nconst { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports\n\ntype ReadonlyStoreApi<T> = Pick<\n  StoreApi<T>,\n  'getState' | 'getInitialState' | 'subscribe'\n>\n\nconst identity = <T>(arg: T): T => arg\n\nexport function useStoreWithEqualityFn<S extends ReadonlyStoreApi<unknown>>(\n  api: S,\n): ExtractState<S>\n\nexport function useStoreWithEqualityFn<S extends ReadonlyStoreApi<unknown>, U>(\n  api: S,\n  selector: (state: ExtractState<S>) => U,\n  equalityFn?: (a: U, b: U) => boolean,\n): U\n\nexport function useStoreWithEqualityFn<TState, StateSlice>(\n  api: ReadonlyStoreApi<TState>,\n  selector: (state: TState) => StateSlice = identity as any,\n  equalityFn?: (a: StateSlice, b: StateSlice) => boolean,\n) {\n  const slice = useSyncExternalStoreWithSelector(\n    api.subscribe,\n    api.getState,\n    api.getInitialState,\n    selector,\n    equalityFn,\n  )\n  React.useDebugValue(slice)\n  return slice\n}\n\nexport type UseBoundStoreWithEqualityFn<S extends ReadonlyStoreApi<unknown>> = {\n  (): ExtractState<S>\n  <U>(\n    selector: (state: ExtractState<S>) => U,\n    equalityFn?: (a: U, b: U) => boolean,\n  ): U\n} & S\n\ntype CreateWithEqualityFn = {\n  <T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(\n    initializer: StateCreator<T, [], Mos>,\n    defaultEqualityFn?: <U>(a: U, b: U) => boolean,\n  ): UseBoundStoreWithEqualityFn<Mutate<StoreApi<T>, Mos>>\n  <T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(\n    initializer: StateCreator<T, [], Mos>,\n    defaultEqualityFn?: <U>(a: U, b: U) => boolean,\n  ) => UseBoundStoreWithEqualityFn<Mutate<StoreApi<T>, Mos>>\n}\n\nconst createWithEqualityFnImpl = <T>(\n  createState: StateCreator<T, [], []>,\n  defaultEqualityFn?: <U>(a: U, b: U) => boolean,\n) => {\n  const api = createStore(createState)\n\n  const useBoundStoreWithEqualityFn: any = (\n    selector?: any,\n    equalityFn = defaultEqualityFn,\n  ) => useStoreWithEqualityFn(api, selector, equalityFn)\n\n  Object.assign(useBoundStoreWithEqualityFn, api)\n\n  return useBoundStoreWithEqualityFn\n}\n\nexport const createWithEqualityFn = (<T>(\n  createState: StateCreator<T, [], []> | undefined,\n  defaultEqualityFn?: <U>(a: U, b: U) => boolean,\n) =>\n  createState\n    ? createWithEqualityFnImpl(createState, defaultEqualityFn)\n    : createWithEqualityFnImpl) as CreateWithEqualityFn\n"
  },
  {
    "path": "src/types.d.ts",
    "content": "declare interface ImportMeta {\n  env?: {\n    MODE: string\n  }\n}\n"
  },
  {
    "path": "src/vanilla/shallow.ts",
    "content": "const isIterable = (obj: object): obj is Iterable<unknown> =>\n  Symbol.iterator in obj\n\nconst hasIterableEntries = (\n  value: Iterable<unknown>,\n): value is Iterable<unknown> & {\n  entries(): Iterable<[unknown, unknown]>\n} =>\n  // HACK: avoid checking entries type\n  'entries' in value\n\nconst compareEntries = (\n  valueA: { entries(): Iterable<[unknown, unknown]> },\n  valueB: { entries(): Iterable<[unknown, unknown]> },\n) => {\n  const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries())\n  const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries())\n  if (mapA.size !== mapB.size) {\n    return false\n  }\n  for (const [key, value] of mapA) {\n    if (!mapB.has(key) || !Object.is(value, mapB.get(key))) {\n      return false\n    }\n  }\n  return true\n}\n\n// Ordered iterables\nconst compareIterables = (\n  valueA: Iterable<unknown>,\n  valueB: Iterable<unknown>,\n) => {\n  const iteratorA = valueA[Symbol.iterator]()\n  const iteratorB = valueB[Symbol.iterator]()\n  let nextA = iteratorA.next()\n  let nextB = iteratorB.next()\n  while (!nextA.done && !nextB.done) {\n    if (!Object.is(nextA.value, nextB.value)) {\n      return false\n    }\n    nextA = iteratorA.next()\n    nextB = iteratorB.next()\n  }\n  return !!nextA.done && !!nextB.done\n}\n\nexport function shallow<T>(valueA: T, valueB: T): boolean {\n  if (Object.is(valueA, valueB)) {\n    return true\n  }\n  if (\n    typeof valueA !== 'object' ||\n    valueA === null ||\n    typeof valueB !== 'object' ||\n    valueB === null\n  ) {\n    return false\n  }\n  if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {\n    return false\n  }\n  if (isIterable(valueA) && isIterable(valueB)) {\n    if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {\n      return compareEntries(valueA, valueB)\n    }\n    return compareIterables(valueA, valueB)\n  }\n  // assume plain objects\n  return compareEntries(\n    { entries: () => Object.entries(valueA) },\n    { entries: () => Object.entries(valueB) },\n  )\n}\n"
  },
  {
    "path": "src/vanilla.ts",
    "content": "type SetStateInternal<T> = {\n  _(\n    partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],\n    replace?: false,\n  ): void\n  _(state: T | { _(state: T): T }['_'], replace: true): void\n}['_']\n\nexport interface StoreApi<T> {\n  setState: SetStateInternal<T>\n  getState: () => T\n  getInitialState: () => T\n  subscribe: (listener: (state: T, prevState: T) => void) => () => void\n}\n\nexport type ExtractState<S> = S extends { getState: () => infer T } ? T : never\n\ntype Get<T, K, F> = K extends keyof T ? T[K] : F\n\nexport type Mutate<S, Ms> = number extends Ms['length' & keyof Ms]\n  ? S\n  : Ms extends []\n    ? S\n    : Ms extends [[infer Mi, infer Ma], ...infer Mrs]\n      ? Mutate<StoreMutators<S, Ma>[Mi & StoreMutatorIdentifier], Mrs>\n      : never\n\nexport type StateCreator<\n  T,\n  Mis extends [StoreMutatorIdentifier, unknown][] = [],\n  Mos extends [StoreMutatorIdentifier, unknown][] = [],\n  U = T,\n> = ((\n  setState: Get<Mutate<StoreApi<T>, Mis>, 'setState', never>,\n  getState: Get<Mutate<StoreApi<T>, Mis>, 'getState', never>,\n  store: Mutate<StoreApi<T>, Mis>,\n) => U) & { $$storeMutators?: Mos }\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-object-type\nexport interface StoreMutators<S, A> {}\nexport type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>\n\ntype CreateStore = {\n  <T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(\n    initializer: StateCreator<T, [], Mos>,\n  ): Mutate<StoreApi<T>, Mos>\n\n  <T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(\n    initializer: StateCreator<T, [], Mos>,\n  ) => Mutate<StoreApi<T>, Mos>\n}\n\ntype CreateStoreImpl = <\n  T,\n  Mos extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n  initializer: StateCreator<T, [], Mos>,\n) => Mutate<StoreApi<T>, Mos>\n\nconst createStoreImpl: CreateStoreImpl = (createState) => {\n  type TState = ReturnType<typeof createState>\n  type Listener = (state: TState, prevState: TState) => void\n  let state: TState\n  const listeners: Set<Listener> = new Set()\n\n  const setState: StoreApi<TState>['setState'] = (partial, replace) => {\n    // TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved\n    // https://github.com/microsoft/TypeScript/issues/37663#issuecomment-759728342\n    const nextState =\n      typeof partial === 'function'\n        ? (partial as (state: TState) => TState)(state)\n        : partial\n    if (!Object.is(nextState, state)) {\n      const previousState = state\n      state =\n        (replace ?? (typeof nextState !== 'object' || nextState === null))\n          ? (nextState as TState)\n          : Object.assign({}, state, nextState)\n      listeners.forEach((listener) => listener(state, previousState))\n    }\n  }\n\n  const getState: StoreApi<TState>['getState'] = () => state\n\n  const getInitialState: StoreApi<TState>['getInitialState'] = () =>\n    initialState\n\n  const subscribe: StoreApi<TState>['subscribe'] = (listener) => {\n    listeners.add(listener)\n    // Unsubscribe\n    return () => listeners.delete(listener)\n  }\n\n  const api = { setState, getState, getInitialState, subscribe }\n  const initialState = (state = createState(setState, getState, api))\n  return api as any\n}\n\nexport const createStore = ((createState) =>\n  createState ? createStoreImpl(createState) : createStoreImpl) as CreateStore\n"
  },
  {
    "path": "tests/basic.test.tsx",
    "content": "import {\n  Component as ClassComponent,\n  StrictMode,\n  useEffect,\n  useLayoutEffect,\n  useState,\n} from 'react'\nimport type { ReactNode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport ReactDOM from 'react-dom'\nimport { afterEach, expect, it, vi } from 'vitest'\nimport { create } from 'zustand'\nimport type { StoreApi } from 'zustand'\nimport { createWithEqualityFn } from 'zustand/traditional'\n\nconst consoleError = console.error\n\nafterEach(() => {\n  console.error = consoleError\n})\n\nit('creates a store hook and api object', () => {\n  let params\n  const result = create((...args) => {\n    params = args\n    return { value: null }\n  })\n  expect({ params, result }).toMatchInlineSnapshot(`\n    {\n      \"params\": [\n        [Function],\n        [Function],\n        {\n          \"getInitialState\": [Function],\n          \"getState\": [Function],\n          \"setState\": [Function],\n          \"subscribe\": [Function],\n        },\n      ],\n      \"result\": [Function],\n    }\n  `)\n})\n\ntype CounterState = {\n  count: number\n  inc: () => void\n}\n\nit('uses the store with no args', () => {\n  const useBoundStore = create<CounterState>((set) => ({\n    count: 0,\n    inc: () => set((state) => ({ count: state.count + 1 })),\n  }))\n\n  function Counter() {\n    const { count, inc } = useBoundStore()\n    useEffect(inc, [inc])\n    return <div>count: {count}</div>\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('uses the store with selectors', () => {\n  const useBoundStore = create<CounterState>((set) => ({\n    count: 0,\n    inc: () => set((state) => ({ count: state.count + 1 })),\n  }))\n\n  function Counter() {\n    const count = useBoundStore((s) => s.count)\n    const inc = useBoundStore((s) => s.inc)\n    useEffect(inc, [inc])\n    return <div>count: {count}</div>\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('uses the store with a selector and equality checker', () => {\n  const useBoundStore = createWithEqualityFn(\n    () => ({ item: { value: 0 } }),\n    Object.is,\n  )\n  const { setState } = useBoundStore\n  let renderCount = 0\n\n  function Component() {\n    // Prevent re-render if new value === 1.\n    const item = useBoundStore(\n      (s) => s.item,\n      (_, newItem) => newItem.value === 1,\n    )\n    return (\n      <div>\n        renderCount: {++renderCount}, value: {item.value}\n      </div>\n    )\n  }\n\n  render(\n    <>\n      <Component />\n    </>,\n  )\n\n  expect(screen.getByText('renderCount: 1, value: 0')).toBeInTheDocument()\n\n  // This will not cause a re-render.\n  act(() => setState({ item: { value: 1 } }))\n  expect(screen.getByText('renderCount: 1, value: 0')).toBeInTheDocument()\n\n  // This will cause a re-render.\n  act(() => setState({ item: { value: 2 } }))\n  expect(screen.getByText('renderCount: 2, value: 2')).toBeInTheDocument()\n})\n\nit('only re-renders if selected state has changed', () => {\n  const useBoundStore = create<CounterState>((set) => ({\n    count: 0,\n    inc: () => set((state) => ({ count: state.count + 1 })),\n  }))\n  let counterRenderCount = 0\n  let controlRenderCount = 0\n\n  function Counter() {\n    const count = useBoundStore((state) => state.count)\n    counterRenderCount++\n    return <div>count: {count}</div>\n  }\n\n  function Control() {\n    const inc = useBoundStore((state) => state.inc)\n    controlRenderCount++\n    return <button onClick={inc}>button</button>\n  }\n\n  render(\n    <>\n      <Counter />\n      <Control />\n    </>,\n  )\n\n  fireEvent.click(screen.getByText('button'))\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  expect(counterRenderCount).toBe(2)\n  expect(controlRenderCount).toBe(1)\n})\n\nit('can batch updates', () => {\n  const useBoundStore = create<CounterState>((set) => ({\n    count: 0,\n    inc: () => set((state) => ({ count: state.count + 1 })),\n  }))\n\n  function Counter() {\n    const { count, inc } = useBoundStore()\n    useEffect(() => {\n      ReactDOM.unstable_batchedUpdates(() => {\n        inc()\n        inc()\n      })\n    }, [inc])\n    return <div>count: {count}</div>\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n})\n\nit('can update the selector', () => {\n  type State = { one: string; two: string }\n  type Props = { selector: (state: State) => string }\n  const useBoundStore = create<State>(() => ({\n    one: 'one',\n    two: 'two',\n  }))\n\n  function Component({ selector }: Props) {\n    return <div>{useBoundStore(selector)}</div>\n  }\n\n  const { rerender } = render(\n    <StrictMode>\n      <Component selector={(s) => s.one} />\n    </StrictMode>,\n  )\n  expect(screen.getByText('one')).toBeInTheDocument()\n\n  rerender(\n    <StrictMode>\n      <Component selector={(s) => s.two} />\n    </StrictMode>,\n  )\n  expect(screen.getByText('two')).toBeInTheDocument()\n})\n\nit('can update the equality checker', () => {\n  type State = { value: number }\n  type Props = { equalityFn: (a: State, b: State) => boolean }\n  const useBoundStore = createWithEqualityFn<State>(\n    () => ({ value: 0 }),\n    Object.is,\n  )\n  const { setState } = useBoundStore\n  const selector = (s: State) => s\n\n  let renderCount = 0\n  function Component({ equalityFn }: Props) {\n    const { value } = useBoundStore(selector, equalityFn)\n    return (\n      <div>\n        renderCount: {++renderCount}, value: {value}\n      </div>\n    )\n  }\n\n  // Set an equality checker that always returns false to always re-render.\n  const { rerender } = render(\n    <>\n      <Component equalityFn={() => false} />\n    </>,\n  )\n\n  // This will cause a re-render due to the equality checker.\n  act(() => setState({ value: 0 }))\n  expect(screen.getByText('renderCount: 2, value: 0')).toBeInTheDocument()\n\n  // Set an equality checker that always returns true to never re-render.\n  rerender(\n    <>\n      <Component equalityFn={() => true} />\n    </>,\n  )\n\n  // This will NOT cause a re-render due to the equality checker.\n  act(() => setState({ value: 1 }))\n  expect(screen.getByText('renderCount: 3, value: 0')).toBeInTheDocument()\n})\n\nit('can call useBoundStore with progressively more arguments', () => {\n  type State = { value: number }\n  type Props = {\n    selector?: (state: State) => number\n    equalityFn?: (a: number, b: number) => boolean\n  }\n\n  const useBoundStore = createWithEqualityFn<State>(\n    () => ({ value: 0 }),\n    Object.is,\n  )\n  const { setState } = useBoundStore\n\n  let renderCount = 0\n  function Component({ selector, equalityFn }: Props) {\n    const value = useBoundStore(selector as any, equalityFn)\n    return (\n      <div>\n        renderCount: {++renderCount}, value: {JSON.stringify(value)}\n      </div>\n    )\n  }\n\n  // Render with no args.\n  const { rerender } = render(\n    <>\n      <Component />\n    </>,\n  )\n  expect(\n    screen.getByText('renderCount: 1, value: {\"value\":0}'),\n  ).toBeInTheDocument()\n\n  // Render with selector.\n  rerender(\n    <>\n      <Component selector={(s) => s.value} />\n    </>,\n  )\n  expect(screen.getByText('renderCount: 2, value: 0')).toBeInTheDocument()\n\n  // Render with selector and equality checker.\n  rerender(\n    <>\n      <Component\n        selector={(s) => s.value}\n        equalityFn={(oldV, newV) => oldV > newV}\n      />\n    </>,\n  )\n\n  // Should not cause a re-render because new value is less than previous.\n  act(() => setState({ value: -1 }))\n  expect(screen.getByText('renderCount: 3, value: 0')).toBeInTheDocument()\n\n  act(() => setState({ value: 1 }))\n  expect(screen.getByText('renderCount: 4, value: 1')).toBeInTheDocument()\n})\n\nit('can throw an error in selector', () => {\n  console.error = vi.fn()\n  type State = { value: string | number }\n\n  const initialState: State = { value: 'foo' }\n  const useBoundStore = create<State>(() => initialState)\n  const { setState } = useBoundStore\n  const selector = (s: State) =>\n    // @ts-expect-error This function is supposed to throw an error\n    s.value.toUpperCase()\n\n  class ErrorBoundary extends ClassComponent<\n    { children?: ReactNode | undefined },\n    { hasError: boolean }\n  > {\n    constructor(props: { children?: ReactNode | undefined }) {\n      super(props)\n      this.state = { hasError: false }\n    }\n    static getDerivedStateFromError() {\n      return { hasError: true }\n    }\n    render() {\n      // eslint-disable-next-line testing-library/no-node-access\n      return this.state.hasError ? <div>errored</div> : this.props.children\n    }\n  }\n\n  function Component() {\n    useBoundStore(selector)\n    return <div>no error</div>\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Component />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n\n  act(() => {\n    setState({ value: 123 })\n  })\n  expect(screen.getByText('errored')).toBeInTheDocument()\n})\n\nit('can throw an error in equality checker', () => {\n  console.error = vi.fn()\n  type State = { value: string | number }\n\n  const initialState: State = { value: 'foo' }\n  const useBoundStore = createWithEqualityFn(() => initialState, Object.is)\n  const { setState } = useBoundStore\n  const selector = (s: State) => s\n  const equalityFn = (a: State, b: State) =>\n    // @ts-expect-error This function is supposed to throw an error\n    a.value.trim() === b.value.trim()\n\n  class ErrorBoundary extends ClassComponent<\n    { children?: ReactNode | undefined },\n    { hasError: boolean }\n  > {\n    constructor(props: { children?: ReactNode | undefined }) {\n      super(props)\n      this.state = { hasError: false }\n    }\n    static getDerivedStateFromError() {\n      return { hasError: true }\n    }\n    render() {\n      // eslint-disable-next-line testing-library/no-node-access\n      return this.state.hasError ? <div>errored</div> : this.props.children\n    }\n  }\n\n  function Component() {\n    useBoundStore(selector, equalityFn)\n    return <div>no error</div>\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Component />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n\n  act(() => {\n    setState({ value: 123 })\n  })\n  expect(screen.getByText('errored')).toBeInTheDocument()\n})\n\nit('can get the store', () => {\n  type State = {\n    value: number\n    getState1: () => State\n    getState2: () => State\n  }\n  const { getState } = create<State>((_, get) => ({\n    value: 1,\n    getState1: () => get(),\n    getState2: (): State => getState(),\n  }))\n\n  expect(getState().getState1().value).toBe(1)\n  expect(getState().getState2().value).toBe(1)\n})\n\nit('can set the store', () => {\n  type State = {\n    value: number\n    setState1: StoreApi<State>['setState']\n    setState2: StoreApi<State>['setState']\n  }\n\n  const { setState, getState } = create<State>((set) => ({\n    value: 1,\n    setState1: (v) => set(v),\n    setState2: (v) => setState(v),\n  }))\n\n  getState().setState1({ value: 2 })\n  expect(getState().value).toBe(2)\n  getState().setState2({ value: 3 })\n  expect(getState().value).toBe(3)\n  getState().setState1((s) => ({ value: ++s.value }))\n  expect(getState().value).toBe(4)\n  getState().setState2((s) => ({ value: ++s.value }))\n  expect(getState().value).toBe(5)\n})\n\nit('both NaN should not update', () => {\n  const { setState, subscribe } = create<number>(() => NaN)\n\n  const fn = vi.fn()\n  subscribe(fn)\n\n  setState(NaN)\n\n  expect(fn).not.toBeCalled()\n})\n\nit('can set the store without merging', () => {\n  const { setState, getState } = create<{ a: number } | { b: number }>(\n    (_set) => ({\n      a: 1,\n    }),\n  )\n\n  // Should override the state instead of merging.\n  setState({ b: 2 }, true)\n  expect(getState()).toEqual({ b: 2 })\n})\n\nit('only calls selectors when necessary with static selector', () => {\n  type State = { a: number; b: number }\n  const useBoundStore = createWithEqualityFn<State>(() => ({ a: 0, b: 0 }))\n  const { setState } = useBoundStore\n  let staticSelectorCallCount = 0\n\n  function staticSelector(s: State) {\n    staticSelectorCallCount++\n    return s.a\n  }\n\n  function Component() {\n    useBoundStore(staticSelector)\n    return (\n      <>\n        <div>static: {staticSelectorCallCount}</div>\n      </>\n    )\n  }\n\n  const { rerender } = render(\n    <>\n      <Component />\n    </>,\n  )\n  expect(screen.getByText('static: 1')).toBeInTheDocument()\n\n  rerender(\n    <>\n      <Component />\n    </>,\n  )\n  expect(screen.getByText('static: 1')).toBeInTheDocument()\n\n  act(() => setState({ a: 1, b: 1 }))\n  expect(screen.getByText('static: 2')).toBeInTheDocument()\n})\n\nit('only calls selectors when necessary (traditional)', () => {\n  type State = { a: number; b: number }\n  const useBoundStore = createWithEqualityFn<State>(() => ({ a: 0, b: 0 }))\n  const { setState } = useBoundStore\n  let inlineSelectorCallCount = 0\n  let staticSelectorCallCount = 0\n\n  function staticSelector(s: State) {\n    staticSelectorCallCount++\n    return s.a\n  }\n\n  function Component() {\n    useBoundStore((s) => (inlineSelectorCallCount++, s.b))\n    useBoundStore(staticSelector)\n    return (\n      <>\n        <div>inline: {inlineSelectorCallCount}</div>\n        <div>static: {staticSelectorCallCount}</div>\n      </>\n    )\n  }\n\n  const { rerender } = render(\n    <>\n      <Component />\n    </>,\n  )\n  expect(screen.getByText('inline: 1')).toBeInTheDocument()\n  expect(screen.getByText('static: 1')).toBeInTheDocument()\n\n  rerender(\n    <>\n      <Component />\n    </>,\n  )\n  expect(screen.getByText('inline: 2')).toBeInTheDocument()\n  expect(screen.getByText('static: 1')).toBeInTheDocument()\n\n  act(() => setState({ a: 1, b: 1 }))\n  expect(screen.getByText('inline: 4')).toBeInTheDocument()\n  expect(screen.getByText('static: 2')).toBeInTheDocument()\n})\n\nit('ensures parent components subscribe before children', () => {\n  type State = {\n    childItems: { [key: string]: { text: string } }\n  }\n  type Props = { id: string }\n  const useBoundStore = create<State>(() => ({\n    childItems: {\n      '1': { text: 'child 1' },\n      '2': { text: 'child 2' },\n    },\n  }))\n  const api = useBoundStore\n\n  function changeState() {\n    api.setState({\n      childItems: {\n        '3': { text: 'child 3' },\n      },\n    })\n  }\n\n  function Child({ id }: Props) {\n    const text = useBoundStore((s) => s.childItems[id]?.text)\n    return <div>{text}</div>\n  }\n\n  function Parent() {\n    const childStates = useBoundStore((s) => s.childItems)\n    return (\n      <>\n        <button onClick={changeState}>change state</button>\n        {Object.keys(childStates).map((id) => (\n          <Child id={id} key={id} />\n        ))}\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  fireEvent.click(screen.getByText('change state'))\n\n  expect(screen.getByText('child 3')).toBeInTheDocument()\n})\n\n// https://github.com/pmndrs/zustand/issues/84\nit('ensures the correct subscriber is removed on unmount', () => {\n  const useBoundStore = create(() => ({ count: 0 }))\n  const api = useBoundStore\n\n  function increment() {\n    api.setState(({ count }) => ({ count: count + 1 }))\n  }\n\n  function Count() {\n    const c = useBoundStore((s) => s.count)\n    return <div>count: {c}</div>\n  }\n\n  function CountWithInitialIncrement() {\n    useLayoutEffect(increment, [])\n    return <Count />\n  }\n\n  function Component() {\n    const [Counter, setCounter] = useState(() => CountWithInitialIncrement)\n    useLayoutEffect(() => {\n      // eslint-disable-next-line react-hooks/set-state-in-effect\n      setCounter(() => Count)\n    }, [])\n    return (\n      <>\n        <Counter />\n        <Count />\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Component />\n    </>,\n  )\n\n  expect(screen.getAllByText('count: 1').length).toBe(2)\n\n  act(increment)\n\n  expect(screen.getAllByText('count: 2').length).toBe(2)\n})\n\n// https://github.com/pmndrs/zustand/issues/86\nit('ensures a subscriber is not mistakenly overwritten', () => {\n  const useBoundStore = create(() => ({ count: 0 }))\n  const { setState } = useBoundStore\n\n  function Count1() {\n    const c = useBoundStore((s) => s.count)\n    return <div>count1: {c}</div>\n  }\n\n  function Count2() {\n    const c = useBoundStore((s) => s.count)\n    return <div>count2: {c}</div>\n  }\n\n  // Add 1st subscriber.\n  const { rerender } = render(\n    <StrictMode>\n      <Count1 />\n    </StrictMode>,\n  )\n\n  // Replace 1st subscriber with another.\n  rerender(\n    <StrictMode>\n      <Count2 />\n    </StrictMode>,\n  )\n\n  // Add 2 additional subscribers.\n  rerender(\n    <StrictMode>\n      <Count2 />\n      <Count1 />\n      <Count1 />\n    </StrictMode>,\n  )\n\n  // Call all subscribers\n  act(() => setState({ count: 1 }))\n\n  expect(screen.getAllByText('count1: 1').length).toBe(2)\n  expect(screen.getAllByText('count2: 1').length).toBe(1)\n})\n\nit('works with non-object state', () => {\n  const useCount = create(() => 1)\n  const inc = () => useCount.setState((c) => c + 1)\n\n  const Counter = () => {\n    const count = useCount()\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={inc}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n})\n\nit('works with \"undefined\" state', () => {\n  const useUndefined = create(() => undefined)\n\n  const Component = () => {\n    const str = useUndefined((v) => v || 'undefined')\n    return <div>str: {str}</div>\n  }\n\n  render(\n    <StrictMode>\n      <Component />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('str: undefined')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/devtools.test.tsx",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport type { Mock } from 'vitest'\nimport { devtools, redux } from 'zustand/middleware'\nimport { createStore } from 'zustand/vanilla'\nimport type { StoreApi } from 'zustand/vanilla'\n\ntype TupleOfEqualLengthH<\n  Arr extends unknown[],\n  T,\n  Acc extends T[],\n> = Arr extends [unknown, ...infer Rest]\n  ? TupleOfEqualLengthH<Rest, T, [T, ...Acc]>\n  : Acc\ntype TupleOfEqualLength<Arr extends unknown[], T> = number extends Arr['length']\n  ? T[]\n  : TupleOfEqualLengthH<Arr, T, []>\n\ntype Connection = {\n  subscribers: ((message: unknown) => void)[]\n  api: {\n    subscribe: Mock<(f: (message: unknown) => void) => () => void>\n    unsubscribe: Mock<any>\n    send: Mock<any>\n    init: Mock<any>\n    error: Mock<any>\n    dispatch?: Mock<any>\n  }\n}\nconst namedConnections = new Map<string | undefined, Connection>()\nconst unnamedConnections = new Map<string, Connection>()\n\nfunction assertAllAreDefined<T>(arr: (T | undefined)[]): asserts arr is T[] {\n  if (arr.some((e) => e === undefined)) {\n    throw new Error()\n  }\n}\nfunction getNamedConnectionApis<Keys extends (string | undefined)[]>(\n  ...keys: Keys\n) {\n  const apis = keys.map((k) => namedConnections.get(k)?.api)\n  assertAllAreDefined(apis)\n  return apis as TupleOfEqualLength<Keys, Connection['api']>\n}\nfunction getNamedConnectionSubscribers<Keys extends (string | undefined)[]>(\n  ...keys: Keys\n) {\n  const subscribers = keys.map((k) => {\n    const subs = namedConnections.get(k)?.subscribers\n    if (subs?.length !== 1) throw new Error()\n    return subs[0]\n  })\n  assertAllAreDefined(subscribers)\n  return subscribers as TupleOfEqualLength<\n    Keys,\n    Connection['subscribers'][number]\n  >\n}\nfunction getUnnamedConnectionApis<Keys extends string[]>(...keys: Keys) {\n  const apis = keys.map((k) => unnamedConnections.get(k)?.api)\n  assertAllAreDefined(apis)\n  return apis as TupleOfEqualLength<Keys, Connection['api']>\n}\nfunction getUnnamedConnectionSubscribers<Keys extends string[]>(...keys: Keys) {\n  const subscribers = keys.map((k) => {\n    const subs = unnamedConnections.get(k)?.subscribers\n    if (!subs) {\n      throw new Error()\n    }\n    return subs[0]\n  })\n  assertAllAreDefined(subscribers)\n  return subscribers as TupleOfEqualLength<\n    Keys,\n    Connection['subscribers'][number]\n  >\n}\n\nfunction getKeyFromOptions(options: any): string | undefined {\n  let key: string | undefined = options?.name\n  if (options?.testStore) {\n    key = `${options?.name}|${options?.testStore}`\n  }\n  return key\n}\n\nconst extensionConnector = {\n  connect: vi.fn((options: any) => {\n    const key = getKeyFromOptions(options)\n    //console.log('options', options)\n    const areNameUndefinedMapsNeeded =\n      options.testConnectionId !== undefined && options?.name === undefined\n    const connectionMap = areNameUndefinedMapsNeeded\n      ? unnamedConnections\n      : namedConnections\n    const subscribers: Connection['subscribers'] = []\n    const api: Connection['api'] = {\n      subscribe: vi.fn((f: (m: unknown) => void) => {\n        subscribers.push(f)\n        return () => {}\n      }),\n      unsubscribe: vi.fn(() => {\n        connectionMap.delete(\n          areNameUndefinedMapsNeeded ? options.testConnectionId : key,\n        )\n      }),\n      send: vi.fn(),\n      init: vi.fn(),\n      error: vi.fn(),\n    }\n    connectionMap.set(\n      areNameUndefinedMapsNeeded ? options.testConnectionId : key,\n      {\n        subscribers,\n        api,\n      },\n    )\n    return api\n  }),\n}\n;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = extensionConnector\n\nbeforeEach(() => {\n  vi.resetModules()\n  extensionConnector.connect.mockClear()\n  namedConnections.clear()\n  unnamedConnections.clear()\n})\n\nit('connects to the extension by passing the options and initializes', async () => {\n  const options = { name: 'test', foo: 'bar' }\n  const initialState = { count: 0 }\n  createStore(devtools(() => initialState, { enabled: true, ...options }))\n\n  expect(extensionConnector.connect).toHaveBeenLastCalledWith(options)\n\n  const [conn] = getNamedConnectionApis(options.name)\n  expect(conn.init).toHaveBeenLastCalledWith(initialState)\n})\n\ndescribe('If there is no extension installed...', () => {\n  let savedConsoleWarn: any\n  beforeEach(() => {\n    savedConsoleWarn = console.warn\n    console.warn = vi.fn()\n    ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = undefined\n  })\n  afterEach(() => {\n    console.warn = savedConsoleWarn\n    ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = extensionConnector\n  })\n\n  it('does not throw', async () => {\n    expect(() => {\n      createStore(devtools(() => ({ count: 0 })))\n    }).not.toThrow()\n  })\n\n  it('does not warn', async () => {\n    createStore(devtools(() => ({ count: 0 })))\n    expect(console.warn).not.toBeCalled()\n  })\n})\n\ndescribe('When state changes...', () => {\n  it(\"sends { type: setStateName || 'anonymous`, ...rest } as the action with current state\", async () => {\n    const options = {\n      name: 'testOptionsName',\n      enabled: true,\n    }\n    const api = createStore(devtools(() => ({ count: 0, foo: 'bar' }), options))\n\n    api.setState({ count: 10 }, false, 'testSetStateName')\n    const [connection] = getNamedConnectionApis(options.name)\n    expect(connection.send).toHaveBeenLastCalledWith(\n      { type: 'testSetStateName' },\n      { count: 10, foo: 'bar' },\n    )\n\n    api.setState({ count: 15 }, false, {\n      type: 'testSetStateName',\n      payload: 15,\n    })\n    expect(connection.send).toHaveBeenLastCalledWith(\n      { type: 'testSetStateName', payload: 15 },\n      { count: 15, foo: 'bar' },\n    )\n\n    api.setState({ count: 5, foo: 'baz' }, true)\n    expect(connection.send).toHaveBeenLastCalledWith(\n      { type: 'anonymous' },\n      { count: 5, foo: 'baz' },\n    )\n  })\n})\n\ndescribe('When state changes with automatic setter inferring...', () => {\n  it(\"sends { type: setStateName || 'setCount`, ...rest } as the action with current state\", async () => {\n    const options = {\n      name: 'testOptionsName',\n      enabled: true,\n    }\n\n    const api = createStore<{\n      count: number\n      setCount: (count: number) => void\n    }>()(\n      devtools(\n        (set) => ({\n          count: 0,\n          setCount: (newCount: number) => {\n            set({ count: newCount })\n          },\n        }),\n        options,\n      ),\n    )\n\n    api.getState().setCount(10)\n    const [connection] = getNamedConnectionApis(options.name)\n    expect(connection.send).toHaveBeenLastCalledWith(\n      { type: expect.stringMatching(/^(Object\\.setCount|anonymous)$/) },\n      { count: 10, setCount: expect.any(Function) },\n    )\n  })\n})\n\ndescribe('when it receives a message of type...', () => {\n  describe('ACTION...', () => {\n    it('does nothing', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      const setState = vi.spyOn(api, 'setState')\n\n      const [subscriber] = getNamedConnectionSubscribers(undefined)\n      subscriber({\n        type: 'ACTION',\n        payload: '{ \"type\": \"INCREMENT\" }',\n      })\n\n      expect(api.getState()).toBe(initialState)\n      expect(setState).not.toBeCalled()\n    })\n\n    it('unless action type is __setState', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      connectionSubscriber({\n        type: 'ACTION',\n        payload: '{ \"type\": \"__setState\", \"state\": { \"foo\": \"bar\" } }',\n      })\n\n      expect(api.getState()).toStrictEqual({ ...initialState, foo: 'bar' })\n    })\n\n    it('does nothing even if there is `api.dispatch`', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      ;(api as any).dispatch = vi.fn()\n      const setState = vi.spyOn(api, 'setState')\n\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      connectionSubscriber({\n        type: 'ACTION',\n        payload: '{ \"type\": \"INCREMENT\" }',\n      })\n\n      expect(api.getState()).toBe(initialState)\n      expect(setState).not.toBeCalled()\n      expect((api as any).dispatch).not.toBeCalled()\n    })\n\n    it('dispatches with `api.dispatch` when `api.dispatchFromDevtools` is set to true', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      ;(api as any).dispatch = vi.fn()\n      ;(api as any).dispatchFromDevtools = true\n      const setState = vi.spyOn(api, 'setState')\n\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      connectionSubscriber({\n        type: 'ACTION',\n        payload: '{ \"type\": \"INCREMENT\" }',\n      })\n\n      expect(api.getState()).toBe(initialState)\n      expect(setState).not.toBeCalled()\n      expect((api as any).dispatch).toHaveBeenLastCalledWith({\n        type: 'INCREMENT',\n      })\n    })\n\n    it('does not throw for unsupported payload', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      ;(api as any).dispatch = vi.fn()\n      ;(api as any).dispatchFromDevtools = true\n      const setState = vi.spyOn(api, 'setState')\n      const originalConsoleError = console.error\n      console.error = vi.fn()\n\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      expect(() => {\n        connectionSubscriber({\n          type: 'ACTION',\n          payload: 'this.increment()',\n        })\n      }).not.toThrow()\n\n      expect(console.error).toHaveBeenLastCalledWith(\n        '[zustand devtools middleware] Could not parse the received json',\n        (() => {\n          try {\n            JSON.parse('this.increment()')\n          } catch (e) {\n            return e\n          }\n        })(),\n      )\n\n      expect(() => {\n        connectionSubscriber({\n          type: 'ACTION',\n          payload: { name: 'increment', args: [] },\n        })\n      }).not.toThrow()\n\n      expect(console.error).toHaveBeenLastCalledWith(\n        '[zustand devtools middleware] Unsupported action format',\n      )\n\n      expect(api.getState()).toBe(initialState)\n      expect(setState).not.toBeCalled()\n      expect((api as any).dispatch).not.toBeCalled()\n\n      console.error = originalConsoleError\n    })\n  })\n\n  describe('DISPATCH and payload of type...', () => {\n    it('RESET, it inits with initial state', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      api.setState({ count: 1 })\n\n      const [connection] = getNamedConnectionApis(undefined)\n      connection.send.mockClear()\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      connectionSubscriber({\n        type: 'DISPATCH',\n        payload: { type: 'RESET' },\n      })\n\n      expect(api.getState()).toStrictEqual(initialState)\n      expect(connection.init).toHaveBeenLastCalledWith(initialState)\n      expect(connection.send).not.toBeCalled()\n    })\n\n    it('COMMIT, it inits with current state', async () => {\n      const initialState = { count: 0 }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      api.setState({ count: 2 })\n      const currentState = api.getState()\n\n      const [connection] = getNamedConnectionApis(undefined)\n      connection.send.mockClear()\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      connectionSubscriber({\n        type: 'DISPATCH',\n        payload: { type: 'COMMIT' },\n      })\n\n      expect(connection.init).toHaveBeenLastCalledWith(currentState)\n      expect(connection.send).not.toBeCalled()\n    })\n\n    describe('ROLLBACK...', () => {\n      it('updates state without recording and inits with `message.state`', async () => {\n        const initialState = { count: 0, increment: () => {} }\n        const api = createStore(devtools(() => initialState, { enabled: true }))\n        const newState = { foo: 'bar' }\n\n        const [connection] = getNamedConnectionApis(undefined)\n        connection.send.mockClear()\n        const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n        connectionSubscriber({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: JSON.stringify(newState),\n        })\n\n        expect(api.getState()).toStrictEqual({ ...initialState, ...newState })\n        expect(connection.init).toHaveBeenLastCalledWith({\n          ...initialState,\n          ...newState,\n        })\n        expect(connection.send).not.toBeCalled()\n      })\n\n      it('does not throw for unparsable `message.state`', async () => {\n        const increment = () => {}\n        const initialState = { count: 0, increment }\n        const api = createStore(devtools(() => initialState, { enabled: true }))\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const [connection] = getNamedConnectionApis(undefined)\n        connection.init.mockClear()\n        connection.send.mockClear()\n        const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n        connectionSubscriber({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: 'foobar',\n        })\n\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        expect(api.getState()).toBe(initialState)\n        expect(connection.init).not.toBeCalled()\n        expect(connection.send).not.toBeCalled()\n\n        console.error = originalConsoleError\n      })\n    })\n\n    describe('JUMP_TO_STATE...', () => {\n      const increment = () => {}\n      it('updates state without recording with `message.state`', async () => {\n        const initialState = { count: 0, increment }\n        const api = createStore(devtools(() => initialState, { enabled: true }))\n        const newState = { foo: 'bar' }\n\n        const [connection] = getNamedConnectionApis(undefined)\n        connection.send.mockClear()\n        const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n        connectionSubscriber({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: JSON.stringify(newState),\n        })\n        expect(api.getState()).toStrictEqual({ ...initialState, ...newState })\n        expect(connection.send).not.toBeCalled()\n      })\n\n      it('does not throw for unparsable `message.state`', async () => {\n        const initialState = { count: 0, increment: () => {} }\n        const api = createStore(devtools(() => initialState, { enabled: true }))\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const [connection] = getNamedConnectionApis(undefined)\n        connection.send.mockClear()\n        const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n        connectionSubscriber({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: 'foobar',\n        })\n\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        expect(api.getState()).toBe(initialState)\n        expect(connection.send).not.toBeCalled()\n\n        console.error = originalConsoleError\n      })\n    })\n\n    describe('JUMP_TO_ACTION...', () => {\n      it('updates state without recording with `message.state`', async () => {\n        const initialState = { count: 0, increment: () => {} }\n        const api = createStore(devtools(() => initialState, { enabled: true }))\n        const newState = { foo: 'bar' }\n\n        const [connection] = getNamedConnectionApis(undefined)\n        connection.send.mockClear()\n        const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n        connectionSubscriber({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: JSON.stringify(newState),\n        })\n        expect(api.getState()).toStrictEqual({ ...initialState, ...newState })\n        expect(connection.send).not.toBeCalled()\n      })\n\n      it('does not throw for unparsable `message.state`', async () => {\n        const increment = () => {}\n        const initialState = { count: 0, increment }\n        const api = createStore(devtools(() => initialState, { enabled: true }))\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const [connection] = getNamedConnectionApis(undefined)\n        connection.send.mockClear()\n        const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n        connectionSubscriber({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: 'foobar',\n        })\n\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        expect(api.getState()).toBe(initialState)\n        expect(connection.send).not.toBeCalled()\n\n        console.error = originalConsoleError\n      })\n    })\n\n    it('IMPORT_STATE, it updates state without recording and inits the last computedState', async () => {\n      const initialState = { count: 0, increment: () => {} }\n      const api = createStore(devtools(() => initialState, { enabled: true }))\n      const nextLiftedState = {\n        computedStates: [{ state: { count: 4 } }, { state: { count: 5 } }],\n      }\n\n      const [connection] = getNamedConnectionApis(undefined)\n      connection.send.mockClear()\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      connectionSubscriber({\n        type: 'DISPATCH',\n        payload: {\n          type: 'IMPORT_STATE',\n          nextLiftedState,\n        },\n      })\n      expect(api.getState()).toStrictEqual({\n        ...initialState,\n        ...nextLiftedState.computedStates.slice(-1)[0]?.state,\n      })\n      expect(connection.send).toHaveBeenLastCalledWith(null, nextLiftedState)\n    })\n\n    it('PAUSE_RECORDING, it toggles the sending of actions', async () => {\n      const api = createStore(devtools(() => ({ count: 0 }), { enabled: true }))\n\n      api.setState({ count: 1 }, false, 'increment')\n      const [connection] = getNamedConnectionApis(undefined)\n      const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n      expect(connection.send).toHaveBeenLastCalledWith(\n        { type: 'increment' },\n        { count: 1 },\n      )\n      connectionSubscriber({\n        type: 'DISPATCH',\n        payload: { type: 'PAUSE_RECORDING' },\n      })\n\n      api.setState({ count: 2 }, false, 'increment')\n      expect(connection.send).toHaveBeenLastCalledWith(\n        { type: 'increment' },\n        { count: 1 },\n      )\n      connectionSubscriber({\n        type: 'DISPATCH',\n        payload: { type: 'PAUSE_RECORDING' },\n      })\n\n      api.setState({ count: 3 }, false, 'increment')\n      expect(connection.send).toHaveBeenLastCalledWith(\n        { type: 'increment' },\n        { count: 3 },\n      )\n    })\n  })\n})\n\ndescribe('with redux middleware', () => {\n  let api: StoreApi<{\n    count: number\n    dispatch: (\n      action: { type: 'INCREMENT' } | { type: 'DECREMENT' },\n    ) => { type: 'INCREMENT' } | { type: 'DECREMENT' }\n  }>\n\n  it('works as expected', async () => {\n    api = createStore(\n      devtools(\n        redux(\n          (\n            { count },\n            { type }: { type: 'INCREMENT' } | { type: 'DECREMENT' },\n          ) => ({\n            count: count + (type === 'INCREMENT' ? 1 : -1),\n          }),\n          { count: 0 },\n        ),\n        { enabled: true },\n      ),\n    )\n    ;(api as any).dispatch({ type: 'INCREMENT' })\n    ;(api as any).dispatch({ type: 'INCREMENT' })\n    const [connection] = getNamedConnectionApis(undefined)\n    const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n    connectionSubscriber({\n      type: 'ACTION',\n      payload: JSON.stringify({ type: 'DECREMENT' }),\n    })\n\n    expect(connection.init.mock.calls).toMatchObject([\n      [{ count: 0 }] as unknown as Record<string, unknown>,\n    ])\n    expect(connection.send.mock.calls).toMatchObject([\n      [{ type: 'INCREMENT' }, { count: 1 }] as unknown as Record<\n        string,\n        unknown\n      >,\n      [{ type: 'INCREMENT' }, { count: 2 }] as unknown as Record<\n        string,\n        unknown\n      >,\n      [{ type: 'DECREMENT' }, { count: 1 }] as unknown as Record<\n        string,\n        unknown\n      >,\n    ])\n    expect(api.getState()).toMatchObject({ count: 1 })\n  })\n\n  it('[DEV-ONLY] warns about misusage', () => {\n    const originalConsoleWarn = console.warn\n    console.warn = vi.fn()\n    ;(api as any).dispatch({ type: '__setState' as any })\n    expect(console.warn).toHaveBeenLastCalledWith(\n      '[zustand devtools middleware] \"__setState\" action type is reserved ' +\n        'to set state from the devtools. Avoid using it.',\n    )\n\n    console.warn = originalConsoleWarn\n  })\n})\n\ndescribe('different envs', () => {\n  let savedConsoleWarn: any\n  beforeEach(() => {\n    savedConsoleWarn = console.warn\n    console.warn = vi.fn()\n  })\n  afterEach(() => {\n    console.warn = savedConsoleWarn\n  })\n\n  it('works in non-browser env', async () => {\n    const originalWindow = globalThis.window\n    globalThis.window = undefined as any\n\n    expect(() => {\n      createStore(devtools(() => ({ count: 0 }), { enabled: true }))\n    }).not.toThrow()\n\n    globalThis.window = originalWindow\n  })\n\n  it('works in react native env', async () => {\n    const originalWindow = globalThis.window\n    globalThis.window = {} as any\n\n    expect(() => {\n      createStore(devtools(() => ({ count: 0 }), { enabled: true }))\n    }).not.toThrow()\n\n    globalThis.window = originalWindow\n  })\n})\n\nit('preserves isRecording after setting from devtools', async () => {\n  const api = createStore(devtools(() => ({ count: 0 }), { enabled: true }))\n  const [connection] = getNamedConnectionApis(undefined)\n  const [connectionSubscriber] = getNamedConnectionSubscribers(undefined)\n  connectionSubscriber({\n    type: 'DISPATCH',\n    payload: { type: 'PAUSE_RECORDING' },\n  })\n  connectionSubscriber({\n    type: 'ACTION',\n    payload: '{ \"type\": \"__setState\", \"state\": { \"foo\": \"bar\" } }',\n  })\n\n  api.setState({ count: 1 })\n  expect(connection.send).not.toBeCalled()\n})\n\n/* features:\n * [] if name is undefined - use multiple devtools connections.\n * [] if name and store is defined - use connection for specific 'name'.\n * [] if two stores are connected to one 'name' group and.\n *      another connected to another 'name' group, then feature should work\n * [] check actions with this feature, for multiple stores that store prefixes are added -\n * [] - reset\n * [] - commit\n * [] - rollback\n * [] - jump to state, jump to action\n * [] - import state\n **/\n\ndescribe('when redux connection was called on multiple stores with `name` undefined in `devtools` options', () => {\n  it('should create separate connection for each devtools store with .connect call', async () => {\n    const options1 = { foo: 'bar', testConnectionId: 'asdf' }\n    const options2 = { foo: 'barr', testConnectionId: '123asd' }\n    const initialState1 = { count: 0 }\n    const initialState2 = { count1: 1 }\n\n    createStore(devtools(() => initialState1, { enabled: true, ...options1 }))\n    createStore(devtools(() => initialState2, { enabled: true, ...options2 }))\n\n    expect(extensionConnector.connect).toHaveBeenNthCalledWith(1, options1)\n    expect(extensionConnector.connect).toHaveBeenNthCalledWith(2, options2)\n  })\n\n  it('should call .init on each different connection object', async () => {\n    const options1 = { foo: 'bar', testConnectionId: 'asdf' }\n    const options2 = { foo: 'barr', testConnectionId: '123asd' }\n    const initialState1 = { count: 0 }\n    const initialState2 = { count1: 1 }\n\n    createStore(devtools(() => initialState1, { enabled: true, ...options1 }))\n    createStore(devtools(() => initialState2, { enabled: true, ...options2 }))\n\n    const [conn1, conn2] = getUnnamedConnectionApis(\n      options1.testConnectionId,\n      options2.testConnectionId,\n    )\n    expect(conn1.init).toHaveBeenCalledWith(initialState1)\n    expect(conn2.init).toHaveBeenCalledWith(initialState2)\n  })\n\n  describe('when `store` property was provided in `devtools` call in options', () => {\n    it('should create single connection for all internal calls of .connect and `store` is not passed to .connect', async () => {\n      const { devtools: newDevtools } = await import('zustand/middleware')\n\n      const options1 = { store: 'store1123', foo: 'bar1' }\n      const options2 = { store: 'store2313132', foo: 'bar2' }\n      const initialState1 = { count: 0 }\n      const initialState2 = { count1: 1 }\n\n      createStore(\n        newDevtools(() => initialState1, { enabled: true, ...options1 }),\n      )\n      createStore(\n        newDevtools(() => initialState2, { enabled: true, ...options2 }),\n      )\n\n      expect(extensionConnector.connect).toHaveBeenCalledTimes(1)\n      expect(extensionConnector.connect).toHaveBeenCalledWith({\n        foo: options1.foo,\n      })\n    })\n\n    it('should call `.init` on single connection with combined states after each `create(devtools` call', async () => {\n      const { devtools: newDevtools } = await import('zustand/middleware')\n\n      const options1 = { store: 'store12' }\n      const options2 = { store: 'store21' }\n      const initialState1 = { count1: 0 }\n      const initialState2 = { count2: 1 }\n\n      createStore(\n        newDevtools(() => initialState1, { enabled: true, ...options1 }),\n      )\n      createStore(\n        newDevtools(() => initialState2, { enabled: true, ...options2 }),\n      )\n\n      expect(extensionConnector.connect).toHaveBeenCalledTimes(1)\n      const [connection] = getNamedConnectionApis(undefined)\n      expect(connection.init).toHaveBeenCalledTimes(2)\n      expect(connection.init).toHaveBeenNthCalledWith(1, {\n        [options1.store]: initialState1,\n      })\n      expect(connection.init).toHaveBeenNthCalledWith(2, {\n        [options1.store]: initialState1,\n        [options2.store]: initialState2,\n      })\n    })\n  })\n})\n\ndescribe('when redux connection was called on multiple stores with `name` provided in `devtools` options', () => {\n  describe('when same `name` is provided to all stores in devtools options', () => {\n    it('should call .connect of redux extension with `name` that was passed from `devtools` options', async () => {\n      const connectionName = 'test'\n      const options1 = { name: connectionName, store: 'store1123', foo: 'bar1' }\n      const options2 = { name: connectionName, store: 'store1414', foo: 'bar1' }\n      const initialState1 = { count: 0 }\n      const initialState2 = { count: 2 }\n\n      createStore(devtools(() => initialState1, { enabled: true, ...options1 }))\n      createStore(devtools(() => initialState2, { enabled: true, ...options2 }))\n\n      expect(extensionConnector.connect).toHaveBeenCalledTimes(1)\n      expect(extensionConnector.connect).toHaveBeenCalledWith({\n        foo: options1.foo,\n        name: connectionName,\n      })\n    })\n  })\n\n  describe('when different `name` props were provided for different group of stores in devtools options', () => {\n    it('should call .connect of redux extension with `name` that was passed from `devtools` options', async () => {\n      const connectionNameGroup1 = 'test1'\n      const connectionNameGroup2 = 'test2'\n      const options1 = {\n        name: connectionNameGroup1,\n        store: 'store1123',\n        foo: 'bar2',\n      }\n      const options2 = {\n        name: connectionNameGroup1,\n        store: 'store1232',\n        foo: 'bar3',\n      }\n      const options3 = {\n        name: connectionNameGroup2,\n        store: 'store61661',\n        foo: 'bar4',\n      }\n      const options4 = {\n        name: connectionNameGroup2,\n        store: 'store14632',\n        foo: 'bar5',\n      }\n      const initialState1 = { count: 0 }\n      const initialState2 = { count: 2 }\n      const initialState3 = { count: 5 }\n      const initialState4 = { count: 7 }\n\n      createStore(devtools(() => initialState1, { enabled: true, ...options1 }))\n      createStore(devtools(() => initialState2, { enabled: true, ...options2 }))\n      createStore(devtools(() => initialState3, { enabled: true, ...options3 }))\n      createStore(devtools(() => initialState4, { enabled: true, ...options4 }))\n\n      expect(extensionConnector.connect).toHaveBeenCalledTimes(2)\n      expect(extensionConnector.connect).toHaveBeenNthCalledWith(1, {\n        foo: options1.foo,\n        name: connectionNameGroup1,\n      })\n      expect(extensionConnector.connect).toHaveBeenNthCalledWith(2, {\n        foo: options3.foo,\n        name: connectionNameGroup2,\n      })\n    })\n\n    it('should call `.init` on single connection with combined states after each `create(devtools` call', async () => {\n      const { devtools: newDevtools } = await import('zustand/middleware')\n      const connectionNameGroup1 = 'test1'\n      const connectionNameGroup2 = 'test2'\n      const options1 = {\n        name: connectionNameGroup1,\n        store: 'store1123',\n        foo: 'bar2',\n      }\n      const options2 = {\n        name: connectionNameGroup1,\n        store: 'store1232',\n        foo: 'bar3',\n      }\n      const options3 = {\n        name: connectionNameGroup2,\n        store: 'store61661',\n        foo: 'bar4',\n      }\n      const options4 = {\n        name: connectionNameGroup2,\n        store: 'store14632',\n        foo: 'bar5',\n      }\n      const initialState1 = { count: 0 }\n      const initialState2 = { count: 2 }\n      const initialState3 = { count: 5 }\n      const initialState4 = { count: 7 }\n\n      createStore(\n        newDevtools(() => initialState1, { enabled: true, ...options1 }),\n      )\n      createStore(\n        newDevtools(() => initialState2, { enabled: true, ...options2 }),\n      )\n      createStore(\n        newDevtools(() => initialState3, { enabled: true, ...options3 }),\n      )\n      createStore(\n        newDevtools(() => initialState4, { enabled: true, ...options4 }),\n      )\n\n      expect(extensionConnector.connect).toHaveBeenCalledTimes(2)\n      const [connection1, connection2] = getNamedConnectionApis(\n        connectionNameGroup1,\n        connectionNameGroup2,\n      )\n      expect(connection1.init).toHaveBeenCalledTimes(2)\n      expect(connection1.init).toHaveBeenNthCalledWith(1, {\n        [options1.store]: initialState1,\n      })\n      expect(connection1.init).toHaveBeenNthCalledWith(2, {\n        [options1.store]: initialState1,\n        [options2.store]: initialState2,\n      })\n      expect(connection2.init).toHaveBeenCalledTimes(2)\n      expect(connection2.init).toHaveBeenNthCalledWith(1, {\n        [options3.store]: initialState3,\n      })\n      expect(connection2.init).toHaveBeenNthCalledWith(2, {\n        [options3.store]: initialState3,\n        [options4.store]: initialState4,\n      })\n    })\n\n    it('preserves isRecording after setting from devtools on proper connection subscriber', async () => {\n      const options1 = { name: 'asdf1' }\n      const options2 = { name: 'asdf2' }\n      const api1 = createStore(\n        devtools(() => ({ count: 0 }), { enabled: true, ...options1 }),\n      )\n      createStore(\n        devtools(() => ({ count: 0 }), { enabled: true, ...options2 }),\n      )\n      const connections = getNamedConnectionApis(options1.name, options2.name)\n      const [connectionSubscriber] = getNamedConnectionSubscribers(\n        options1.name,\n      )\n      connectionSubscriber({\n        type: 'DISPATCH',\n        payload: { type: 'PAUSE_RECORDING' },\n      })\n      connectionSubscriber({\n        type: 'ACTION',\n        payload: '{ \"type\": \"__setState\", \"state\": { \"foo\": \"bar\" } }',\n      })\n\n      api1.setState({ count: 1 })\n      connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n    })\n\n    describe('with redux middleware', () => {\n      let api1: StoreApi<{\n        count: number\n        dispatch: (\n          action: { type: 'INCREMENT' } | { type: 'DECREMENT' },\n        ) => { type: 'INCREMENT' } | { type: 'DECREMENT' }\n      }>\n      let api2: StoreApi<{\n        count: number\n        dispatch: (\n          action: { type: 'INCREMENT' } | { type: 'DECREMENT' },\n        ) => { type: 'INCREMENT' } | { type: 'DECREMENT' }\n      }>\n\n      it('works as expected', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        api1 = createStore(\n          devtools(\n            redux(\n              (\n                { count },\n                { type }: { type: 'INCREMENT' } | { type: 'DECREMENT' },\n              ) => ({\n                count: count + (type === 'INCREMENT' ? 1 : -1),\n              }),\n              { count: 0 },\n            ),\n            { enabled: true, ...options1 },\n          ),\n        )\n        api2 = createStore(\n          devtools(\n            redux(\n              (\n                { count },\n                { type }: { type: 'INCREMENT' } | { type: 'DECREMENT' },\n              ) => ({\n                count: count + (type === 'INCREMENT' ? 1 : -1),\n              }),\n              { count: 10 },\n            ),\n            { enabled: true, ...options2 },\n          ),\n        )\n        ;(api1 as any).dispatch({ type: 'INCREMENT' })\n        ;(api1 as any).dispatch({ type: 'INCREMENT' })\n        ;(api2 as any).dispatch({ type: 'INCREMENT' })\n        ;(api2 as any).dispatch({ type: 'INCREMENT' })\n        const [connection1, connection2] = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n        )\n        const [connectionSubscriber1, connectionSubscriber2] =\n          getUnnamedConnectionSubscribers(\n            options1.testConnectionId,\n            options2.testConnectionId,\n          )\n        connectionSubscriber1({\n          type: 'ACTION',\n          payload: JSON.stringify({ type: 'DECREMENT' }),\n        })\n        connectionSubscriber2({\n          type: 'ACTION',\n          payload: JSON.stringify({ type: 'DECREMENT' }),\n        })\n\n        expect(connection1.init.mock.calls).toMatchObject([\n          [{ count: 0 }] as unknown as Record<string, unknown>,\n        ])\n        expect(connection2.init.mock.calls).toMatchObject([\n          [{ count: 10 }] as unknown as Record<string, unknown>,\n        ])\n        expect(connection1.send.mock.calls).toMatchObject([\n          [{ type: 'INCREMENT' }, { count: 1 }] as unknown as Record<\n            string,\n            unknown\n          >,\n          [{ type: 'INCREMENT' }, { count: 2 }] as unknown as Record<\n            string,\n            unknown\n          >,\n          [{ type: 'DECREMENT' }, { count: 1 }] as unknown as Record<\n            string,\n            unknown\n          >,\n        ])\n        expect(connection2.send.mock.calls).toMatchObject([\n          [{ type: 'INCREMENT' }, { count: 11 }] as unknown as Record<\n            string,\n            unknown\n          >,\n          [{ type: 'INCREMENT' }, { count: 12 }] as unknown as Record<\n            string,\n            unknown\n          >,\n          [{ type: 'DECREMENT' }, { count: 11 }] as unknown as Record<\n            string,\n            unknown\n          > as unknown as Record<string, unknown>,\n        ])\n        expect(api1.getState()).toMatchObject({ count: 1 })\n        expect(api2.getState()).toMatchObject({ count: 11 })\n      })\n    })\n  })\n})\n\ndescribe('when create devtools was called multiple times with `name` option undefined', () => {\n  describe('When state changes...', () => {\n    it(\"sends { type: setStateName || 'anonymous`, ...rest } as the action with current state, isolated from other connections\", async () => {\n      const options1 = {\n        enabled: true,\n        testConnectionId: '123',\n      }\n      const options2 = {\n        enabled: true,\n        testConnectionId: '324',\n      }\n      const options3 = {\n        enabled: true,\n        testConnectionId: '412',\n      }\n      const api1 = createStore(\n        devtools(() => ({ count: 0, foo: 'bar' }), options1),\n      )\n      createStore(devtools(() => ({ count: 0, foo: 'bar1' }), options2))\n      createStore(devtools(() => ({ count: 0, foo: 'bar2' }), options3))\n\n      api1.setState({ count: 10 }, false, 'testSetStateName')\n      const [connection1, connection2, connection3] = getUnnamedConnectionApis(\n        options1.testConnectionId,\n        options2.testConnectionId,\n        options3.testConnectionId,\n      )\n      expect(connection1.send).toHaveBeenLastCalledWith(\n        { type: 'testSetStateName' },\n        { count: 10, foo: 'bar' },\n      )\n      expect(connection2.send).not.toBeCalled()\n      expect(connection3.send).not.toBeCalled()\n\n      api1.setState({ count: 15 }, false, {\n        type: 'testSetStateName',\n        payload: 15,\n      })\n      expect(connection1.send).toHaveBeenLastCalledWith(\n        { type: 'testSetStateName', payload: 15 },\n        { count: 15, foo: 'bar' },\n      )\n      expect(connection2.send).not.toBeCalled()\n      expect(connection3.send).not.toBeCalled()\n\n      api1.setState({ count: 5, foo: 'baz' }, true)\n      expect(connection1.send).toHaveBeenLastCalledWith(\n        { type: 'anonymous' },\n        { count: 5, foo: 'baz' },\n      )\n      expect(connection2.send).not.toBeCalled()\n      expect(connection3.send).not.toBeCalled()\n    })\n  })\n\n  describe('when it receives a message of type...', () => {\n    describe('ACTION...', () => {\n      it('does nothing, connections isolated from each other', async () => {\n        const options1 = { testConnectionId: '123' }\n        const options2 = { testConnectionId: '231' }\n        const options3 = { testConnectionId: '4342' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 3 }\n        const api1 = createStore(\n          devtools(() => initialState1, {\n            enabled: true,\n            ...options1,\n          }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, {\n            enabled: true,\n            ...options2,\n          }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, {\n            enabled: true,\n            ...options3,\n          }),\n        )\n        const setState1 = vi.spyOn(api1, 'setState')\n        const setState2 = vi.spyOn(api2, 'setState')\n        const setState3 = vi.spyOn(api3, 'setState')\n\n        const [subscriber] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n        )\n        subscriber({\n          type: 'ACTION',\n          payload: '{ \"type\": \"INCREMENT\" }',\n        })\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        expect(setState1).not.toBeCalled()\n        expect(setState2).not.toBeCalled()\n        expect(setState3).not.toBeCalled()\n      })\n\n      it('unless action type is __setState, connections isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 5 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n\n        const [connectionSubscriber] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n        )\n        connectionSubscriber({\n          type: 'ACTION',\n          payload: '{ \"type\": \"__setState\", \"state\": { \"foo\": \"bar\" } }',\n        })\n\n        expect(api1.getState()).toStrictEqual({ ...initialState1, foo: 'bar' })\n        expect(api2.getState()).toStrictEqual({ ...initialState2 })\n        expect(api3.getState()).toStrictEqual({ ...initialState3 })\n      })\n\n      it('does nothing even if there is `api.dispatch`, connections isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 5 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        ;(api1 as any).dispatch = vi.fn()\n        ;(api2 as any).dispatch = vi.fn()\n        ;(api3 as any).dispatch = vi.fn()\n        const setState1 = vi.spyOn(api1, 'setState')\n        const setState2 = vi.spyOn(api2, 'setState')\n        const setState3 = vi.spyOn(api3, 'setState')\n\n        const subscribers = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        const testPayload = {\n          type: 'ACTION',\n          payload: '{ \"type\": \"INCREMENT\" }',\n        }\n        subscribers.forEach((sub) => sub(testPayload))\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        expect(setState1).not.toBeCalled()\n        expect(setState2).not.toBeCalled()\n        expect(setState3).not.toBeCalled()\n        expect((api1 as any).dispatch).not.toBeCalled()\n        expect((api2 as any).dispatch).not.toBeCalled()\n        expect((api3 as any).dispatch).not.toBeCalled()\n      })\n\n      it('dispatches with `api.dispatch` when `api.dispatchFromDevtools` is set to true, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 5 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        ;(api1 as any).dispatch = vi.fn()\n        ;(api1 as any).dispatchFromDevtools = true\n        ;(api2 as any).dispatch = vi.fn()\n        ;(api2 as any).dispatchFromDevtools = true\n        ;(api3 as any).dispatch = vi.fn()\n        ;(api3 as any).dispatchFromDevtools = true\n        const setState1 = vi.spyOn(api1, 'setState')\n        const setState2 = vi.spyOn(api2, 'setState')\n        const setState3 = vi.spyOn(api3, 'setState')\n\n        const subscribers = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        const getTestPayload = (n: number) => ({\n          type: 'ACTION',\n          payload: `{ \"type\": \"INCREMENT${n}\" }`,\n        })\n        subscribers.forEach((sub, i) => sub(getTestPayload(i + 1)))\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        expect(setState1).not.toBeCalled()\n        expect(setState2).not.toBeCalled()\n        expect(setState3).not.toBeCalled()\n        expect((api1 as any).dispatch).toHaveBeenLastCalledWith({\n          type: 'INCREMENT1',\n        })\n        expect((api2 as any).dispatch).toHaveBeenLastCalledWith({\n          type: 'INCREMENT2',\n        })\n        expect((api3 as any).dispatch).toHaveBeenLastCalledWith({\n          type: 'INCREMENT3',\n        })\n      })\n\n      it('does not throw for unsupported payload, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 5 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        ;(api1 as any).dispatch = vi.fn()\n        ;(api1 as any).dispatchFromDevtools = true\n        ;(api2 as any).dispatch = vi.fn()\n        ;(api2 as any).dispatchFromDevtools = true\n        ;(api3 as any).dispatch = vi.fn()\n        ;(api3 as any).dispatchFromDevtools = true\n        const setState1 = vi.spyOn(api1, 'setState')\n        const setState2 = vi.spyOn(api2, 'setState')\n        const setState3 = vi.spyOn(api3, 'setState')\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        expect(() => {\n          connectionSubscriber1({\n            type: 'ACTION',\n            payload: 'this.increment1()',\n          })\n        }).not.toThrow()\n        expect(console.error).toHaveBeenNthCalledWith(\n          1,\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('this.increment1()')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n\n        expect(() => {\n          connectionSubscriber1({\n            type: 'ACTION',\n            payload: 'this.increment2()',\n          })\n        }).not.toThrow()\n        expect(console.error).toHaveBeenNthCalledWith(\n          2,\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('this.increment2()')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n\n        expect(() => {\n          connectionSubscriber1({\n            type: 'ACTION',\n            payload: 'this.increment3()',\n          })\n        }).not.toThrow()\n        expect(console.error).toHaveBeenNthCalledWith(\n          3,\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('this.increment3()')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n\n        expect(() => {\n          connectionSubscriber1({\n            type: 'ACTION',\n            payload: { name: 'increment', args: [] },\n          })\n        }).not.toThrow()\n        expect(console.error).toHaveBeenNthCalledWith(\n          4,\n          '[zustand devtools middleware] Unsupported action format',\n        )\n        expect(() => {\n          connectionSubscriber2({\n            type: 'ACTION',\n            payload: { name: 'increment', args: [] },\n          })\n        }).not.toThrow()\n        expect(console.error).toHaveBeenNthCalledWith(\n          5,\n          '[zustand devtools middleware] Unsupported action format',\n        )\n        expect(() => {\n          connectionSubscriber3({\n            type: 'ACTION',\n            payload: { name: 'increment', args: [] },\n          })\n        }).not.toThrow()\n        expect(console.error).toHaveBeenNthCalledWith(\n          6,\n          '[zustand devtools middleware] Unsupported action format',\n        )\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        expect(setState1).not.toBeCalled()\n        expect(setState2).not.toBeCalled()\n        expect(setState3).not.toBeCalled()\n        expect((api1 as any).dispatch).not.toBeCalled()\n        expect((api2 as any).dispatch).not.toBeCalled()\n        expect((api3 as any).dispatch).not.toBeCalled()\n\n        console.error = originalConsoleError\n      })\n    })\n\n    describe('DISPATCH and payload of type...', () => {\n      it('RESET, it inits with initial state, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 5 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        api1.setState({ count: 1 })\n        api2.setState({ count: 3 })\n        api3.setState({ count: 10 })\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        const [connection1, connection2, connection3] = connections\n        connections.forEach((conn) => conn.send.mockClear())\n        const subscribers = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        const action = {\n          type: 'DISPATCH',\n          payload: { type: 'RESET' },\n        }\n        subscribers.forEach((sub) => sub(action))\n\n        expect(api1.getState()).toStrictEqual(initialState1)\n        expect(api1.getState()).toStrictEqual(initialState1)\n        expect(api1.getState()).toStrictEqual(initialState1)\n        expect(connection1.init).toHaveBeenLastCalledWith(initialState1)\n        expect(connection2.init).toHaveBeenLastCalledWith(initialState2)\n        expect(connection3.init).toHaveBeenLastCalledWith(initialState3)\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n      })\n\n      it('COMMIT, it inits with current state, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0 }\n        const initialState2 = { count: 2 }\n        const initialState3 = { count: 5 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        api1.setState({ count: 1 })\n        api2.setState({ count: 3 })\n        api3.setState({ count: 10 })\n        const currentState1 = api1.getState()\n        const currentState2 = api2.getState()\n        const currentState3 = api3.getState()\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const subscribers = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        const action = {\n          type: 'DISPATCH',\n          payload: { type: 'COMMIT' },\n        }\n        subscribers.forEach((sub) => sub(action))\n\n        const [connection1, connection2, connection3] = connections\n        expect(connection1.init).toHaveBeenLastCalledWith(currentState1)\n        expect(connection2.init).toHaveBeenLastCalledWith(currentState2)\n        expect(connection3.init).toHaveBeenLastCalledWith(currentState3)\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n      })\n    })\n\n    describe('ROLLBACK...', () => {\n      it('updates state without recording and inits with `message.state, connections are isolated from each other`', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: () => {} }\n        const initialState2 = { count: 2, increment: () => {} }\n        const initialState3 = { count: 5, increment: () => {} }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const newState1 = { foo: 'bar1' }\n        const newState2 = { foo: 'bar2' }\n        const newState3 = { foo: 'bar3' }\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: JSON.stringify(newState1),\n        })\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: JSON.stringify(newState2),\n        })\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: JSON.stringify(newState3),\n        })\n\n        expect(api1.getState()).toStrictEqual({\n          ...initialState1,\n          ...newState1,\n        })\n        expect(api2.getState()).toStrictEqual({\n          ...initialState2,\n          ...newState2,\n        })\n        expect(api3.getState()).toStrictEqual({\n          ...initialState3,\n          ...newState3,\n        })\n        const [connection1, connection2, connection3] = connections\n        expect(connection1.init).toHaveBeenLastCalledWith({\n          ...initialState1,\n          ...newState1,\n        })\n        expect(connection2.init).toHaveBeenLastCalledWith({\n          ...initialState2,\n          ...newState2,\n        })\n        expect(connection3.init).toHaveBeenLastCalledWith({\n          ...initialState3,\n          ...newState3,\n        })\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n      })\n\n      it('does not throw for unparsable `message.state`, connections are isolated from each other', async () => {\n        const increment1 = () => {}\n        const increment2 = () => {}\n        const increment3 = () => {}\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: increment1 }\n        const initialState2 = { count: 2, increment: increment2 }\n        const initialState3 = { count: 5, increment: increment3 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.init.mockClear())\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: 'foobar',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: 'foobar1',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar1')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'ROLLBACK' },\n          state: 'foobar3',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar3')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        connections.forEach((conn) => {\n          expect(conn.init).not.toBeCalled()\n          expect(conn.send).not.toBeCalled()\n        })\n\n        console.error = originalConsoleError\n      })\n    })\n\n    describe('JUMP_TO_STATE...', () => {\n      const increment1 = () => {}\n      const increment2 = () => {}\n      const increment3 = () => {}\n\n      it('updates state without recording with `message.state`, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: increment1 }\n        const initialState2 = { count: 2, increment: increment2 }\n        const initialState3 = { count: 5, increment: increment3 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const newState1 = { foo: 'bar1' }\n        const newState2 = { foo: 'bar2' }\n        const newState3 = { foo: 'bar3' }\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: JSON.stringify(newState1),\n        })\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: JSON.stringify(newState2),\n        })\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: JSON.stringify(newState3),\n        })\n\n        expect(api1.getState()).toStrictEqual({\n          ...initialState1,\n          ...newState1,\n        })\n        expect(api2.getState()).toStrictEqual({\n          ...initialState2,\n          ...newState2,\n        })\n        expect(api3.getState()).toStrictEqual({\n          ...initialState3,\n          ...newState3,\n        })\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n      })\n\n      it('does not throw for unparsable `message.state`, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: increment1 }\n        const initialState2 = { count: 2, increment: increment2 }\n        const initialState3 = { count: 5, increment: increment3 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: 'foobar',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: 'foobar2',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar2')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_STATE' },\n          state: 'foobar3',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar3')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n\n        console.error = originalConsoleError\n      })\n    })\n\n    describe('JUMP_TO_ACTION...', () => {\n      const increment1 = () => {}\n      const increment2 = () => {}\n      const increment3 = () => {}\n\n      it('updates state without recording with `message.state`, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: increment1 }\n        const initialState2 = { count: 2, increment: increment2 }\n        const initialState3 = { count: 5, increment: increment3 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const newState1 = { foo: 'bar1' }\n        const newState2 = { foo: 'bar2' }\n        const newState3 = { foo: 'bar3' }\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: JSON.stringify(newState1),\n        })\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: JSON.stringify(newState2),\n        })\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: JSON.stringify(newState3),\n        })\n\n        expect(api1.getState()).toStrictEqual({\n          ...initialState1,\n          ...newState1,\n        })\n        expect(api2.getState()).toStrictEqual({\n          ...initialState2,\n          ...newState2,\n        })\n        expect(api3.getState()).toStrictEqual({\n          ...initialState3,\n          ...newState3,\n        })\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n      })\n\n      it('does not throw for unparsable `message.state`, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: increment1 }\n        const initialState2 = { count: 2, increment: increment2 }\n        const initialState3 = { count: 5, increment: increment3 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const originalConsoleError = console.error\n        console.error = vi.fn()\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: 'foobar',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: 'foobar2',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar2')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'JUMP_TO_ACTION' },\n          state: 'foobar3',\n        })\n        expect(console.error).toHaveBeenLastCalledWith(\n          '[zustand devtools middleware] Could not parse the received json',\n          (() => {\n            try {\n              JSON.parse('foobar3')\n            } catch (e) {\n              return e\n            }\n          })(),\n        )\n\n        expect(api1.getState()).toBe(initialState1)\n        expect(api2.getState()).toBe(initialState2)\n        expect(api3.getState()).toBe(initialState3)\n        connections.forEach((conn) => expect(conn.send).not.toBeCalled())\n\n        console.error = originalConsoleError\n      })\n\n      it('IMPORT_STATE, it updates state without recording and inits the last computedState, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const initialState1 = { count: 0, increment: increment1 }\n        const initialState2 = { count: 2, increment: increment2 }\n        const initialState3 = { count: 5, increment: increment3 }\n        const api1 = createStore(\n          devtools(() => initialState1, { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => initialState2, { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => initialState3, { enabled: true, ...options3 }),\n        )\n        const nextLiftedState1 = {\n          computedStates: [{ state: { count: 4 } }, { state: { count: 5 } }],\n        }\n        const nextLiftedState2 = {\n          computedStates: [{ state: { count: 20 } }, { state: { count: 8 } }],\n        }\n        const nextLiftedState3 = {\n          computedStates: [{ state: { count: 12 } }, { state: { count: 100 } }],\n        }\n\n        const connections = getUnnamedConnectionApis(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n        connections.forEach((conn) => conn.send.mockClear())\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: {\n            type: 'IMPORT_STATE',\n            nextLiftedState: nextLiftedState1,\n          },\n        })\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: {\n            type: 'IMPORT_STATE',\n            nextLiftedState: nextLiftedState2,\n          },\n        })\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: {\n            type: 'IMPORT_STATE',\n            nextLiftedState: nextLiftedState3,\n          },\n        })\n\n        expect(api1.getState()).toStrictEqual({\n          ...initialState1,\n          ...nextLiftedState1.computedStates.slice(-1)[0]?.state,\n        })\n        expect(api2.getState()).toStrictEqual({\n          ...initialState2,\n          ...nextLiftedState2.computedStates.slice(-1)[0]?.state,\n        })\n        expect(api3.getState()).toStrictEqual({\n          ...initialState3,\n          ...nextLiftedState3.computedStates.slice(-1)[0]?.state,\n        })\n        const [connection1, connection2, connection3] = connections\n        expect(connection1.send).toHaveBeenLastCalledWith(\n          null,\n          nextLiftedState1,\n        )\n        expect(connection2.send).toHaveBeenLastCalledWith(\n          null,\n          nextLiftedState2,\n        )\n        expect(connection3.send).toHaveBeenLastCalledWith(\n          null,\n          nextLiftedState3,\n        )\n      })\n\n      it('PAUSE_RECORDING, it toggles the sending of actions, connections are isolated from each other', async () => {\n        const options1 = { testConnectionId: 'asdf' }\n        const options2 = { testConnectionId: '2f' }\n        const options3 = { testConnectionId: 'd2e' }\n        const api1 = createStore(\n          devtools(() => ({ count: 0 }), { enabled: true, ...options1 }),\n        )\n        const api2 = createStore(\n          devtools(() => ({ count: 2 }), { enabled: true, ...options2 }),\n        )\n        const api3 = createStore(\n          devtools(() => ({ count: 4 }), { enabled: true, ...options3 }),\n        )\n\n        const newState1 = { count: 1 }\n        const newState2 = { count: 12 }\n        const newState3 = { count: 30 }\n        api1.setState(newState1, false, 'increment')\n        api2.setState(newState2, false, 'increment')\n        api3.setState(newState3, false, 'increment')\n\n        const [connection1, connection2, connection3] =\n          getUnnamedConnectionApis(\n            options1.testConnectionId,\n            options2.testConnectionId,\n            options3.testConnectionId,\n          )\n        const [\n          connectionSubscriber1,\n          connectionSubscriber2,\n          connectionSubscriber3,\n        ] = getUnnamedConnectionSubscribers(\n          options1.testConnectionId,\n          options2.testConnectionId,\n          options3.testConnectionId,\n        )\n\n        expect(connection1.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          newState1,\n        )\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'PAUSE_RECORDING' },\n        })\n        api1.setState({ count: 2 }, false, 'increment')\n        expect(connection1.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          newState1,\n        )\n        connectionSubscriber1({\n          type: 'DISPATCH',\n          payload: { type: 'PAUSE_RECORDING' },\n        })\n        api1.setState({ count: 3 }, false, 'increment')\n        expect(connection1.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          { count: 3 },\n        )\n\n        expect(connection2.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          newState2,\n        )\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'PAUSE_RECORDING' },\n        })\n        api2.setState({ count: 2 }, false, 'increment')\n        expect(connection2.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          newState2,\n        )\n        connectionSubscriber2({\n          type: 'DISPATCH',\n          payload: { type: 'PAUSE_RECORDING' },\n        })\n        api2.setState({ count: 3 }, false, 'increment')\n        expect(connection2.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          { count: 3 },\n        )\n\n        expect(connection3.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          newState3,\n        )\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'PAUSE_RECORDING' },\n        })\n        api3.setState({ count: 2 }, false, 'increment')\n        expect(connection3.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          newState3,\n        )\n        connectionSubscriber3({\n          type: 'DISPATCH',\n          payload: { type: 'PAUSE_RECORDING' },\n        })\n        api3.setState({ count: 3 }, false, 'increment')\n        expect(connection3.send).toHaveBeenLastCalledWith(\n          { type: 'increment' },\n          { count: 3 },\n        )\n      })\n    })\n  })\n})\n\ndescribe('when create devtools was called multiple times with `name` and `store` options defined', () => {\n  describe('when `type` was provided in store state methods as option', () => {\n    describe('When state changes...', () => {\n      it(\"sends { type: setStateName || 'anonymous`, ...rest } as the action with current state\", async () => {\n        const options = {\n          name: 'testOptionsName',\n          store: 'someStore',\n          enabled: true,\n        }\n        const api = createStore(\n          devtools(() => ({ count: 0, foo: 'bar' }), options),\n        )\n\n        const testStateActionType = 'testSetStateName'\n\n        api.setState({ count: 10 }, false, testStateActionType)\n        const [connection] = getNamedConnectionApis(options.name)\n        expect(connection.send).toHaveBeenLastCalledWith(\n          { type: `${options.store}/${testStateActionType}` },\n          { [options.store]: { count: 10, foo: 'bar' } },\n        )\n\n        api.setState({ count: 15 }, false, {\n          type: testStateActionType,\n          payload: 15,\n        })\n        expect(connection.send).toHaveBeenLastCalledWith(\n          { type: `${options.store}/${testStateActionType}`, payload: 15 },\n          { [options.store]: { count: 15, foo: 'bar' } },\n        )\n\n        api.setState({ count: 5, foo: 'baz' }, true)\n        expect(connection.send).toHaveBeenLastCalledWith(\n          { type: `${options.store}/anonymous` },\n          { [options.store]: { count: 5, foo: 'baz' } },\n        )\n      })\n    })\n\n    describe('when it receives a message of type...', () => {\n      describe('ACTION...', () => {\n        it('does nothing, connections isolated from each other', async () => {\n          const options1 = { testConnectionId: '123', store: 'store1' }\n          const options2 = { testConnectionId: '231', store: 'store2' }\n          const initialState1 = { count: 0 }\n          const initialState2 = { count: 2 }\n          const initialState3 = { count: 5 }\n          const initialState4 = { count: 6 }\n          const api1 = createStore(\n            devtools(() => initialState1, {\n              enabled: true,\n              ...options1,\n            }),\n          )\n          const api2 = createStore(\n            devtools(() => initialState2, {\n              enabled: true,\n              ...options1,\n            }),\n          )\n          const api3 = createStore(\n            devtools(() => initialState3, {\n              enabled: true,\n              ...options2,\n            }),\n          )\n          const api4 = createStore(\n            devtools(() => initialState4, {\n              enabled: true,\n              ...options2,\n            }),\n          )\n          const setState1 = vi.spyOn(api1, 'setState')\n          const setState2 = vi.spyOn(api2, 'setState')\n          const setState3 = vi.spyOn(api3, 'setState')\n          const setState4 = vi.spyOn(api4, 'setState')\n\n          const [subscriber] = getUnnamedConnectionSubscribers(\n            options1.testConnectionId,\n          )\n          subscriber({\n            type: 'ACTION',\n            payload: '{ \"type\": \"INCREMENT\" }',\n          })\n\n          expect(api1.getState()).toBe(initialState1)\n          expect(api2.getState()).toBe(initialState2)\n          expect(api3.getState()).toBe(initialState3)\n          expect(api4.getState()).toBe(initialState4)\n          expect(setState1).not.toBeCalled()\n          expect(setState2).not.toBeCalled()\n          expect(setState3).not.toBeCalled()\n          expect(setState4).not.toBeCalled()\n        })\n\n        it('unless action type is __setState, connections isolated from each other', async () => {\n          const name1 = 'name1'\n          const name2 = 'name2'\n          const store1 = 'someStore1'\n          const store2 = 'someStore2'\n          const options1 = {\n            name: name1,\n            store: store1,\n            testStore: store1,\n          }\n          const options2 = {\n            name: name2,\n            store: store2,\n            testStore: store2,\n          }\n          const initialState1 = { count: 0 }\n          const initialState2 = { count: 2 }\n          const api1 = createStore(\n            devtools(() => initialState1, { enabled: true, ...options1 }),\n          )\n          const api2 = createStore(\n            devtools(() => initialState2, { enabled: true, ...options2 }),\n          )\n          const originalConsoleError = console.error\n          console.error = vi.fn()\n\n          const [connectionSubscriber] = getNamedConnectionSubscribers(\n            getKeyFromOptions(options1),\n          )\n          connectionSubscriber({\n            type: 'ACTION',\n            payload:\n              '{ \"type\": \"__setState\", \"state\": { \"foo\": \"bar\", \"foo2\": \"bar2\" } }',\n          })\n\n          expect(console.error).toHaveBeenCalledWith(\n            expect.stringContaining(\n              '[zustand devtools middleware] Unsupported __setState',\n            ),\n          )\n          connectionSubscriber({\n            type: 'ACTION',\n            payload: `{ \"type\": \"__setState\", \"state\": { \"${options1.store}\": { \"foo\": \"bar\" } } }`,\n          })\n\n          expect(console.error).toHaveBeenCalledTimes(1)\n\n          expect(api1.getState()).toStrictEqual({\n            ...initialState1,\n            foo: 'bar',\n          })\n          expect(api2.getState()).toStrictEqual({ ...initialState2 })\n\n          console.error = originalConsoleError\n        })\n\n        it('does nothing even if there is `api.dispatch`, connections isolated from each other', async () => {\n          const { devtools: newDevtools } = await import('zustand/middleware')\n\n          const name1 = 'name1'\n          const name2 = 'name2'\n          const store1 = 'someStore1'\n          const store2 = 'someStore2'\n          const options1 = {\n            name: name1,\n            store: store1,\n            testStore: store1,\n          }\n          const options2 = {\n            name: name2,\n            store: store2,\n            testStore: store2,\n          }\n          const initialState1 = { count: 0 }\n          const initialState2 = { count: 2 }\n          const api1 = createStore(\n            newDevtools(() => initialState1, { enabled: true, ...options1 }),\n          )\n          const api2 = createStore(\n            newDevtools(() => initialState2, { enabled: true, ...options2 }),\n          )\n          ;(api1 as any).dispatch = vi.fn()\n          ;(api2 as any).dispatch = vi.fn()\n          const setState1 = vi.spyOn(api1, 'setState')\n          const setState2 = vi.spyOn(api2, 'setState')\n\n          const subscribers = getNamedConnectionSubscribers(\n            getKeyFromOptions(options1),\n            getKeyFromOptions(options2),\n          )\n          const testPayload = {\n            type: 'ACTION',\n            payload: '{ \"type\": \"INCREMENT\" }',\n          }\n          subscribers.forEach((sub) => sub(testPayload))\n\n          expect(api1.getState()).toBe(initialState1)\n          expect(api2.getState()).toBe(initialState2)\n          expect(setState1).not.toBeCalled()\n          expect(setState2).not.toBeCalled()\n          expect((api1 as any).dispatch).not.toBeCalled()\n          expect((api2 as any).dispatch).not.toBeCalled()\n        })\n\n        it('dispatches with `api.dispatch` when `api.dispatchFromDevtools` is set to true, connections are isolated from each other', async () => {\n          const { devtools: newDevtools } = await import('zustand/middleware')\n          const name1 = 'name1'\n          const name2 = 'name2'\n          const store1 = 'someStore1'\n          const store2 = 'someStore2'\n          const options1 = {\n            name: name1,\n            store: store1,\n            testStore: store1,\n          }\n          const options2 = {\n            name: name2,\n            store: store2,\n            testStore: store2,\n          }\n          const initialState1 = { count: 0 }\n          const initialState2 = { count: 2 }\n          const api1 = createStore(\n            newDevtools(() => initialState1, { enabled: true, ...options1 }),\n          )\n          const api2 = createStore(\n            newDevtools(() => initialState2, { enabled: true, ...options2 }),\n          )\n          ;(api1 as any).dispatch = vi.fn()\n          ;(api1 as any).dispatchFromDevtools = true\n          ;(api2 as any).dispatch = vi.fn()\n          ;(api2 as any).dispatchFromDevtools = true\n          const setState1 = vi.spyOn(api1, 'setState')\n          const setState2 = vi.spyOn(api2, 'setState')\n\n          const subscribers = getNamedConnectionSubscribers(\n            getKeyFromOptions(options1),\n            getKeyFromOptions(options2),\n          )\n          const getTestPayload = (n: number) => ({\n            type: 'ACTION',\n            payload: `{ \"type\": \"INCREMENT${n}\" }`,\n          })\n          subscribers.forEach((sub, i) => sub(getTestPayload(i + 1)))\n\n          expect(api1.getState()).toBe(initialState1)\n          expect(api2.getState()).toBe(initialState2)\n          expect(setState1).not.toBeCalled()\n          expect(setState2).not.toBeCalled()\n          expect((api1 as any).dispatch).toHaveBeenLastCalledWith({\n            type: 'INCREMENT1',\n          })\n          expect((api2 as any).dispatch).toHaveBeenLastCalledWith({\n            type: 'INCREMENT2',\n          })\n        })\n      })\n    })\n  })\n})\n\ndescribe('cleanup', () => {\n  it('should unsubscribe from devtools when cleanup is called', async () => {\n    const options = { name: 'test' }\n    const store = createStore(devtools(() => ({ count: 0 }), options))\n    const [connection] = getNamedConnectionApis(options.name)\n    store.devtools.cleanup()\n\n    expect(connection.unsubscribe).toHaveBeenCalledTimes(1)\n  })\n\n  it('should remove store from tracked connection after cleanup', async () => {\n    const options = {\n      name: 'test-store-name',\n      store: 'test-store-id',\n      enabled: true,\n    }\n\n    const store1 = createStore(devtools(() => ({ count: 0 }), options))\n    store1.devtools.cleanup()\n    const store2 = createStore(devtools(() => ({ count: 0 }), options))\n\n    const [connection] = getNamedConnectionApis(options.name)\n\n    store2.setState({ count: 15 }, false, 'updateCount')\n    expect(connection.send).toHaveBeenLastCalledWith(\n      { type: `${options.store}/updateCount` },\n      { [options.store]: { count: 15 } },\n    )\n\n    store1.setState({ count: 20 }, false, 'ignoredAction')\n    expect(connection.send).not.toHaveBeenLastCalledWith(\n      { type: `${options.store}/ignoredAction` },\n      expect.anything(),\n    )\n  })\n})\n\ndescribe('actionsDenylist', () => {\n  it('should pass actionsDenylist option to Redux DevTools', async () => {\n    const options = {\n      name: 'test-filter',\n      enabled: true,\n      actionsDenylist: ['secret.*'],\n    }\n\n    createStore(devtools(() => ({ count: 0 }), options))\n\n    // Verify that actionsDenylist was passed to the connect call\n    const extensionConnector = (window as any).__REDUX_DEVTOOLS_EXTENSION__\n    expect(extensionConnector.connect).toHaveBeenCalledWith(\n      expect.objectContaining({\n        actionsDenylist: ['secret.*'],\n      }),\n    )\n  })\n})\n"
  },
  {
    "path": "tests/middlewareTypes.test.tsx",
    "content": "import { describe, expect, expectTypeOf, it } from 'vitest'\nimport { create } from 'zustand'\nimport type { StateCreator, StoreApi, StoreMutatorIdentifier } from 'zustand'\nimport {\n  combine,\n  devtools,\n  persist,\n  redux,\n  subscribeWithSelector,\n} from 'zustand/middleware'\nimport { immer } from 'zustand/middleware/immer'\nimport { createStore } from 'zustand/vanilla'\n\ntype CounterState = {\n  count: number\n  inc: () => void\n}\n\ntype ExampleStateCreator<T, A> = <\n  Mps extends [StoreMutatorIdentifier, unknown][] = [],\n  Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n  U = T,\n>(\n  f: StateCreator<T, [...Mps, ['org/example', A]], Mcs>,\n) => StateCreator<T, Mps, [['org/example', A], ...Mcs], U & A>\n\ntype Write<T, U> = Omit<T, keyof U> & U\ntype StoreModifyAllButSetState<S, A> = S extends {\n  getState: () => infer T\n}\n  ? Omit<StoreApi<T & A>, 'setState'>\n  : never\n\ndeclare module 'zustand/vanilla' {\n  interface StoreMutators<S, A> {\n    'org/example': Write<S, StoreModifyAllButSetState<S, A>>\n  }\n}\n\ndescribe('counter state spec (no middleware)', () => {\n  it('no middleware', () => {\n    const useBoundStore = create<CounterState>((set, get) => ({\n      count: 0,\n      inc: () => set({ count: get().count + 1 }, false),\n    }))\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('counter state spec (single middleware)', () => {\n  it('immer', () => {\n    const useBoundStore = create<CounterState>()(\n      immer((set, get) => ({\n        count: 0,\n        inc: () =>\n          set((state) => {\n            state.count = get().count + 1\n          }),\n      })),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n\n    const testSubtyping: StoreApi<object> = createStore(\n      immer(() => ({ count: 0 })),\n    )\n    expect(testSubtyping).toBeDefined()\n\n    const exampleMiddleware = ((initializer) =>\n      initializer) as ExampleStateCreator<CounterState, { additional: number }>\n\n    const testDerivedSetStateType = create<CounterState>()(\n      exampleMiddleware(\n        immer((set, get) => ({\n          count: 0,\n          inc: () =>\n            set((state) => {\n              state.count = get().count + 1\n              type OmitFn<T> = Exclude<T, (...args: any[]) => any>\n              expectTypeOf<OmitFn<Parameters<typeof set>[0]>>().not.toExtend<{\n                additional: number\n              }>()\n              expectTypeOf<ReturnType<typeof get>>().toExtend<{\n                additional: number\n              }>()\n            }),\n        })),\n      ),\n    )\n    expect(testDerivedSetStateType).toBeDefined()\n    // the type of the `getState` should include our new property\n    expectTypeOf(testDerivedSetStateType.getState()).toExtend<{\n      additional: number\n    }>()\n    // the type of the `setState` should not include our new property\n    expectTypeOf<\n      Parameters<typeof testDerivedSetStateType.setState>[0]\n    >().not.toExtend<{ additional: number }>()\n  })\n\n  it('redux', () => {\n    const useBoundStore = create(\n      redux<{ count: number }, { type: 'INC' }>(\n        (state, action) => {\n          switch (action.type) {\n            case 'INC':\n              return { ...state, count: state.count + 1 }\n            default:\n              return state\n          }\n        },\n        { count: 0 },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(\n        useBoundStore((s) => s.dispatch)({ type: 'INC' }),\n      ).toEqualTypeOf<{ type: 'INC' }>()\n      expectTypeOf(useBoundStore().dispatch({ type: 'INC' })).toEqualTypeOf<{\n        type: 'INC'\n      }>()\n      expectTypeOf(useBoundStore.dispatch({ type: 'INC' })).toEqualTypeOf<{\n        type: 'INC'\n      }>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n\n    const testSubtyping: StoreApi<object> = createStore(\n      redux((x) => x, { count: 0 }),\n    )\n    expect(testSubtyping).toBeDefined()\n  })\n\n  it('devtools', () => {\n    const useBoundStore = create<CounterState>()(\n      devtools(\n        (set, get) => ({\n          count: 0,\n          inc: () => set({ count: get().count + 1 }, false, 'inc'),\n        }),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n\n    const testSubtyping: StoreApi<object> = createStore(\n      devtools(() => ({ count: 0 })),\n    )\n    expect(testSubtyping).toBeDefined()\n  })\n\n  it('devtools #2700', () => {\n    type TableStore = {\n      table: string\n    }\n    const useStoreA = create<TableStore | null>()(\n      devtools((_set) => null, { name: 'table-storage' }),\n    )\n    expect(useStoreA).toBeDefined()\n    const useStoreB = create<TableStore | null>()(\n      devtools(() => null, { name: 'table-storage' }),\n    )\n    expect(useStoreB).toBeDefined()\n    const useStoreC = create<TableStore | null>()((_set) => null)\n    expect(useStoreC).toBeDefined()\n    const useStoreD = create<TableStore | null>()(() => null)\n    expect(useStoreD).toBeDefined()\n  })\n\n  it('subscribeWithSelector', () => {\n    const useBoundStore = create<CounterState>()(\n      subscribeWithSelector((set, get) => ({\n        count: 1,\n        inc: () => set({ count: get().count + 1 }, false),\n      })),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      useBoundStore.subscribe(\n        (state) => state.count,\n        (count) => console.log(count * 2),\n      )\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n\n    const testSubtyping: StoreApi<object> = createStore(\n      subscribeWithSelector(() => ({ count: 0 })),\n    )\n    expect(testSubtyping).toBeDefined()\n  })\n\n  it('combine', () => {\n    const useBoundStore = create(\n      combine({ count: 1 }, (set, get) => ({\n        inc: () => set({ count: get().count + 1 }, false),\n      })),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n\n    const testSubtyping: StoreApi<object> = createStore(\n      combine({ count: 0 }, () => ({})),\n    )\n    expect(testSubtyping).toBeDefined()\n  })\n\n  it('persist', () => {\n    const useBoundStore = create<CounterState>()(\n      persist(\n        (set, get) => ({\n          count: 1,\n          inc: () => set({ count: get().count + 1 }, false),\n        }),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      useBoundStore.persist.hasHydrated()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n\n    const testSubtyping: StoreApi<object> = createStore(\n      persist(() => ({ count: 0 }), { name: 'prefix' }),\n    )\n    expect(testSubtyping).toBeDefined()\n  })\n\n  it('persist with partialize', () => {\n    const useBoundStore = create<CounterState>()(\n      persist(\n        (set, get) => ({\n          count: 1,\n          inc: () => set({ count: get().count + 1 }, false),\n        }),\n        { name: 'prefix', partialize: (s) => s.count },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      useBoundStore.persist.hasHydrated()\n      useBoundStore.persist.setOptions({\n        // @ts-expect-error to test if the partialized state is inferred as number\n        partialize: () => 'not-a-number',\n      })\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('persist without custom api (#638)', () => {\n    const useBoundStore = create<CounterState>()(\n      persist(\n        (set, get) => ({\n          count: 1,\n          inc: () => set({ count: get().count + 1 }, false),\n        }),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('counter state spec (double middleware)', () => {\n  it('immer & devtools', () => {\n    const useBoundStore = create<CounterState>()(\n      immer(\n        devtools(\n          (set, get) => ({\n            count: 0,\n            inc: () =>\n              set(\n                (state) => {\n                  state.count = get().count + 1\n                },\n                false,\n                { type: 'inc', by: 1 },\n              ),\n          }),\n          { name: 'prefix' },\n        ),\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('devtools & redux', () => {\n    const useBoundStore = create(\n      devtools(\n        redux(\n          (state, action: { type: 'INC' }) => {\n            switch (action.type) {\n              case 'INC':\n                return { ...state, count: state.count + 1 }\n              default:\n                return state\n            }\n          },\n          { count: 0 },\n        ),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(\n        useBoundStore((s) => s.dispatch)({ type: 'INC' }),\n      ).toEqualTypeOf<{ type: 'INC' }>()\n      expectTypeOf(useBoundStore().dispatch({ type: 'INC' })).toEqualTypeOf<{\n        type: 'INC'\n      }>()\n      expectTypeOf(useBoundStore.dispatch({ type: 'INC' })).toEqualTypeOf<{\n        type: 'INC'\n      }>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('devtools & combine', () => {\n    const useBoundStore = create(\n      devtools(\n        combine({ count: 1 }, (set, get) => ({\n          inc: () => set({ count: get().count + 1 }, false, 'inc'),\n        })),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('subscribeWithSelector & combine', () => {\n    const useBoundStore = create(\n      subscribeWithSelector(\n        combine({ count: 1 }, (set, get) => ({\n          inc: () => set({ count: get().count + 1 }, false),\n        })),\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      useBoundStore.subscribe(\n        (state) => state.count,\n        (count) => console.log(count * 2),\n      )\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('devtools & subscribeWithSelector', () => {\n    const useBoundStore = create<CounterState>()(\n      devtools(\n        subscribeWithSelector((set, get) => ({\n          count: 1,\n          inc: () => set({ count: get().count + 1 }, false, 'inc'),\n        })),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.subscribe(\n          (state) => state.count,\n          (count) => console.log(count * 2),\n        ),\n      ).toEqualTypeOf<() => void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('devtools & persist', () => {\n    const useBoundStore = create<CounterState>()(\n      devtools(\n        persist(\n          (set, get) => ({\n            count: 1,\n            inc: () => set({ count: get().count + 1 }, false, 'inc'),\n          }),\n          { name: 'count' },\n        ),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<unknown>()\n      expectTypeOf(useBoundStore.persist.hasHydrated()).toEqualTypeOf<boolean>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('counter state spec (triple middleware)', () => {\n  it('devtools & persist & immer', () => {\n    const useBoundStore = create<CounterState>()(\n      devtools(\n        persist(\n          immer((set, get) => ({\n            count: 0,\n            inc: () =>\n              set((state) => {\n                state.count = get().count + 1\n              }),\n          })),\n          { name: 'count' },\n        ),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<unknown>()\n      expectTypeOf(useBoundStore.persist.hasHydrated()).toEqualTypeOf<boolean>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('devtools & subscribeWithSelector & combine', () => {\n    const useBoundStore = create(\n      devtools(\n        subscribeWithSelector(\n          combine({ count: 1 }, (set, get) => ({\n            inc: () => set({ count: get().count + 1 }, false, 'inc'),\n          })),\n        ),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.subscribe(\n          (state) => state.count,\n          (count) => console.log(count * 2),\n        ),\n      ).toEqualTypeOf<() => void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('devtools & subscribeWithSelector & persist', () => {\n    const useBoundStore = create<CounterState>()(\n      devtools(\n        subscribeWithSelector(\n          persist(\n            (set, get) => ({\n              count: 0,\n              inc: () => set({ count: get().count + 1 }, false),\n            }),\n            { name: 'count' },\n          ),\n        ),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.subscribe(\n          (state) => state.count,\n          (count) => console.log(count * 2),\n        ),\n      ).toEqualTypeOf<() => void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<unknown>()\n      expectTypeOf(useBoundStore.persist.hasHydrated()).toEqualTypeOf<boolean>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('counter state spec (quadruple middleware)', () => {\n  it('devtools & subscribeWithSelector & persist & immer (#616)', () => {\n    const useBoundStore = create<CounterState>()(\n      devtools(\n        subscribeWithSelector(\n          persist(\n            immer((set, get) => ({\n              count: 0,\n              inc: () =>\n                set((state) => {\n                  state.count = get().count + 1\n                }),\n            })),\n            { name: 'count' },\n          ),\n        ),\n        { name: 'prefix' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.subscribe(\n          (state) => state.count,\n          (count) => console.log(count * 2),\n        ),\n      ).toEqualTypeOf<() => void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false, 'reset'),\n      ).toEqualTypeOf<unknown>()\n      expectTypeOf(useBoundStore.persist.hasHydrated()).toEqualTypeOf<boolean>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('more complex state spec with subscribeWithSelector', () => {\n  it('#619, #632', () => {\n    const useBoundStore = create(\n      subscribeWithSelector(\n        persist(\n          () => ({\n            foo: true,\n          }),\n          { name: 'name' },\n        ),\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.foo)).toEqualTypeOf<boolean>()\n      expectTypeOf(useBoundStore().foo).toEqualTypeOf<boolean>()\n      expectTypeOf(useBoundStore.getState().foo).toEqualTypeOf<boolean>()\n      useBoundStore.subscribe(\n        (state) => state.foo,\n        (foo) => console.log(foo),\n      )\n      useBoundStore.persist.hasHydrated()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('#631', () => {\n    type MyState = {\n      foo: number | null\n    }\n    const useBoundStore = create<MyState>()(\n      subscribeWithSelector(\n        () =>\n          ({\n            foo: 1,\n          }) as MyState, // NOTE: Asserting the entire state works too.\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.foo)).toEqualTypeOf<number | null>()\n      expectTypeOf(useBoundStore().foo).toEqualTypeOf<number | null>()\n      expectTypeOf(useBoundStore.getState().foo).toEqualTypeOf<number | null>()\n      useBoundStore.subscribe(\n        (state) => state.foo,\n        (foo) => console.log(foo),\n      )\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n\n  it('#650', () => {\n    type MyState = {\n      token: string | undefined\n      authenticated: boolean\n      authenticate: (username: string, password: string) => Promise<void>\n    }\n    const useBoundStore = create<MyState>()(\n      persist(\n        (set) => ({\n          token: undefined,\n          authenticated: false,\n          authenticate: async (_username, _password) => {\n            set({ authenticated: true })\n          },\n        }),\n        { name: 'auth-store' },\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(\n        useBoundStore((s) => s.authenticated),\n      ).toEqualTypeOf<boolean>()\n      expectTypeOf(\n        useBoundStore((s) => s.authenticate)('u', 'p'),\n      ).resolves.toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().authenticated).toEqualTypeOf<boolean>()\n      expectTypeOf(\n        useBoundStore().authenticate('u', 'p'),\n      ).resolves.toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.getState().authenticated,\n      ).toEqualTypeOf<boolean>()\n      expectTypeOf(\n        useBoundStore.getState().authenticate('u', 'p'),\n      ).resolves.toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('create with explicitly annotated mutators', () => {\n  it('subscribeWithSelector & persist', () => {\n    const useBoundStore = create<\n      CounterState,\n      [\n        ['zustand/subscribeWithSelector', never],\n        ['zustand/persist', CounterState],\n      ]\n    >(\n      subscribeWithSelector(\n        persist(\n          (set, get) => ({\n            count: 0,\n            inc: () => set({ count: get().count + 1 }, false),\n          }),\n          { name: 'count' },\n        ),\n      ),\n    )\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.count) * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.inc)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore().inc()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore.getState().count * 2).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore.getState().inc()).toEqualTypeOf<void>()\n      expectTypeOf(\n        useBoundStore.subscribe(\n          (state) => state.count,\n          (count) => console.log(count * 2),\n        ),\n      ).toEqualTypeOf<() => void>()\n      expectTypeOf(\n        useBoundStore.setState({ count: 0 }, false),\n      ).toEqualTypeOf<unknown>()\n      expectTypeOf(useBoundStore.persist.hasHydrated()).toEqualTypeOf<boolean>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n\ndescribe('single middleware with sliced store', () => {\n  it('immer with slices where slice type differs from store (#3371)', () => {\n    interface BearSlice {\n      bears: number\n      addBear: () => void\n      eatFish: () => void\n    }\n\n    interface FishSlice {\n      fishes: number\n      addFish: () => void\n    }\n\n    const createBearSlice: StateCreator<\n      BearSlice & FishSlice,\n      [['zustand/immer', never]],\n      [],\n      BearSlice\n    > = (set) => ({\n      bears: 0,\n      addBear: () => set((state) => ({ bears: state.bears + 1 })),\n      eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),\n    })\n\n    const createFishSlice: StateCreator<\n      BearSlice & FishSlice,\n      [['zustand/immer', never]],\n      [],\n      FishSlice\n    > = (set) => ({\n      fishes: 0,\n      addFish: () => set((state) => ({ fishes: state.fishes + 1 })),\n    })\n\n    const useBoundStore = create<BearSlice & FishSlice>()((...a) => ({\n      ...immer(createBearSlice)(...a),\n      ...immer(createFishSlice)(...a),\n    }))\n\n    const TestComponent = () => {\n      expectTypeOf(useBoundStore((s) => s.bears)).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.fishes)).toEqualTypeOf<number>()\n      expectTypeOf(useBoundStore((s) => s.addBear)()).toEqualTypeOf<void>()\n      expectTypeOf(useBoundStore((s) => s.eatFish)()).toEqualTypeOf<void>()\n      return <></>\n    }\n    expect(TestComponent).toBeDefined()\n  })\n})\n"
  },
  {
    "path": "tests/persistAsync.test.tsx",
    "content": "/// <reference types=\"node\" />\n\nimport { StrictMode, useEffect } from 'react'\nimport { act, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { create } from 'zustand'\nimport { createJSONStorage, persist } from 'zustand/middleware'\nimport { replacer, reviver, sleep } from './test-utils'\n\nconst createPersistantStore = (initialValue: string | null) => {\n  let state = initialValue\n\n  const getItem = async (): Promise<string | null> => {\n    getItemSpy()\n    await sleep(10)\n    return state\n  }\n  const setItem = async (name: string, newState: string) => {\n    setItemSpy(name, newState)\n    await sleep(10)\n    state = newState\n  }\n\n  const removeItem = async (name: string) => {\n    removeItemSpy(name)\n    await sleep(10)\n    state = null\n  }\n\n  const getItemSpy = vi.fn()\n  const setItemSpy = vi.fn()\n  const removeItemSpy = vi.fn()\n\n  return {\n    storage: { getItem, setItem, removeItem },\n    getItemSpy,\n    setItemSpy,\n    removeItemSpy,\n  }\n}\n\ndescribe('persist middleware with async configuration', () => {\n  const consoleError = console.error\n\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n    console.error = consoleError\n  })\n\n  it('can rehydrate state', async () => {\n    const onRehydrateStorageSpy = vi.fn()\n    const storage = {\n      getItem: async (name: string) => {\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: 42, name },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(\n        () => ({\n          count: 0,\n          name: 'empty',\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        },\n      ),\n    )\n\n    function Counter() {\n      const { count, name } = useBoundStore()\n      return (\n        <div>\n          count: {count}, name: {name}\n        </div>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0, name: empty')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(\n      screen.getByText('count: 42, name: test-storage'),\n    ).toBeInTheDocument()\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      { count: 42, name: 'test-storage' },\n      undefined,\n    )\n  })\n\n  it('can throw rehydrate error', async () => {\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        throw new Error('getItem error')\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      undefined,\n      new Error('getItem error'),\n    )\n  })\n\n  it('can persist state', async () => {\n    const { storage, setItemSpy } = createPersistantStore(null)\n\n    const createStore = () => {\n      const onRehydrateStorageSpy = vi.fn()\n      const useBoundStore = create(\n        persist(() => ({ count: 0 }), {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        }),\n      )\n      return { useBoundStore, onRehydrateStorageSpy }\n    }\n\n    // Initialize from empty storage\n    const { useBoundStore, onRehydrateStorageSpy } = createStore()\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith({ count: 0 }, undefined)\n\n    // Write something to the store\n    act(() => {\n      useBoundStore.setState({ count: 42 })\n    })\n    expect(screen.getByText('count: 42')).toBeInTheDocument()\n    expect(setItemSpy).toHaveBeenCalledWith(\n      'test-storage',\n      JSON.stringify({ state: { count: 42 }, version: 0 }),\n    )\n\n    // Create the same store a second time and check if the persisted state\n    // is loaded correctly\n    const {\n      useBoundStore: useBoundStore2,\n      onRehydrateStorageSpy: onRehydrateStorageSpy2,\n    } = createStore()\n    function Counter2() {\n      const { count } = useBoundStore2()\n      return <div>count2: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter2 />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count2: 42')).toBeInTheDocument()\n    expect(onRehydrateStorageSpy2).toHaveBeenCalledWith(\n      { count: 42 },\n      undefined,\n    )\n  })\n\n  it('can async migrate persisted state', async () => {\n    const setItemSpy = vi.fn()\n    const onRehydrateStorageSpy = vi.fn()\n    const migrateSpy = vi.fn(async () => {\n      await sleep(10)\n      return { count: 99 }\n    })\n\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: 42 },\n          version: 12,\n        })\n      },\n      setItem: setItemSpy,\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        version: 13,\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n        migrate: migrateSpy,\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(20))\n    expect(screen.getByText('count: 99')).toBeInTheDocument()\n    expect(migrateSpy).toHaveBeenCalledWith({ count: 42 }, 12)\n    expect(setItemSpy).toHaveBeenCalledWith(\n      'test-storage',\n      JSON.stringify({\n        state: { count: 99 },\n        version: 13,\n      }),\n    )\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith({ count: 99 }, undefined)\n  })\n\n  it('can merge partial persisted state', async () => {\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: 42 },\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create<{\n      count: number\n      name: string\n      setName: (name: string) => void\n    }>()(\n      persist(\n        (set) => ({\n          count: 0,\n          name: 'unknown',\n          setName: (name: string) => {\n            set({ name })\n          },\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n        },\n      ),\n    )\n\n    function Component() {\n      const { count, setName, name } = useBoundStore()\n      useEffect(() => {\n        setName('test')\n      }, [setName])\n      return (\n        <div>\n          <div>count: {count}</div>\n          <div>name: {name}</div>\n        </div>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Component />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count: 42')).toBeInTheDocument()\n    expect(screen.getByText('name: test')).toBeInTheDocument()\n\n    expect(useBoundStore.getState()).toEqual(\n      expect.objectContaining({\n        count: 42,\n        name: 'test',\n      }),\n    )\n  })\n\n  it('can correctly handle a missing migrate function', async () => {\n    console.error = vi.fn()\n    const onRehydrateStorageSpy = vi.fn()\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: 42 },\n          version: 12,\n        })\n      },\n      setItem: (_: string, _value: string) => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        version: 13,\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(console.error).toHaveBeenCalled()\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith({ count: 0 }, undefined)\n  })\n\n  it('can throw migrate error', async () => {\n    console.error = vi.fn()\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: {},\n          version: 12,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        version: 13,\n        storage: createJSONStorage(() => storage),\n        migrate: () => {\n          throw new Error('migrate error')\n        },\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      undefined,\n      new Error('migrate error'),\n    )\n  })\n\n  it('passes the latest state to onRehydrateStorage and onHydrate on first hydrate', async () => {\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({ state: { count: 1 } })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: onRehydrateStorageSpy,\n      }),\n    )\n\n    /**\n     * NOTE: It's currently not possible to add an 'onHydrate' listener which will be\n     * invoked prior to the first hydration. This is because, during first hydration,\n     * the 'onHydrate' listener set (which will be empty) is evaluated before the\n     * 'persist' API is exposed to the caller of 'create'/'createStore'.\n     *\n     * const onHydrateSpy = vi.fn()\n     * useBoundStore.persist.onHydrate(onHydrateSpy)\n     * ...\n     * await act(() => vi.advanceTimersByTimeAsync(10))\n     * expect(onHydrateSpy).toHaveBeenCalledWith({ count: 0 })\n     */\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n    // The 'onRehydrateStorage' spy is invoked prior to rehydration, so it should\n    // be passed the default state.\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith({ count: 0 })\n  })\n\n  it('gives the merged state to onRehydrateStorage', async () => {\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: 1 },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const unstorableMethod = () => {}\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0, unstorableMethod }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      { count: 1, unstorableMethod },\n      undefined,\n    )\n  })\n\n  it('can custom merge the stored state', async () => {\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: {\n            count: 1,\n            actions: {},\n          },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const unstorableMethod = () => {}\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0, actions: { unstorableMethod } }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        merge: (_persistedState, currentState) => {\n          const persistedState = _persistedState as any\n          delete persistedState.actions\n\n          return {\n            ...currentState,\n            ...persistedState,\n          }\n        },\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(useBoundStore.getState()).toEqual({\n      count: 1,\n      actions: {\n        unstorableMethod,\n      },\n    })\n  })\n\n  it(\"can merge the state when the storage item doesn't have a version\", async () => {\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: {\n            count: 1,\n          },\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(useBoundStore.getState()).toEqual({\n      count: 1,\n    })\n  })\n\n  it('can manually rehydrate through the api', async () => {\n    const storageValue = '{\"state\":{\"count\":1},\"version\":0}'\n\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return ''\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    storage.getItem = async () => {\n      await sleep(10)\n      return storageValue\n    }\n    const rehydratePromise = useBoundStore.persist.rehydrate()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    await rehydratePromise\n    expect(useBoundStore.getState()).toEqual({\n      count: 1,\n    })\n  })\n\n  it('can check if the store has been hydrated through the api', async () => {\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return null\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n    expect(useBoundStore.persist.hasHydrated()).toBe(false)\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(useBoundStore.persist.hasHydrated()).toBe(true)\n\n    const rehydratePromise = useBoundStore.persist.rehydrate()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    await rehydratePromise\n    expect(useBoundStore.persist.hasHydrated()).toBe(true)\n  })\n\n  it('can skip initial hydration', async () => {\n    const storage = {\n      getItem: async (name: string) => {\n        await sleep(10)\n        return {\n          state: { count: 42, name },\n          version: 0,\n        }\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const onRehydrateStorageSpy = vi.fn()\n    const useBoundStore = create(\n      persist(\n        () => ({\n          count: 0,\n          name: 'empty',\n        }),\n        {\n          name: 'test-storage',\n          storage: storage,\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n          skipHydration: true,\n        },\n      ),\n    )\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 0,\n      name: 'empty',\n    })\n\n    // Asserting store hasn't hydrated\n    expect(useBoundStore.persist.hasHydrated()).toBe(false)\n\n    const rehydratePromise = useBoundStore.persist.rehydrate()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    await rehydratePromise\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 42,\n      name: 'test-storage',\n    })\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      { count: 42, name: 'test-storage' },\n      undefined,\n    )\n  })\n\n  it('handles state updates during onRehydrateStorage', async () => {\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({ state: { count: 1 } })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create<{ count: number; inc: () => void }>()(\n      persist(\n        (set) => ({\n          count: 0,\n          inc: () => set((s) => ({ count: s.count + 1 })),\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => (s) => s?.inc(),\n        },\n      ),\n    )\n\n    function Counter() {\n      const { count } = useBoundStore()\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n    expect(useBoundStore.getState().count).toEqual(2)\n  })\n\n  it('passes latest state to post-rehydration callback after hydration-triggered updates', async () => {\n    const onRehydrateStorageSpy = vi.fn()\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: 1, bumped: false },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0, bumped: false }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    let patchedDuringHydration = false\n    const unsubscribe = useBoundStore.subscribe((state) => {\n      if (!patchedDuringHydration && state.count === 1 && !state.bumped) {\n        patchedDuringHydration = true\n        useBoundStore.setState({ bumped: true })\n      }\n    })\n\n    function Counter() {\n      const { count, bumped } = useBoundStore()\n      return (\n        <div>\n          count: {count}, bumped: {String(bumped)}\n        </div>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('count: 1, bumped: true')).toBeInTheDocument()\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      { count: 1, bumped: true },\n      undefined,\n    )\n    unsubscribe()\n  })\n\n  it('can rehydrate state with custom deserialized Map', async () => {\n    const onRehydrateStorageSpy = vi.fn()\n    const storage = {\n      getItem: async () => {\n        await sleep(10)\n        return JSON.stringify({\n          state: {\n            map: { type: 'Map', value: [['foo', 'bar']] },\n          },\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(\n        () => ({\n          map: new Map(),\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage, { replacer, reviver }),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        },\n      ),\n    )\n\n    function MapDisplay() {\n      const { map } = useBoundStore()\n      return <div>map: {map.get('foo')}</div>\n    }\n\n    render(\n      <StrictMode>\n        <MapDisplay />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('map: bar')).toBeInTheDocument()\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      { map: new Map([['foo', 'bar']]) },\n      undefined,\n    )\n  })\n\n  it('can persist state with custom serialization of Map', async () => {\n    const { storage, setItemSpy } = createPersistantStore(null)\n    const map = new Map()\n\n    const createStore = () => {\n      const onRehydrateStorageSpy = vi.fn()\n      const useBoundStore = create(\n        persist(() => ({ map }), {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage, { replacer, reviver }),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        }),\n      )\n      return { useBoundStore, onRehydrateStorageSpy }\n    }\n\n    // Initialize from empty storage\n    const { useBoundStore, onRehydrateStorageSpy } = createStore()\n\n    function MapDisplay() {\n      const { map } = useBoundStore()\n      return <div>map-content: {map.get('foo')}</div>\n    }\n\n    render(\n      <StrictMode>\n        <MapDisplay />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('map-content:')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith({ map }, undefined)\n\n    // Write something to the store\n    const updatedMap = new Map(map).set('foo', 'bar')\n    act(() => {\n      useBoundStore.setState({ map: updatedMap })\n    })\n    expect(screen.getByText('map-content: bar')).toBeInTheDocument()\n\n    expect(setItemSpy).toHaveBeenCalledWith(\n      'test-storage',\n      JSON.stringify({\n        state: { map: { type: 'Map', value: [['foo', 'bar']] } },\n        version: 0,\n      }),\n    )\n\n    // Create the same store a second time and check if the persisted state\n    // is loaded correctly\n    const {\n      useBoundStore: useBoundStore2,\n      onRehydrateStorageSpy: onRehydrateStorageSpy2,\n    } = createStore()\n    function MapDisplay2() {\n      const { map } = useBoundStore2()\n      return <div>map-content2: {map.get('foo')}</div>\n    }\n\n    render(\n      <StrictMode>\n        <MapDisplay2 />\n      </StrictMode>,\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    expect(screen.getByText('map-content2: bar')).toBeInTheDocument()\n    expect(onRehydrateStorageSpy2).toHaveBeenCalledWith(\n      { map: updatedMap },\n      undefined,\n    )\n  })\n\n  it('should handle multiple concurrent rehydrate calls (only last one wins)', async () => {\n    let callCount = 0\n    const storage = {\n      getItem: async () => {\n        const currentCall = ++callCount\n        await sleep(10)\n        return JSON.stringify({\n          state: { count: currentCall * 10 },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const onFinishHydrationSpy = vi.fn()\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        skipHydration: true,\n      }),\n    )\n\n    useBoundStore.persist.onFinishHydration(onFinishHydrationSpy)\n\n    // Start first rehydration\n    const promise1 = useBoundStore.persist.rehydrate()\n    // Immediately start second rehydration (before first completes)\n    const promise2 = useBoundStore.persist.rehydrate()\n    // Start third rehydration\n    const promise3 = useBoundStore.persist.rehydrate()\n\n    // Advance time to complete all hydrations\n    await act(() => vi.advanceTimersByTimeAsync(30))\n    await Promise.all([promise1, promise2, promise3])\n\n    // Only the last rehydration should have applied its state\n    // callCount will be 3, so count should be 30\n    expect(useBoundStore.getState().count).toBe(30)\n\n    // onFinishHydration should only be called once (for the last hydration)\n    expect(onFinishHydrationSpy).toHaveBeenCalledTimes(1)\n    expect(onFinishHydrationSpy).toHaveBeenCalledWith({ count: 30 })\n  })\n\n  it('should not overwrite user state changes made during async hydration', async () => {\n    const storage = {\n      getItem: async () => {\n        await sleep(20)\n        return JSON.stringify({\n          state: { count: 42, userValue: 'from-storage' },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0, userValue: 'initial' }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        // Custom merge that preserves user changes made during hydration\n        merge: (persistedState, currentState) => ({\n          ...currentState,\n          ...(persistedState as object),\n        }),\n      }),\n    )\n\n    // User makes a change while hydration is in progress\n    await act(() => vi.advanceTimersByTimeAsync(10))\n    useBoundStore.setState({ count: 100 })\n\n    // Complete hydration\n    await act(() => vi.advanceTimersByTimeAsync(10))\n\n    // The merge function combines storage state with current state\n    // Storage has count: 42, userValue: 'from-storage'\n    // Current state before merge has count: 100, userValue: 'initial'\n    // After merge (storage overwrites): count: 42, userValue: 'from-storage'\n    expect(useBoundStore.getState()).toEqual({\n      count: 42,\n      userValue: 'from-storage',\n    })\n  })\n\n  it('should abort hydration with async migration when newer hydration starts', async () => {\n    let migrationCount = 0\n    const storage = {\n      getItem: async () => {\n        await sleep(5)\n        return JSON.stringify({\n          state: { count: 1 },\n          version: 0,\n        })\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        version: 1,\n        migrate: async (state) => {\n          migrationCount++\n          await sleep(10)\n          return { count: (state as { count: number }).count * 10 }\n        },\n        skipHydration: true,\n      }),\n    )\n\n    // Start first rehydration (will trigger migration)\n    useBoundStore.persist.rehydrate()\n    await act(() => vi.advanceTimersByTimeAsync(5)) // getItem completes\n\n    // Start second rehydration before migration completes\n    useBoundStore.persist.rehydrate()\n\n    // Advance time to complete everything\n    await act(() => vi.advanceTimersByTimeAsync(20))\n\n    // Both hydrations triggered migration, but only last one should apply\n    expect(migrationCount).toBe(2)\n    // Final state should be from second hydration's migration\n    expect(useBoundStore.getState().count).toBe(10)\n  })\n})\n"
  },
  {
    "path": "tests/persistSync.test.tsx",
    "content": "/// <reference types=\"node\" />\n\nimport { afterEach, describe, expect, it, vi } from 'vitest'\nimport { create } from 'zustand'\nimport { createJSONStorage, persist } from 'zustand/middleware'\nimport { replacer, reviver } from './test-utils'\n\nconst createPersistentStore = (initialValue: string | null) => {\n  let state = initialValue\n\n  const getItem = (): string | null => {\n    getItemSpy()\n    return state\n  }\n  const setItem = (name: string, newState: string) => {\n    setItemSpy(name, newState)\n    state = newState\n  }\n  const removeItem = (name: string) => {\n    removeItemSpy(name)\n    state = null\n  }\n\n  const getItemSpy = vi.fn()\n  const setItemSpy = vi.fn()\n  const removeItemSpy = vi.fn()\n\n  return {\n    storage: { getItem, setItem, removeItem },\n    getItemSpy,\n    setItemSpy,\n  }\n}\n\ndescribe('persist middleware with sync configuration', () => {\n  const consoleError = console.error\n  afterEach(() => {\n    console.error = consoleError\n  })\n\n  it('can rehydrate state', () => {\n    const storage = {\n      getItem: (name: string) =>\n        JSON.stringify({\n          state: { count: 42, name },\n          version: 0,\n        }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const onRehydrateStorageSpy = vi.fn()\n    const useBoundStore = create(\n      persist(\n        () => ({\n          count: 0,\n          name: 'empty',\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        },\n      ),\n    )\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 42,\n      name: 'test-storage',\n    })\n    expect(onRehydrateStorageSpy).toBeCalledWith(\n      { count: 42, name: 'test-storage' },\n      undefined,\n    )\n  })\n\n  it('can throw rehydrate error', () => {\n    const storage = {\n      getItem: () => {\n        throw new Error('getItem error')\n      },\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const spy = vi.fn()\n    create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => spy,\n      }),\n    )\n\n    expect(spy).toBeCalledWith(undefined, new Error('getItem error'))\n  })\n\n  it('can persist state', () => {\n    const { storage, setItemSpy } = createPersistentStore(null)\n\n    const createStore = () => {\n      const onRehydrateStorageSpy = vi.fn()\n      const useBoundStore = create(\n        persist(() => ({ count: 0 }), {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        }),\n      )\n      return { useBoundStore, onRehydrateStorageSpy }\n    }\n\n    // Initialize from empty storage\n    const { useBoundStore, onRehydrateStorageSpy } = createStore()\n    expect(useBoundStore.getState()).toEqual({ count: 0 })\n    expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined)\n\n    // Write something to the store\n    useBoundStore.setState({ count: 42 })\n    expect(useBoundStore.getState()).toEqual({ count: 42 })\n    expect(setItemSpy).toBeCalledWith(\n      'test-storage',\n      JSON.stringify({ state: { count: 42 }, version: 0 }),\n    )\n\n    // Create the same store a second time and check if the persisted state\n    // is loaded correctly\n    const {\n      useBoundStore: useBoundStore2,\n      onRehydrateStorageSpy: onRehydrateStorageSpy2,\n    } = createStore()\n    expect(useBoundStore2.getState()).toEqual({ count: 42 })\n    expect(onRehydrateStorageSpy2).toBeCalledWith({ count: 42 }, undefined)\n  })\n\n  it('can non-async migrate persisted state', () => {\n    const setItemSpy = vi.fn()\n    const onRehydrateStorageSpy = vi.fn()\n    const migrateSpy = vi.fn(() => ({ count: 99 }))\n\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          state: { count: 42 },\n          version: 12,\n        }),\n      setItem: setItemSpy,\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        version: 13,\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n        migrate: migrateSpy,\n      }),\n    )\n\n    expect(useBoundStore.getState()).toEqual({ count: 99 })\n    expect(migrateSpy).toBeCalledWith({ count: 42 }, 12)\n    expect(setItemSpy).toBeCalledWith(\n      'test-storage',\n      JSON.stringify({\n        state: { count: 99 },\n        version: 13,\n      }),\n    )\n    expect(onRehydrateStorageSpy).toBeCalledWith({ count: 99 }, undefined)\n  })\n\n  it('can correctly handle a missing migrate function', () => {\n    console.error = vi.fn()\n    const onRehydrateStorageSpy = vi.fn()\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          state: { count: 42 },\n          version: 12,\n        }),\n      setItem: (_: string, _value: string) => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        version: 13,\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    expect(useBoundStore.getState()).toEqual({ count: 0 })\n    expect(console.error).toHaveBeenCalled()\n    expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined)\n  })\n\n  it('can throw migrate error', () => {\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          state: {},\n          version: 12,\n        }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        version: 13,\n        storage: createJSONStorage(() => storage),\n        migrate: () => {\n          throw new Error('migrate error')\n        },\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    expect(useBoundStore.getState()).toEqual({ count: 0 })\n    expect(onRehydrateStorageSpy).toBeCalledWith(\n      undefined,\n      new Error('migrate error'),\n    )\n  })\n\n  it('passes the latest state to onRehydrateStorage and onHydrate on first hydrate', () => {\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: () => JSON.stringify({ state: { count: 1 } }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: onRehydrateStorageSpy,\n      }),\n    )\n\n    /**\n     * NOTE: It's currently not possible to add an 'onHydrate' listener which will be\n     * invoked prior to the first hydration. This is because, during first hydration,\n     * the 'onHydrate' listener set (which will be empty) is evaluated before the\n     * 'persist' API is exposed to the caller of 'create'/'createStore'.\n     *\n     * const onHydrateSpy = vi.fn()\n     * useBoundStore.persist.onHydrate(onHydrateSpy)\n     * expect(onHydrateSpy).toBeCalledWith({ count: 0 })\n     */\n\n    // The 'onRehydrateStorage' and 'onHydrate' spies are invoked prior to rehydration,\n    // so they should both be passed the default state.\n    expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 })\n    expect(useBoundStore.getState()).toEqual({ count: 1 })\n  })\n\n  it('gives the merged state to onRehydrateStorage', () => {\n    const onRehydrateStorageSpy = vi.fn()\n\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          state: { count: 1 },\n          version: 0,\n        }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const unstorableMethod = () => {}\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0, unstorableMethod }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        onRehydrateStorage: () => onRehydrateStorageSpy,\n      }),\n    )\n\n    const expectedState = { count: 1, unstorableMethod }\n\n    expect(useBoundStore.getState()).toEqual(expectedState)\n    expect(onRehydrateStorageSpy).toBeCalledWith(expectedState, undefined)\n  })\n\n  it('can custom merge the stored state', () => {\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          state: {\n            count: 1,\n            actions: {},\n          },\n          version: 0,\n        }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const unstorableMethod = () => {}\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0, actions: { unstorableMethod } }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        merge: (_persistedState, currentState) => {\n          const persistedState = _persistedState as any\n          delete persistedState.actions\n\n          return {\n            ...currentState,\n            ...persistedState,\n          }\n        },\n      }),\n    )\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 1,\n      actions: {\n        unstorableMethod,\n      },\n    })\n  })\n\n  it(\"can merge the state when the storage item doesn't have a version\", () => {\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          state: {\n            count: 1,\n          },\n        }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 1,\n    })\n  })\n\n  it('can filter the persisted value', () => {\n    const setItemSpy = vi.fn()\n\n    const storage = {\n      getItem: () => '',\n      setItem: setItemSpy,\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(\n        () => ({\n          object: {\n            first: '0',\n            second: '1',\n          },\n          array: [\n            {\n              value: '0',\n            },\n            {\n              value: '1',\n            },\n            {\n              value: '2',\n            },\n          ],\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          partialize: (state) => {\n            return {\n              object: {\n                first: state.object.first,\n              },\n              array: state.array.filter((e) => e.value !== '1'),\n            }\n          },\n        },\n      ),\n    )\n\n    useBoundStore.setState({})\n    expect(setItemSpy).toBeCalledWith(\n      'test-storage',\n      JSON.stringify({\n        state: {\n          object: {\n            first: '0',\n          },\n          array: [\n            {\n              value: '0',\n            },\n            {\n              value: '2',\n            },\n          ],\n        },\n        version: 0,\n      }),\n    )\n  })\n\n  it('can access the options through the api', () => {\n    const storage = {\n      getItem: () => null,\n      setItem: vi.fn(),\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n    expect(useBoundStore.persist.getOptions().name).toBeDefined()\n    expect(useBoundStore.persist.getOptions().name).toBe('test-storage')\n  })\n\n  it('can change the options through the api', () => {\n    const setItemSpy = vi.fn()\n\n    const storage = {\n      getItem: () => null,\n      setItem: setItemSpy,\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n        partialize: (s) => s as Partial<typeof s>,\n      }),\n    )\n\n    useBoundStore.setState({})\n    expect(setItemSpy).toBeCalledWith(\n      'test-storage',\n      '{\"state\":{\"count\":0},\"version\":0}',\n    )\n\n    useBoundStore.persist.setOptions({\n      name: 'test-storage-2',\n      partialize: (state) =>\n        Object.fromEntries(\n          Object.entries(state).filter(([key]) => key !== 'count'),\n        ),\n    })\n    useBoundStore.setState({})\n    expect(setItemSpy).toBeCalledWith(\n      'test-storage-2',\n      '{\"state\":{},\"version\":0}',\n    )\n  })\n\n  it('can clear the storage through the api', () => {\n    const removeItemSpy = vi.fn()\n\n    const storage = {\n      getItem: () => null,\n      setItem: () => {},\n      removeItem: removeItemSpy,\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    useBoundStore.persist.clearStorage()\n    expect(removeItemSpy).toBeCalledWith('test-storage')\n  })\n\n  it('can manually rehydrate through the api', () => {\n    const storageValue = '{\"state\":{\"count\":1},\"version\":0}'\n\n    const storage = {\n      getItem: () => '',\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    storage.getItem = () => storageValue\n    useBoundStore.persist.rehydrate()\n    expect(useBoundStore.getState()).toEqual({\n      count: 1,\n    })\n  })\n\n  it('can check if the store has been hydrated through the api', async () => {\n    const storage = {\n      getItem: () => null,\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    expect(useBoundStore.persist.hasHydrated()).toBe(true)\n\n    await useBoundStore.persist.rehydrate()\n    expect(useBoundStore.persist.hasHydrated()).toBe(true)\n  })\n\n  it('can wait for rehydration through the api', async () => {\n    const storageValue1 = '{\"state\":{\"count\":1},\"version\":0}'\n    const storageValue2 = '{\"state\":{\"count\":2},\"version\":0}'\n\n    const onHydrateSpy1 = vi.fn()\n    const onHydrateSpy2 = vi.fn()\n    const onFinishHydrationSpy1 = vi.fn()\n    const onFinishHydrationSpy2 = vi.fn()\n\n    const storage = {\n      getItem: () => '',\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({ count: 0 }), {\n        name: 'test-storage',\n        storage: createJSONStorage(() => storage),\n      }),\n    )\n\n    const hydrateUnsub1 = useBoundStore.persist.onHydrate(onHydrateSpy1)\n    useBoundStore.persist.onHydrate(onHydrateSpy2)\n\n    const finishHydrationUnsub1 = useBoundStore.persist.onFinishHydration(\n      onFinishHydrationSpy1,\n    )\n    useBoundStore.persist.onFinishHydration(onFinishHydrationSpy2)\n\n    storage.getItem = () => storageValue1\n    await useBoundStore.persist.rehydrate()\n    expect(onHydrateSpy1).toBeCalledWith({ count: 0 })\n    expect(onHydrateSpy2).toBeCalledWith({ count: 0 })\n    expect(onFinishHydrationSpy1).toBeCalledWith({ count: 1 })\n    expect(onFinishHydrationSpy2).toBeCalledWith({ count: 1 })\n\n    hydrateUnsub1()\n    finishHydrationUnsub1()\n\n    storage.getItem = () => storageValue2\n    await useBoundStore.persist.rehydrate()\n    expect(onHydrateSpy1).not.toBeCalledTimes(2)\n    expect(onHydrateSpy2).toBeCalledWith({ count: 1 })\n    expect(onFinishHydrationSpy1).not.toBeCalledTimes(2)\n    expect(onFinishHydrationSpy2).toBeCalledWith({ count: 2 })\n  })\n\n  it('can skip initial hydration', async () => {\n    const storage = {\n      getItem: (name: string) => ({\n        state: { count: 42, name },\n        version: 0,\n      }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const onRehydrateStorageSpy = vi.fn()\n    const useBoundStore = create(\n      persist(\n        () => ({\n          count: 0,\n          name: 'empty',\n        }),\n        {\n          name: 'test-storage',\n          storage: storage,\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n          skipHydration: true,\n        },\n      ),\n    )\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 0,\n      name: 'empty',\n    })\n\n    // Asserting store hasn't hydrated\n    expect(useBoundStore.persist.hasHydrated()).toBe(false)\n\n    await useBoundStore.persist.rehydrate()\n\n    expect(useBoundStore.getState()).toEqual({\n      count: 42,\n      name: 'test-storage',\n    })\n    expect(onRehydrateStorageSpy).toHaveBeenCalledWith(\n      { count: 42, name: 'test-storage' },\n      undefined,\n    )\n  })\n\n  it('handles state updates during onRehydrateStorage', () => {\n    const storage = {\n      getItem: () => JSON.stringify({ state: { count: 1 } }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create<{ count: number; inc: () => void }>()(\n      persist(\n        (set) => ({\n          count: 0,\n          inc: () => set((s) => ({ count: s.count + 1 })),\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => (s) => s?.inc(),\n        },\n      ),\n    )\n\n    expect(useBoundStore.getState().count).toEqual(2)\n  })\n\n  it('can rehydrate state with custom deserialized Map', () => {\n    const storage = {\n      getItem: () =>\n        JSON.stringify({\n          map: { type: 'Map', value: [['foo', 'bar']] },\n        }),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n\n    const map = new Map()\n    const onRehydrateStorageSpy = vi.fn()\n    const useBoundStore = create(\n      persist(\n        () => ({\n          map,\n        }),\n        {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        },\n      ),\n    )\n\n    const updatedMap = map.set('foo', 'bar')\n    expect(useBoundStore.getState()).toEqual({\n      map: updatedMap,\n    })\n    expect(onRehydrateStorageSpy).toBeCalledWith({ map: updatedMap }, undefined)\n  })\n\n  it('can persist state with custom serialization of Map', () => {\n    const { storage, setItemSpy } = createPersistentStore(null)\n    const map = new Map()\n\n    const createStore = () => {\n      const onRehydrateStorageSpy = vi.fn()\n      const useBoundStore = create(\n        persist(() => ({ map }), {\n          name: 'test-storage',\n          storage: createJSONStorage(() => storage, { replacer, reviver }),\n          onRehydrateStorage: () => onRehydrateStorageSpy,\n        }),\n      )\n      return { useBoundStore, onRehydrateStorageSpy }\n    }\n\n    // Initialize from empty storage\n    const { useBoundStore, onRehydrateStorageSpy } = createStore()\n    expect(useBoundStore.getState()).toEqual({ map })\n    expect(onRehydrateStorageSpy).toBeCalledWith({ map }, undefined)\n\n    // Write something to the store\n    const updatedMap = map.set('foo', 'bar')\n    useBoundStore.setState({ map: updatedMap })\n    expect(useBoundStore.getState()).toEqual({\n      map: updatedMap,\n    })\n    expect(setItemSpy).toBeCalledWith(\n      'test-storage',\n      JSON.stringify({\n        state: { map: { type: 'Map', value: [['foo', 'bar']] } },\n        version: 0,\n      }),\n    )\n\n    // Create the same store a second time and check if the persisted state\n    // is loaded correctly\n    const {\n      useBoundStore: useBoundStore2,\n      onRehydrateStorageSpy: onRehydrateStorageSpy2,\n    } = createStore()\n    expect(useBoundStore2.getState()).toEqual({ map: updatedMap })\n    expect(onRehydrateStorageSpy2).toBeCalledWith(\n      { map: updatedMap },\n      undefined,\n    )\n  })\n\n  it('does not call setItem when hydrating from its own storage', async () => {\n    const setItem = vi.fn()\n    const storage = {\n      getItem: (name: string) => ({\n        state: { count: 42, name },\n        version: 0,\n      }),\n      setItem,\n      removeItem: () => {},\n    }\n\n    const useBoundStore = create(\n      persist(() => ({}), {\n        name: 'test-storage',\n        storage: storage,\n      }),\n    )\n\n    expect(useBoundStore.persist.hasHydrated()).toBe(true)\n    expect(setItem).toBeCalledTimes(0)\n  })\n})\n"
  },
  {
    "path": "tests/setup.ts",
    "content": "import '@testing-library/jest-dom/vitest'\n"
  },
  {
    "path": "tests/shallow.test.tsx",
    "content": "import { useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { create } from 'zustand'\nimport { shallow, useShallow } from 'zustand/shallow'\nimport { createWithEqualityFn } from 'zustand/traditional'\n\ndescribe('types', () => {\n  it('works with useBoundStore and array selector (#1107)', () => {\n    const useBoundStore = createWithEqualityFn(() => ({\n      villages: [] as { name: string }[],\n    }))\n    const Component = () => {\n      const villages = useBoundStore((state) => state.villages, shallow)\n      return <>{villages.length}</>\n    }\n    expect(Component).toBeDefined()\n  })\n\n  it('works with useBoundStore and string selector (#1107)', () => {\n    const useBoundStore = createWithEqualityFn(() => ({\n      refetchTimestamp: '',\n    }))\n    const Component = () => {\n      const refetchTimestamp = useBoundStore(\n        (state) => state.refetchTimestamp,\n        shallow,\n      )\n      return <>{refetchTimestamp.toUpperCase()}</>\n    }\n    expect(Component).toBeDefined()\n  })\n})\n\ndescribe('useShallow', () => {\n  const testUseShallowSimpleCallback = vi.fn()\n  const TestUseShallowSimple = ({\n    selector,\n    state,\n  }: {\n    state: Record<string, unknown>\n    selector: (state: Record<string, unknown>) => string[]\n  }) => {\n    const selectorOutput = selector(state)\n    const useShallowOutput = useShallow(selector)(state)\n\n    return (\n      <div\n        data-testid=\"test-shallow\"\n        onClick={() =>\n          testUseShallowSimpleCallback({ selectorOutput, useShallowOutput })\n        }\n      />\n    )\n  }\n\n  beforeEach(() => {\n    testUseShallowSimpleCallback.mockClear()\n  })\n\n  it('input and output selectors always return shallow equal values', () => {\n    const { rerender } = render(\n      <TestUseShallowSimple state={{ a: 1, b: 2 }} selector={Object.keys} />,\n    )\n\n    expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(0)\n    fireEvent.click(screen.getByTestId('test-shallow'))\n\n    const firstRender = testUseShallowSimpleCallback.mock.lastCall?.[0]\n\n    expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(1)\n    expect(firstRender).toBeTruthy()\n    expect(firstRender?.selectorOutput).toEqual(firstRender?.useShallowOutput)\n\n    rerender(\n      <TestUseShallowSimple\n        state={{ a: 1, b: 2, c: 3 }}\n        selector={Object.keys}\n      />,\n    )\n\n    fireEvent.click(screen.getByTestId('test-shallow'))\n    expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2)\n\n    const secondRender = testUseShallowSimpleCallback.mock.lastCall?.[0]\n\n    expect(secondRender).toBeTruthy()\n    expect(secondRender?.selectorOutput).toEqual(secondRender?.useShallowOutput)\n  })\n\n  it('returns the previously computed instance when possible', () => {\n    const state = { a: 1, b: 2 }\n    const { rerender } = render(\n      <TestUseShallowSimple state={state} selector={Object.keys} />,\n    )\n\n    fireEvent.click(screen.getByTestId('test-shallow'))\n    expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(1)\n    const output1 =\n      testUseShallowSimpleCallback.mock.lastCall?.[0]?.useShallowOutput\n    expect(output1).toBeTruthy()\n\n    // Change selector, same output\n    rerender(\n      <TestUseShallowSimple\n        state={state}\n        selector={(state) => Object.keys(state)}\n      />,\n    )\n\n    fireEvent.click(screen.getByTestId('test-shallow'))\n    expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2)\n\n    const output2 =\n      testUseShallowSimpleCallback.mock.lastCall?.[0]?.useShallowOutput\n    expect(output2).toBeTruthy()\n\n    expect(output2).toBe(output1)\n  })\n\n  it('only re-renders if selector output has changed according to shallow', () => {\n    let countRenders = 0\n    const useMyStore = create(\n      (): Record<string, unknown> => ({ a: 1, b: 2, c: 3 }),\n    )\n    const TestShallow = ({\n      selector = (state) => Object.keys(state).sort(),\n    }: {\n      selector?: (state: Record<string, unknown>) => string[]\n    }) => {\n      const output = useMyStore(useShallow(selector))\n\n      ++countRenders\n\n      return <div data-testid=\"test-shallow\">{output.join(',')}</div>\n    }\n\n    expect(countRenders).toBe(0)\n    render(<TestShallow />)\n\n    expect(countRenders).toBe(1)\n    expect(screen.getByTestId('test-shallow')).toHaveTextContent('a,b,c')\n\n    act(() => {\n      useMyStore.setState({ a: 4 }) // This will not cause a re-render.\n    })\n\n    expect(countRenders).toBe(1)\n\n    act(() => {\n      useMyStore.setState({ d: 10 }) // This will cause a re-render.\n    })\n\n    expect(countRenders).toBe(2)\n    expect(screen.getByTestId('test-shallow')).toHaveTextContent('a,b,c,d')\n  })\n\n  it('does not cause stale closure issues', () => {\n    const useMyStore = create(\n      (): Record<string, unknown> => ({ a: 1, b: 2, c: 3 }),\n    )\n    const TestShallowWithState = () => {\n      const [count, setCount] = useState(0)\n      const output = useMyStore(\n        useShallow((state) => Object.keys(state).concat([count.toString()])),\n      )\n\n      return (\n        <div\n          data-testid=\"test-shallow\"\n          onClick={() => setCount((prev) => ++prev)}\n        >\n          {output.join(',')}\n        </div>\n      )\n    }\n\n    render(<TestShallowWithState />)\n\n    expect(screen.getByTestId('test-shallow')).toHaveTextContent('a,b,c,0')\n\n    fireEvent.click(screen.getByTestId('test-shallow'))\n\n    expect(screen.getByTestId('test-shallow')).toHaveTextContent('a,b,c,1')\n  })\n})\n"
  },
  {
    "path": "tests/ssr.test.tsx",
    "content": "import React, { useEffect } from 'react'\nimport { act, screen } from '@testing-library/react'\nimport { renderToString } from 'react-dom/server'\nimport { describe, expect, it, vi } from 'vitest'\nimport { create } from 'zustand'\n\ninterface BearStoreState {\n  bears: number\n}\n\ninterface BearStoreAction {\n  increasePopulation: () => void\n}\n\nconst initialState = { bears: 0 }\nconst useBearStore = create<BearStoreState & BearStoreAction>((set) => ({\n  ...initialState,\n  increasePopulation: () => set(({ bears }) => ({ bears: bears + 1 })),\n}))\n\nfunction Counter() {\n  const bears = useBearStore(({ bears }) => bears)\n  const increasePopulation = useBearStore(\n    ({ increasePopulation }) => increasePopulation,\n  )\n\n  useEffect(() => {\n    increasePopulation()\n  }, [increasePopulation])\n\n  return <div>bears: {bears}</div>\n}\n\ndescribe('ssr behavior with react 18+', () => {\n  it('should handle different states between server and client correctly', async () => {\n    const { hydrateRoot } =\n      await vi.importActual<typeof import('react-dom/client')>(\n        'react-dom/client',\n      )\n\n    const view = renderToString(\n      <React.Suspense fallback={<div>Loading...</div>}>\n        <Counter />\n      </React.Suspense>,\n    )\n\n    const container = document.createElement('div')\n    document.body.appendChild(container)\n    container.innerHTML = view\n\n    expect(container).toHaveTextContent(/bears: 0/)\n\n    await act(async () => {\n      hydrateRoot(\n        container,\n        <React.Suspense fallback={<div>Loading...</div>}>\n          <Counter />\n        </React.Suspense>,\n      )\n    })\n\n    expect(screen.getByText('bears: 1')).toBeInTheDocument()\n    document.body.removeChild(container)\n  })\n  it('should not have hydration errors', async () => {\n    const useStore = create(() => ({\n      bears: 0,\n    }))\n\n    const { hydrateRoot } =\n      await vi.importActual<typeof import('react-dom/client')>(\n        'react-dom/client',\n      )\n\n    const Component = () => {\n      const bears = useStore((state) => state.bears)\n      return <div>bears: {bears}</div>\n    }\n\n    const view = renderToString(\n      <React.Suspense fallback={<div>Loading...</div>}>\n        <Component />\n      </React.Suspense>,\n    )\n\n    const container = document.createElement('div')\n    document.body.appendChild(container)\n    container.innerHTML = view\n\n    expect(container).toHaveTextContent(/bears: 0/)\n\n    const consoleMock = vi.spyOn(console, 'error')\n\n    const hydratePromise = act(async () => {\n      hydrateRoot(\n        container,\n        <React.Suspense fallback={<div>Loading...</div>}>\n          <Component />\n        </React.Suspense>,\n      )\n    })\n\n    // set state during hydration\n    useStore.setState({ bears: 1 })\n\n    await hydratePromise\n\n    expect(consoleMock).toHaveBeenCalledTimes(0)\n\n    expect(screen.getByText('bears: 1')).toBeInTheDocument()\n    document.body.removeChild(container)\n  })\n})\n"
  },
  {
    "path": "tests/subscribe.test.tsx",
    "content": "import { describe, expect, it } from 'vitest'\nimport { create } from 'zustand'\n\ndescribe('subscribe()', () => {\n  it('should correctly have access to subscribe', () => {\n    const { subscribe } = create(() => ({ value: 1 }))\n    expect(typeof subscribe).toBe('function')\n  })\n})\n"
  },
  {
    "path": "tests/test-utils.ts",
    "content": "type ReplacedMap = {\n  type: 'Map'\n  value: [string, unknown][]\n}\n\nexport const replacer = (\n  key: string,\n  value: unknown,\n): ReplacedMap | unknown => {\n  if (value instanceof Map) {\n    return {\n      type: 'Map',\n      value: Array.from(value.entries()),\n    }\n  } else {\n    return value\n  }\n}\n\nexport const reviver = (key: string, value: ReplacedMap | unknown): unknown => {\n  if (isReplacedMap(value)) {\n    return new Map(value.value)\n  }\n  return value\n}\n\nconst isReplacedMap = (value: any): value is ReplacedMap => {\n  if (value && value.type === 'Map') {\n    return true\n  }\n\n  return false\n}\n\nexport function sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms))\n}\n"
  },
  {
    "path": "tests/types.test.tsx",
    "content": "import { expect, it } from 'vitest'\nimport { create } from 'zustand'\nimport type {\n  StateCreator,\n  StoreApi,\n  StoreMutatorIdentifier,\n  UseBoundStore,\n} from 'zustand'\nimport { persist } from 'zustand/middleware'\n\nit('can use exposed types', () => {\n  type ExampleState = {\n    num: number\n    numGet: () => number\n    numGetState: () => number\n    numSet: (v: number) => void\n    numSetState: (v: number) => void\n  }\n\n  const listener = (state: ExampleState) => {\n    if (state) {\n      const value = state.num * state.numGet() * state.numGetState()\n      state.numSet(value)\n      state.numSetState(value)\n    }\n  }\n  const selector = (state: ExampleState) => state.num\n  const partial: Partial<ExampleState> = {\n    num: 2,\n    numGet: () => 2,\n  }\n  const partialFn: (state: ExampleState) => Partial<ExampleState> = (\n    state,\n  ) => ({\n    ...state,\n    num: 2,\n  })\n  const equalityFn = (state: ExampleState, newState: ExampleState) =>\n    state !== newState\n\n  const storeApi = create<ExampleState>((set, get) => ({\n    num: 1,\n    numGet: () => get().num,\n    numGetState: () => {\n      // TypeScript can't get the type of storeApi when it tries to enforce the signature of numGetState.\n      // Need to explicitly state the type of storeApi.getState().num or storeApi type will be type 'any'.\n      const result: number = storeApi.getState().num\n      return result\n    },\n    numSet: (v) => {\n      set({ num: v })\n    },\n    numSetState: (v) => {\n      storeApi.setState({ num: v })\n    },\n  }))\n  const useBoundStore = storeApi\n\n  const stateCreator: StateCreator<ExampleState> = (set, get) => ({\n    num: 1,\n    numGet: () => get().num,\n    numGetState: () => get().num,\n    numSet: (v) => {\n      set({ num: v })\n    },\n    numSetState: (v) => {\n      set({ num: v })\n    },\n  })\n\n  function checkAllTypes(\n    _getState: StoreApi<ExampleState>['getState'],\n    _partialState:\n      | Partial<ExampleState>\n      | ((s: ExampleState) => Partial<ExampleState>),\n    _setState: StoreApi<ExampleState>['setState'],\n    _state: object,\n    _stateListener: (state: ExampleState, previousState: ExampleState) => void,\n    _stateSelector: (state: ExampleState) => number,\n    _storeApi: StoreApi<ExampleState>,\n    _subscribe: StoreApi<ExampleState>['subscribe'],\n    _equalityFn: (a: ExampleState, b: ExampleState) => boolean,\n    _stateCreator: StateCreator<ExampleState>,\n    _useBoundStore: UseBoundStore<StoreApi<ExampleState>>,\n  ) {\n    expect(true).toBeTruthy()\n  }\n\n  checkAllTypes(\n    storeApi.getState,\n    Math.random() > 0.5 ? partial : partialFn,\n    storeApi.setState,\n    storeApi.getState(),\n    listener,\n    selector,\n    storeApi,\n    storeApi.subscribe,\n    equalityFn,\n    stateCreator,\n    useBoundStore,\n  )\n})\n\ntype AssertEqual<Type, Expected> = Type extends Expected\n  ? Expected extends Type\n    ? true\n    : never\n  : never\n\nit('should have correct (partial) types for setState', () => {\n  type Count = { count: number }\n\n  const store = create<Count>((set) => ({\n    count: 0,\n    // @ts-expect-error we shouldn't be able to set count to undefined\n    a: () => set(() => ({ count: undefined })),\n    // @ts-expect-error we shouldn't be able to set count to undefined\n    b: () => set({ count: undefined }),\n    c: () => set({ count: 1 }),\n  }))\n\n  const setState: AssertEqual<\n    typeof store.setState,\n    StoreApi<Count>['setState']\n  > = true\n  expect(setState).toEqual(true)\n\n  // ok, should not error\n  store.setState({ count: 1 })\n  store.setState({})\n  store.setState((previous) => previous)\n\n  // @ts-expect-error type undefined is not assignable to type number\n  store.setState({ count: undefined })\n  // @ts-expect-error type undefined is not assignable to type number\n  store.setState((state) => ({ ...state, count: undefined }))\n})\n\nit('should allow for different partial keys to be returnable from setState', () => {\n  type State = {\n    count: number\n    something: string\n  }\n\n  const store = create<State>(() => ({\n    count: 0,\n    something: 'foo',\n  }))\n\n  const setState: AssertEqual<\n    typeof store.setState,\n    StoreApi<State>['setState']\n  > = true\n  expect(setState).toEqual(true)\n\n  // ok, should not error\n  store.setState((previous) => {\n    if (previous.count === 0) {\n      return { count: 1 }\n    }\n    return { count: 0 }\n  })\n  store.setState((previous) => {\n    if (previous.count === 0) {\n      return { count: 1 }\n    }\n    if (previous.count === 1) {\n      return previous\n    }\n    return { something: 'foo' }\n  })\n\n  // @ts-expect-error Type '{ something: boolean; count?: undefined; }' is not assignable to type 'State'.\n  store.setState((previous) => {\n    if (previous.count === 0) {\n      return { count: 1 }\n    }\n    return { something: true }\n  })\n})\n\nit('state is covariant', () => {\n  const store = create<{ count: number; foo: string }>()(() => ({\n    count: 0,\n    foo: '',\n  }))\n\n  const testIsCovariant: StoreApi<{ count: number }> = store\n  expect(testIsCovariant).toBeDefined()\n\n  // @ts-expect-error should not compile\n  const testIsNotContravariant: StoreApi<{\n    count: number\n    foo: string\n    baz: string\n  }> = store\n  expect(testIsNotContravariant).toBeDefined()\n})\n\nit('StateCreator<T, [StoreMutatorIdentfier, unknown][]> is StateCreator<T, []>', () => {\n  interface State {\n    count: number\n    increment: () => void\n  }\n\n  const foo: <M extends [StoreMutatorIdentifier, unknown][]>() => StateCreator<\n    State,\n    M\n  > = () => (set, get) => ({\n    count: 0,\n    increment: () => {\n      set({ count: get().count + 1 })\n    },\n  })\n\n  const store = create<State>()(persist(foo(), { name: 'prefix' }))\n  expect(store).toBeDefined()\n})\n\nit('StateCreator subtyping', () => {\n  interface State {\n    count: number\n    increment: () => void\n  }\n\n  const foo: () => StateCreator<State, []> = () => (set, get) => ({\n    count: 0,\n    increment: () => {\n      set({ count: get().count + 1 })\n    },\n  })\n\n  create<State>()(persist(foo(), { name: 'prefix' }))\n\n  const testSubtyping: StateCreator<State, [['zustand/persist', unknown]]> =\n    {} as StateCreator<State, []>\n  expect(testSubtyping).toBeDefined()\n})\n\nit('set state exists on store with readonly store', () => {\n  interface State {\n    count: number\n    increment: () => void\n  }\n\n  const useStore = create<State>()((set, get) => ({\n    count: 0,\n    increment: () => set({ count: get().count + 1 }),\n  }))\n\n  useStore.setState((state) => ({ ...state, count: state.count + 1 }))\n  expect(useStore).toBeDefined()\n})\n"
  },
  {
    "path": "tests/vanilla/basic.test.ts",
    "content": "import { afterEach, expect, it, vi } from 'vitest'\nimport { createStore } from 'zustand/vanilla'\nimport type { StoreApi } from 'zustand/vanilla'\n\n// To avoid include react deps on vanilla version\nvi.mock('react', () => ({}))\n\nconst consoleError = console.error\nafterEach(() => {\n  console.error = consoleError\n})\n\nit('create a store', () => {\n  let params\n  const result = createStore((...args) => {\n    params = args\n    return { value: null }\n  })\n  expect({ params, result }).toMatchInlineSnapshot(`\n    {\n      \"params\": [\n        [Function],\n        [Function],\n        {\n          \"getInitialState\": [Function],\n          \"getState\": [Function],\n          \"setState\": [Function],\n          \"subscribe\": [Function],\n        },\n      ],\n      \"result\": {\n        \"getInitialState\": [Function],\n        \"getState\": [Function],\n        \"setState\": [Function],\n        \"subscribe\": [Function],\n      },\n    }\n  `)\n})\n\ntype CounterState = {\n  count: number\n  inc: () => void\n}\n\nit('uses the store', async () => {\n  const store = createStore<CounterState>((set) => ({\n    count: 0,\n    inc: () => set((state) => ({ count: state.count + 1 })),\n  }))\n  store.getState().inc()\n\n  expect(store.getState().count).toBe(1)\n})\n\nit('can get the store', async () => {\n  type State = {\n    value: number\n    getState1: () => State\n    getState2: () => State\n  }\n\n  const store = createStore<State>((_, get) => ({\n    value: 1,\n    getState1: () => get(),\n    getState2: (): State => store.getState(),\n  }))\n\n  expect(store.getState().getState1().value).toBe(1)\n  expect(store.getState().getState2().value).toBe(1)\n})\n\nit('can get the initial state', () => {\n  const initial = { value: 1 }\n  const store = createStore(() => initial)\n  store.setState({ value: 2 })\n  expect(store.getInitialState()).toBe(initial)\n})\n\nit('can set the store', async () => {\n  type State = {\n    value: number\n    setState1: StoreApi<State>['setState']\n    setState2: StoreApi<State>['setState']\n  }\n\n  const store = createStore<State>((set) => ({\n    value: 1,\n    setState1: (v) => set(v),\n    setState2: (v): void => store.setState(v),\n  }))\n\n  store.getState().setState1({ value: 2 })\n  expect(store.getState().value).toBe(2)\n  store.getState().setState2({ value: 3 })\n  expect(store.getState().value).toBe(3)\n})\n\nit('both NaN should not update', () => {\n  const store = createStore<number>(() => NaN)\n  const fn = vi.fn()\n\n  store.subscribe(fn)\n  store.setState(NaN)\n\n  expect(fn).not.toBeCalled()\n})\n\nit('can set the store without merging', () => {\n  const { setState, getState } = createStore<{ a: number } | { b: number }>(\n    (_set) => ({\n      a: 1,\n    }),\n  )\n\n  // Should override the state instead of merging.\n  setState({ b: 2 }, true)\n\n  expect(getState()).toEqual({ b: 2 })\n})\n\nit('can set the object store to null', () => {\n  const { setState, getState } = createStore<{ a: number } | null>(() => ({\n    a: 1,\n  }))\n\n  setState(null)\n\n  expect(getState()).toEqual(null)\n})\n\nit('can set the non-object store to null', () => {\n  const { setState, getState } = createStore<string | null>(() => 'value')\n\n  setState(null)\n\n  expect(getState()).toEqual(null)\n})\n\nit('works with non-object state', () => {\n  const store = createStore<number>(() => 1)\n  const inc = () => store.setState((c) => c + 1)\n\n  inc()\n\n  expect(store.getState()).toBe(2)\n})\n"
  },
  {
    "path": "tests/vanilla/shallow.test.tsx",
    "content": "import { describe, expect, it } from 'vitest'\nimport { shallow } from 'zustand/shallow'\n\ndescribe('shallow', () => {\n  it('compares primitive values', () => {\n    expect(shallow(true, true)).toBe(true)\n    expect(shallow(true, false)).toBe(false)\n\n    expect(shallow(1, 1)).toBe(true)\n    expect(shallow(1, 2)).toBe(false)\n\n    expect(shallow('zustand', 'zustand')).toBe(true)\n    expect(shallow('zustand', 'redux')).toBe(false)\n  })\n\n  it('compares objects', () => {\n    expect(shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123 })).toBe(\n      true,\n    )\n\n    expect(\n      shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', foobar: true }),\n    ).toBe(false)\n\n    expect(\n      shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123, foobar: true }),\n    ).toBe(false)\n  })\n\n  it('compares arrays', () => {\n    expect(shallow([1, 2, 3], [1, 2, 3])).toBe(true)\n\n    expect(shallow([1, 2, 3], [2, 3, 4])).toBe(false)\n\n    expect(\n      shallow([{ foo: 'bar' }, { asd: 123 }], [{ foo: 'bar' }, { asd: 123 }]),\n    ).toBe(false)\n\n    expect(shallow([{ foo: 'bar' }], [{ foo: 'bar', asd: 123 }])).toBe(false)\n\n    expect(shallow([1, 2, 3], [2, 3, 1])).toBe(false)\n  })\n\n  it('compares Maps', () => {\n    expect(\n      shallow(\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['asd', 123],\n        ]),\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['asd', 123],\n        ]),\n      ),\n    ).toBe(true)\n\n    expect(\n      shallow(\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['asd', 123],\n        ]),\n        new Map<string, unknown>([\n          ['asd', 123],\n          ['foo', 'bar'],\n        ]),\n      ),\n    ).toBe(true)\n\n    expect(\n      shallow(\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['asd', 123],\n        ]),\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['foobar', true],\n        ]),\n      ),\n    ).toBe(false)\n\n    expect(\n      shallow(\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['asd', 123],\n        ]),\n        new Map<string, unknown>([\n          ['foo', 'bar'],\n          ['asd', 123],\n          ['foobar', true],\n        ]),\n      ),\n    ).toBe(false)\n\n    const obj = {}\n    const obj2 = {}\n    expect(\n      shallow(\n        new Map<object, unknown>([[obj, 'foo']]),\n        new Map<object, unknown>([[obj2, 'foo']]),\n      ),\n    ).toBe(false)\n  })\n\n  it('compares Sets', () => {\n    expect(shallow(new Set(['bar', 123]), new Set(['bar', 123]))).toBe(true)\n\n    expect(shallow(new Set(['bar', 123]), new Set([123, 'bar']))).toBe(true)\n\n    expect(shallow(new Set(['bar', 123]), new Set(['bar', 2]))).toBe(false)\n\n    expect(shallow(new Set(['bar', 123]), new Set(['bar', 123, true]))).toBe(\n      false,\n    )\n\n    const obj = {}\n    const obj2 = {}\n    expect(shallow(new Set([obj]), new Set([obj]))).toBe(true)\n    expect(shallow(new Set([obj]), new Set([obj2]))).toBe(false)\n    expect(shallow(new Set([obj]), new Set([obj, obj2]))).toBe(false)\n    expect(shallow(new Set([obj]), new Set([obj2, obj]))).toBe(false)\n\n    expect(shallow(['bar', 123] as never, new Set(['bar', 123]))).toBe(false)\n  })\n\n  it('compares functions', () => {\n    function firstFnCompare() {\n      return { foo: 'bar' }\n    }\n\n    function secondFnCompare() {\n      return { foo: 'bar' }\n    }\n\n    expect(shallow(firstFnCompare, firstFnCompare)).toBe(true)\n\n    expect(shallow(secondFnCompare, secondFnCompare)).toBe(true)\n\n    expect(shallow(firstFnCompare, secondFnCompare)).toBe(false)\n  })\n\n  it('compares URLSearchParams', () => {\n    expect(\n      shallow(new URLSearchParams({ a: 'a' }), new URLSearchParams({ a: 'a' })),\n    ).toBe(true)\n    expect(\n      shallow(new URLSearchParams({ a: 'a' }), new URLSearchParams({ a: 'b' })),\n    ).toBe(false)\n    expect(\n      shallow(new URLSearchParams({ a: 'a' }), new URLSearchParams({ b: 'b' })),\n    ).toBe(false)\n    expect(\n      shallow(\n        new URLSearchParams({ a: 'a' }),\n        new URLSearchParams({ a: 'a', b: 'b' }),\n      ),\n    ).toBe(false)\n    expect(\n      shallow(\n        new URLSearchParams({ b: 'b', a: 'a' }),\n        new URLSearchParams({ a: 'a', b: 'b' }),\n      ),\n    ).toBe(true)\n  })\n\n  it('should work with nested arrays (#2794)', () => {\n    const arr = [1, 2]\n    expect(shallow([arr, 1], [arr, 1])).toBe(true)\n  })\n\n  it('should work with undefined (#3204)', () => {\n    expect(shallow({ a: undefined }, { b: 1 })).toBe(false)\n  })\n})\n\ndescribe('mixed cases', () => {\n  const obj = { 0: 'foo', 1: 'bar' }\n  const arr = ['foo', 'bar']\n  const set = new Set(['foo', 'bar'])\n  const map = new Map([\n    [0, 'foo'],\n    [1, 'bar'],\n  ])\n\n  it('compares different data structures', () => {\n    expect(shallow<unknown>(obj, arr)).toBe(false)\n    expect(shallow<unknown>(obj, set)).toBe(false)\n    expect(shallow<unknown>(obj, map)).toBe(false)\n    expect(shallow<unknown>(arr, set)).toBe(false)\n    expect(shallow<unknown>(arr, map)).toBe(false)\n    expect(shallow<unknown>(set, map)).toBe(false)\n  })\n})\n\ndescribe('generators', () => {\n  it('pure iterable', () => {\n    function* gen() {\n      yield 1\n      yield 2\n    }\n    expect(Symbol.iterator in gen()).toBe(true)\n    expect(shallow(gen(), gen())).toBe(true)\n  })\n\n  it('pure iterable with different values returns false', () => {\n    const iterableA = {\n      [Symbol.iterator]: function* (): Generator<number> {\n        yield 1\n        yield 2\n      },\n    }\n\n    const iterableB = {\n      [Symbol.iterator]: function* (): Generator<number> {\n        yield 1\n        yield 3\n      },\n    }\n\n    expect(shallow(iterableA, iterableB)).toBe(false)\n  })\n})\n\ndescribe('unsupported cases', () => {\n  it('date', () => {\n    expect(\n      shallow(\n        new Date('2022-07-19T00:00:00.000Z'),\n        new Date('2022-07-20T00:00:00.000Z'),\n      ),\n    ).not.toBe(false)\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/subscribe.test.tsx",
    "content": "import { describe, expect, it, vi } from 'vitest'\nimport { subscribeWithSelector } from 'zustand/middleware'\nimport { createStore } from 'zustand/vanilla'\n\ndescribe('subscribe()', () => {\n  it('should not be called if new state identity is the same', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, subscribe } = createStore(() => initialState)\n\n    subscribe(spy)\n    setState(initialState)\n    expect(spy).not.toHaveBeenCalled()\n  })\n\n  it('should be called if new state identity is different', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, getState, subscribe } = createStore(() => initialState)\n\n    subscribe(spy)\n    setState({ ...getState() })\n    expect(spy).toHaveBeenCalledWith(initialState, initialState)\n  })\n\n  it('should not be called when state slice is the same', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, subscribe } = createStore(\n      subscribeWithSelector(() => initialState),\n    )\n\n    subscribe((s) => s.value, spy)\n    setState({ other: 'b' })\n    expect(spy).not.toHaveBeenCalled()\n  })\n\n  it('should be called when state slice changes', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, subscribe } = createStore(\n      subscribeWithSelector(() => initialState),\n    )\n\n    subscribe((s) => s.value, spy)\n    setState({ value: initialState.value + 1 })\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith(initialState.value + 1, initialState.value)\n  })\n\n  it('should not be called when equality checker returns true', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, subscribe } = createStore(\n      subscribeWithSelector(() => initialState),\n    )\n\n    subscribe((s) => s, spy, { equalityFn: () => true })\n    setState({ value: initialState.value + 2 })\n    expect(spy).not.toHaveBeenCalled()\n  })\n\n  it('should be called when equality checker returns false', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, subscribe } = createStore(\n      subscribeWithSelector(() => initialState),\n    )\n\n    subscribe((s) => s.value, spy, { equalityFn: () => false })\n    setState({ value: initialState.value + 2 })\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith(initialState.value + 2, initialState.value)\n  })\n\n  it('should unsubscribe correctly', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { setState, subscribe } = createStore(\n      subscribeWithSelector(() => initialState),\n    )\n\n    const unsub = subscribe((s) => s.value, spy)\n\n    setState({ value: initialState.value + 1 })\n    unsub()\n    setState({ value: initialState.value + 2 })\n\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith(initialState.value + 1, initialState.value)\n  })\n\n  it('should keep consistent behavior with equality check', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1, other: 'a' }\n    const { getState, setState, subscribe } = createStore(\n      subscribeWithSelector(() => initialState),\n    )\n\n    const isRoughEqual = (x: number, y: number) => Math.abs(x - y) < 1\n    setState({ value: 0 })\n    spy.mockReset()\n    const spy2 = vi.fn()\n    let prevValue = getState().value\n    const unsub = subscribe((s) => {\n      if (isRoughEqual(prevValue, s.value)) {\n        // skip assuming values are equal\n        return\n      }\n      spy(s.value, prevValue)\n      prevValue = s.value\n    })\n    const unsub2 = subscribe((s) => s.value, spy2, { equalityFn: isRoughEqual })\n    setState({ value: 0.5 })\n    setState({ value: 1 })\n    unsub()\n    unsub2()\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith(1, 0)\n    expect(spy2).toHaveBeenCalledTimes(1)\n    expect(spy2).toHaveBeenCalledWith(1, 0)\n  })\n\n  it('should call listener immediately when fireImmediately is true', () => {\n    const spy = vi.fn()\n    const initialState = { value: 1 }\n    const { subscribe } = createStore(subscribeWithSelector(() => initialState))\n\n    subscribe((s) => s.value, spy, { fireImmediately: true })\n\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith(1, 1)\n  })\n})\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"strict\": true,\n    \"jsx\": \"react-jsx\",\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"skipLibCheck\": true /* FIXME remove this once redux-devtools/extension fixes it */,\n    \"allowImportingTsExtensions\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"verbatimModuleSyntax\": true,\n    \"declaration\": true,\n    \"isolatedDeclarations\": true,\n    \"types\": [\"@testing-library/jest-dom/vitest\"],\n    \"noEmit\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"zustand\": [\"./src/index.ts\"],\n      \"zustand/*\": [\"./src/*.ts\"]\n    }\n  },\n  \"include\": [\"src/**/*\", \"tests/**/*\"],\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "vitest.config.mts",
    "content": "import { resolve } from 'path'\nimport { defineConfig } from 'vitest/config'\n\nexport default defineConfig({\n  resolve: {\n    alias: [\n      { find: /^zustand$/, replacement: resolve('./src/index.ts') },\n      { find: /^zustand(.*)$/, replacement: resolve('./src/$1.ts') },\n    ],\n  },\n  test: {\n    name: 'zustand',\n    // Keeping globals to true triggers React Testing Library's auto cleanup\n    // https://vitest.dev/guide/migration.html\n    globals: true,\n    environment: 'jsdom',\n    dir: 'tests',\n    reporters: process.env.GITHUB_ACTIONS\n      ? ['default', 'github-actions']\n      : ['default'],\n    setupFiles: ['tests/setup.ts'],\n    coverage: {\n      include: ['src/**/'],\n      reporter: ['text', 'json', 'html', 'text-summary'],\n      reportsDirectory: './coverage/',\n      provider: 'v8',\n    },\n  },\n})\n"
  }
]