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.
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.We’ve built a small Vite + React app that illustrates exactly how these hooks work in practice. The project uses:
useLocalStorage
to store user theme information (light/dark mode).useSessionStorage
to handle short-lived, session-scoped data like a selected account type.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.
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:
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.getSnapshot
: Returns the latest value from the store. React calls this to fetch current data during renders.useSyncExternalStore
Useful?Before diving into custom hooks, let’s clarify why you might want to store something in sessionStorage vs. localStorage:
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.
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:
useLocalStorage
useSessionStorage
This abstraction allows you to manage both storage types with consistent logic, avoiding code duplication and ensuring reliable state synchronization across your application.
useBrowserStorage
WorksThe 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:
getSnapshot
function synchronously returns the current value from the browser storage. It’s essential for useSyncExternalStore
, allowing React to retrieve the freshest data during renders.getSnapshot
runs, the hook parses the stored JSON. If the parsed data differs from the cached ref, the ref is updated. This ensures the snapshot reflects the latest state of localStorage
or sessionStorage
.useSyncExternalStore
on how to listen for external changes. In this context, an “external change” occurs if another tab or a different hook instance updates the same storage key.storage
event listener to the window
. When a storage change event fires, the listener invokes a callback provided by React, signaling that an update has occurred. React then calls getSnapshot
to fetch the new data and re-renders components if necessary.setValue
function updates the browser storage and the internal cache. This enables components to write data back into localStorage
or sessionStorage
.setValue
is invoked, the hook: localStorage
or sessionStorage
).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.
In our demo app:
useLocalStorage
is utilized for persistent data such as theme preferences (via a useThemeMode
hook). This ensures that the user’s dark or light mode setting is remembered across browser sessions and tabs.useSessionStorage
manages data that only needs to exist for the duration of the current browser tab or session, such as a selected account type. This selection persists through page reloads but resets when the tab or session ends.This approach neatly separates long-term data from session-only data without duplicating the underlying storage logic, making your codebase cleaner and more maintainable.
Persisting React state variables across reloads and tabs doesn’t have to be a tangled mess of stateful code and useEffect
s
. 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.