---
title: "Updated now: Share Zustand State via URL (Copyable Links)"
description: "Updated for Zustand v5: persist state in the URL hash so users can copy, share, and restore app state across sessions."
date: "2026-01-28"
updated: "2026-01-28"
oldPath: "01-zustand-url-state"
tags:
  - docs
  - react
  - zustand
  - tips
  - zustand url state
  - zustand query params
  - zustand url
  - zustand search params
  - zustand getstorage
  - zustand persist state
  - zustand slice
  - zustand persist map
  - zustand store persist
  - zustand remove item from array
  - zustand storage
  - zustand dynamic store
  - url state management
  - zustand function return value
  - zustand localstorage
  - zustand persistance
  - zustand persist storage
  - zustand store
  - zustand set state
  - how to use zustand
  - Zustand URL state management
  - Persist Zustand state in URL
  - Share Zustand state with URL hash
  - Zustand state storage in URL
  - Zustand URL hash state
  - Persist React state with Zustand
  - Zustand state in query params
  - URL-based state management with Zustand
  - Zustand state sharing via URL
  - Store React state in URL with Zustand
  - Zustand state persistence
  - Zustand dynamic URL state
  - Manage Zustand state in URL
  - Zustand hash-based state
  - URL state sharing with Zustand
  - Zustand state URL encoding
  - Zustand URL synchronization
  - Zustand URL state persistence
  - React Zustand URL storage
  - Zustand state in URL hash
faqs:
  - question: "Why store Zustand state in the URL?"
    answer: "It makes state shareable and reproducible. Users can copy a link and you can debug issues by opening the exact same state snapshot."
  - question: "Should I use the query string or the hash?"
    answer: "Hash-based state is often simpler because it doesn’t hit the server and avoids routing conflicts. Query params can work too, but you need to manage parsing, encoding, and URL length more carefully."
  - question: "How do I keep URLs from getting too large?"
    answer: "Only serialize what’s necessary, keep values compact, and avoid dumping entire objects. If the state is large, consider storing a short key in the URL and persisting the full state elsewhere."
  - question: "Is it safe to store state in the URL?"
    answer: "Never put secrets or PII in URLs. URLs can end up in logs, analytics, referrers, and screenshots."
  - question: "How do I handle versioning when state shape changes?"
    answer: "Add a small version field to the encoded payload and write a migration step so old links still work after updates."

ogImage: "/images/zustand-og.png"
---

---

[Image: Store Zustand State into URL](https://saybackend.com/blog/2023-dec-zustand-url-state-sharing/)
## Introduction

**Updated now (Jan 28, 2026):** refreshed for Zustand v5 syntax and the current
URL-hash storage recommendation.

In this tutorial, we'll store Zustand state in the URL hash so your app becomes instantly shareable. Zustand keeps state simple, and the URL hash lets users copy a link, reload the page, or open a new tab and land in the exact same state.

## Code Setup

Let's start by setting up the initial code for our example application. We have a simple counter state using Zustand:

```bash
# Zustand v5 (latest as of Jan 28, 2026)
pnpm add zustand@^5.0.10
```

```ts
import { create } from "zustand";

const useCounter = create()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));
```

## Implementing State Storage in the URL Hash

To achieve this, we need to add the HashStorage utilities:

```ts
import { create } from "zustand";
import { createJSONStorage, persist, StateStorage } from "zustand/middleware";

export const hashStorage: StateStorage = {
  getItem: (key): string => {
    const searchParams = new URLSearchParams(location.hash.slice(1));
    const value = searchParams.get(key) ?? "";
    return JSON.parse(value);
  },
  setItem: (key, newValue): void => {
    const searchParams = new URLSearchParams(location.hash.slice(1));
    searchParams.set(key, JSON.stringify(newValue));
    location.hash = searchParams.toString();
  },
  removeItem: (key): void => {
    const searchParams = new URLSearchParams(location.hash.slice(1));
    searchParams.delete(key);
    location.hash = searchParams.toString();
  },
};

export const useCounter = create()(
  persist(
    (set, get) => ({
      count: 0,
      increment: () => set({ count: get().count + 1 }),
      decrement: () => set({ count: get().count - 1 }),
    }),
    {
      name: "counter",
      storage: createJSONStorage(() => hashStorage),
    },
  ),
);
```

### Notes for 2026

- Zustand v5 prefers the `create()((set) => ...)` signature.
- Use `createJSONStorage` to keep your storage compliant with the `StateStorage`
  interface.
- Latest release listed on GitHub as of Jan 28, 2026: **v5.0.10 (Jan 12)**, which
  includes a `persist` edge‑case fix.

The `hashStorage` object keeps the persisted JSON state in the URL hash. That
makes the URL fully shareable and reload-safe. If your state is large, consider
persisting only a subset (via `partialize`) or switching to `localStorage`.

Now, when users interact with the useCounter store, the state will persist in the URL hash. For example, the URL might look like this:

```
https://saybackend.com#counter=eyJzdGF0ZSI6eyJxdXJhblRleHRFZGl0aW9uIjoiYXJh
```

This enables users to reload the page or share the URL while maintaining a consistent state across all instances.

## Conclusion

Implementing URL hash storage for Zustand allows you to create dynamic and shareable applications. By persisting the state in the URL, users can navigate across pages and even share the URL with others to see the same state. It enhances the user experience and provides a convenient way to maintain and communicate application state.

I hope this tutorial has been helpful in understanding how to store Zustand state into the URL as a hash. Happy coding!
