{"id":18025,"library":"zustand-querystring","title":"Zustand URL Query String Sync","description":"zustand-querystring is a middleware for Zustand that provides declarative synchronization between parts of a Zustand store's state and the URL's query string. This enables persistent and shareable application states directly embedded in the browser's URL. The current stable version is 0.7.0, with ongoing development evidenced by recent frequent releases that include bug fixes and new features. Key differentiators include flexible state selection via the `select` option, support for multiple serialization formats (e.g., `plain`, `readable`, or custom), granular control over how `null` and `undefined` values are handled (`syncNull`, `syncUndefined`), and a powerful bidirectional `map` option for complex transformations between store state and URL representation. It supports both individual query parameters for each state key (`key: false`) or consolidating the entire state into a single query parameter, and offers prefixing for managing multiple stores on the same page.","status":"active","version":"0.7.0","language":"javascript","source_language":"en","source_url":"https://github.com/nitedani/zustand-querystring","tags":["javascript","zustand","querystring","typescript"],"install":[{"cmd":"npm install zustand-querystring","lang":"bash","label":"npm"},{"cmd":"yarn add zustand-querystring","lang":"bash","label":"yarn"},{"cmd":"pnpm add zustand-querystring","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core state management library for which this package serves as middleware.","package":"zustand","optional":false}],"imports":[{"note":"The primary middleware export. For ESM-only projects, CommonJS `require` syntax will fail.","wrong":"const { querystring } = require('zustand-querystring');","symbol":"querystring","correct":"import { querystring } from 'zustand-querystring';"},{"note":"One of the provided serialization formats, enabling cleaner, human-readable URLs. It must be imported from its specific path.","wrong":"import { plain } from 'zustand-querystring';","symbol":"plain","correct":"import { plain } from 'zustand-querystring/format/plain';"},{"note":"An older human-readable format (pre-v0.5.0), often imported as a namespace due to its structure. The `plain` format is generally preferred for new implementations.","wrong":"import { readable } from 'zustand-querystring/format/readable';","symbol":"readable","correct":"import * as format from 'zustand-querystring/format/readable';"}],"quickstart":{"code":"import { create } from 'zustand';\nimport { querystring } from 'zustand-querystring';\n\ninterface MyStoreState {\n  search: string;\n  page: number;\n  setSearch: (search: string) => void;\n  setPage: (page: number) => void;\n}\n\nconst useStore = create<MyStoreState>()(\n  querystring(\n    set => ({\n      search: '',\n      page: 1,\n      setSearch: search => set({ search }),\n      setPage: page => set({ page }),\n    }),\n    {\n      // Select which parts of the state to synchronize with the URL\n      select: () => ({ search: true, page: true }),\n      // Use individual query parameters like '?search=hello&page=2' (default)\n      key: false\n    },\n  ),\n);\n\n// To demonstrate usage, you can update the state, which will reflect in the URL\n// For example, calling this in a component:\n// useStore.getState().setSearch('example-query');\n// useStore.getState().setPage(5);\n\n// This will update the URL to something like: ?search=example-query&page=5","lang":"typescript","description":"Initializes a Zustand store with the querystring middleware, syncing `search` and `page` state to URL parameters."},"warnings":[{"fix":"Re-evaluate how existing URLs are generated and parsed, potentially requiring a one-time migration or handling of legacy URLs if backward compatibility is critical. Ensure all new URLs are encoded with `encodeURIComponent`.","message":"Version 0.1.0 introduced a breaking change by switching from a proprietary URL encoding format to standard `encodeURIComponent`/`decodeURIComponent`. Existing URLs encoded with pre-0.1.0 versions will no longer be correctly parsed.","severity":"breaking","affected_versions":"<0.1.0"},{"fix":"Update existing 'readable' format URLs or migrate to the newer 'plain' format introduced in v0.5.0, which offers a cleaner, hand-editable URL structure and is generally recommended.","message":"Version 0.4.0 introduced breaking changes to the encoding scheme of the 'readable' format. If you were using the readable format prior to 0.4.0, URLs will no longer be correctly parsed.","severity":"breaking","affected_versions":"<0.4.0"},{"fix":"If you need `null` or `undefined` to be explicitly synchronized to the URL (e.g., as part of a filter state), set the `syncNull: true` or `syncUndefined: true` options in the middleware configuration.","message":"By default, `null` and `undefined` values are not synced to the URL. Setting a state property to `null` or `undefined` will effectively clear it from the URL and reset it to its initial state value in the store.","severity":"gotcha","affected_versions":">=0.3.1"},{"fix":"Upgrade to version 0.6.0 or higher. If upgrading is not immediately possible, consider custom mapping or alternative serialization for array types to ensure correct parsing.","message":"Prior to v0.6.0, comma-separated arrays in the 'plain' format with dynamic keys were incorrectly restored as a single string instead of an array. For example, `filters.gpus_.architecture=Blackwell,Ada` would parse as `\"Blackwell,Ada\"`.","severity":"gotcha","affected_versions":"<0.6.0"},{"fix":"Decide on a consistent `key` strategy early in development. If you must change it on a live application, implement a migration strategy (e.g., redirecting old URLs, providing a backward-compatible parser) to prevent broken links.","message":"Changing the `key` option from `false` (default, individual query parameters) to a string value (e.g., `key: 'state'`, which consolidates all state into one parameter) will fundamentally change your URL structure. Existing URLs will become incompatible.","severity":"breaking","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Upgrade `zustand-querystring` to version 0.6.0 or higher, which includes a fix for this parsing issue.","cause":"A bug in the 'plain' format parser for dynamic keys where comma-separated values were not correctly split into arrays.","error":"Comma-separated arrays not restored from URL (e.g. `filters.gpus_.architecture=Blackwell,Ada` parsed as string)"},{"fix":"This issue was fixed in version 0.3.1. Ensure you are using `zustand-querystring` version 0.3.1 or later.","cause":"An issue where URL parameters were encoded twice, leading to incorrect parsing or excessively long URLs.","error":"Query parameters are double-encoded in URL (e.g., `?state=%2522hello%2522` instead of `?state=%22hello%22`)"},{"fix":"Verify that your `select` function correctly returns `true` for all state fields you intend to sync. Also, check the `key` option: `key: false` uses individual parameters, while `key: 'state'` (or any string) consolidates state into a single parameter named 'state' (or your chosen string).","cause":"Commonly caused by incorrect configuration of the `select` option (not selecting the desired state fields) or a misconfigured `key` option.","error":"Zustand state not showing up in the URL query string, or not restoring correctly from URL"},{"fix":"To synchronize `null` or `undefined` values, set `syncNull: true` or `syncUndefined: true` (or both) in the middleware options: `{ syncNull: true, syncUndefined: true }`.","cause":"By default, `zustand-querystring` does not synchronize `null` or `undefined` values to the URL; it treats them as 'clear' operations.","error":"Null or undefined values for state properties are not reflected in the URL and disappear after refresh"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}