Updated now: Share Zustand State via URL (Copyable Links)

/ 3 min read READ

Store Zustand State into URL

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:

# Zustand v5 (latest as of Jan 28, 2026)
pnpm add zustand@^5.0.10
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:

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!