yeti logo icon
Close Icon
contact us
Yeti postage stamp
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

Managing Perisistent Browser Data with useSyncExternalStore

By
Jonny Schult
-
February 7, 2025
code on a computer

Seamlessly Sync Variables Across Tabs and Reloads

Ever found yourself repeatedly writing state management logic for localStorage or sessionStorage—only to discover it’s a bit more complicated than just calling getItem() or setItem() in React? You’re not alone! Tracking changes in these storages across multiple components or tabs can get messy quickly. Enter React’s useSyncExternalStore hook—a relatively new addition (as of React 18) that lets you build stable, consistent, and reactive custom storage hooks.

This blog post demonstrates how you can leverage useSyncExternalStore to manage data in both sessionStorage and localStorage with surprising ease. We’ll break down how it works, why it’s beneficial, and share a demo that showcases how you can persist React variables through tabs, sessions, and reloads.

TL;DR

  1. useSyncExternalStore is a hook that allows you to build custom subscriptions to external data sources—like local or session storage—and keep React states in sync only when these stores truly update.
  2. It’s perfect for building persistent reactive variables that live in the browser, saving you from the overhead of complex state management libraries.
  3. With minimal code, you can listen to storage events, retrieve the latest value, and trigger React re-renders whenever the stored data changes.
  4. Local Storage vs. Session Storage: Use localStorage for data that should persist across browser sessions and tabs, and sessionStorage for data that should reset when the tab or session ends.
  5. Demo: Follow along with our small Vite + React app to see these hooks in action!

Demo

We’ve built a small Vite + React app that illustrates exactly how these hooks work in practice. The project uses:

You can find the project here: Demo Repo Link

Feel free to clone the repo, run it locally, and see how each storage hook performs when switching tabs or refreshing pages. This hands-on experience will help solidify your understanding of how useSyncExternalStore can simplify state persistence in your React applications.

Anatomy of useSyncExternalStore

useSyncExternalStore is designed to integrate an external data source with React’s concurrency features. It guarantees React always has the latest snapshot of that data, plus a subscription mechanism for updates. The hook involves three major parts:

  1. subscribe: Defines how React should listen for changes in the external store. Often, you’ll attach an event listener (like storage) and return a cleanup function that removes it.
  2. getSnapshot: Returns the latest value from the store. React calls this to fetch current data during renders.
  3. Server Snapshot (optional): For server-side rendering scenarios, you can provide an additional snapshot function. If you’re client-only, you can ignore it or pass null.

Why Is useSyncExternalStore Useful?

Session Storage vs. Local Storage

Before diving into custom hooks, let’s clarify why you might want to store something in sessionStorage vs. localStorage:

  1. Local Storage
    • Persists data even after the browser or tab is closed.
    • Ideal for user preferences, theme modes, or data you want to keep until explicitly cleared.
  2. Session Storage
    • Data lives only as long as the tab or browser is open.
    • Perfect for short-lived, session-specific data—like ephemeral wizard steps or a selected account.

Why treat them similarly? In practice, reading and writing to each is nearly identical; you just switch window.localStorage for window.sessionStorage. That’s why you can build a single “storage pattern” and adapt it seamlessly for either type.

Custom Storage Hooks

Overview

We introduce a useBrowserStorage hook that consolidates all the logic for reading from and writing to web storage. Then we wrap it with two specialized hooks:

This abstraction allows you to manage both storage types with consistent logic, avoiding code duplication and ensuring reliable state synchronization across your application.

How useBrowserStorage Works

The useBrowserStorage hook serves as the foundation for managing data in either localStorage or sessionStorage. It can be found in the project at src/shared/hooks. Here’s how its key components function:

Cached Value

getSnapshot

subscribe

setValue

How useLocalStorage and useSessionStorage Build on useBrowserStorage

Both useLocalStorage and useSessionStorage are lightweight wrappers that utilize useBrowserStorage. Essentially, they invoke the same core hook—useBrowserStorage—but specify the storage type (localStorage or sessionStorage) to use. This separation of concerns allows you to create specialized hooks without duplicating the underlying logic.

Usage in the App

In our demo app:

This approach neatly separates long-term data from session-only data without duplicating the underlying storage logic, making your codebase cleaner and more maintainable.

Conclusion

Persisting React state variables across reloads and tabs doesn’t have to be a tangled mess of stateful code and useEffects. With useSyncExternalStore, you gain a robust, concurrency-safe tool to:

By centralizing storage logic in a versatile useBrowserStorage hook and creating specialized hooks like useLocalStorage and useSessionStorage, you streamline your state management approach. This not only enhances code readability and maintainability but also ensures a seamless user experience as data persists reliably across various user actions.

Ready to elevate your React state management? Clone, edit, and play around with our Demo Repo. See firsthand how useSyncExternalStore can simplify persisting state across tabs and reloads. Embrace this powerful hook to build more resilient and user-friendly applications!

Happy Coding!
If you have questions or want to share how you’re using these hooks, feel free to reach out or open an issue in the demo repository. useSyncExternalStore is still a relatively new concept in React, so there’s plenty of room for the community to discover even more amazing use cases.

Yeti designs and develops innovative digital products. If you have a project you'd like to get started on, we'd love to chat! Tell us a bit about what you're working on and we'll get back to you immediately!

Jonny is a software developer at Yeti. His technical career was borne of his interests in language, “truth,” and logic. When not coding or reading philosophy, he can be found blocking volleyballs with his face, DMing epic and frivolous DnD adventures, or watching the West Yorkshire Football Club, Leeds United, all from the Hoosier heartland of LaPorte Indiana.

You Might also like...

software developerReact Hooks 102: When to Avoid useEffect

Overusing useEffect in React can lead to inefficient components and performance issues. In this post, learn when to avoid useEffect and discover better alternatives for managing state, calculations, and user events. Optimize your React code with best practices for cleaner, faster applications.

software developer codingFintech Security with GraphQL Shield

Securing fintech applications is crucial, and GraphQL’s flexibility can be a security risk if not properly managed. GraphQL Shield helps enforce fine-grained authorization using role-based (RBAC) and attribute-based (ABAC) access control. In this post, we’ll explore why authorization matters, how to implement secure, composable rules, and walk through a demo app showcasing best practices.

Developer codingCross-Domain Product Analytics with PostHog

Struggling with cross-domain user tracking? Discover how PostHog simplifies product analytics across multiple platforms. Learn how to set up seamless cross-domain tracking, gain valuable marketing insights, and optimize user engagement with minimal effort. Read our step-by-step guide and example project to streamline your analytics today!

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started