---
title: "Sharing state between unrelated React components"
description: "How to share serializable state between unrelated React components — including server and client components in Next.js — without prop drilling. The whole pattern is a plain object and a few lines of glue."
canonical: "https://asmyshlyaev177.dev/blog/sharing-state-between-unrelated-react-components"
published: "July 11, 2024"
tags: ["React", "Next.js", "State management"]
---

# Sharing state between unrelated React components

> How to share serializable state between unrelated React components — including server and client components in Next.js — without prop drilling. The whole pattern is a plain object and a few lines of glue.

---

Want to show how you can share any serializable data between React components, e.g. client components in NextJS.

We have few unrelated components:

![Example app UI](https://asmyshlyaev177.dev/_astro/8bv8lzxc4423tkizgcvv.D_CAqtTR.png)

Let's create an object that will contain initial state

```typescript
export const state: { count: number } = { count: 0 };
```

We can store data in a `WeakMap`, `state` will be a key to access it. Also, will need a `subscribers` array.

```typescript
const stateMap = new WeakMap<object, object>();
const subscribers: (() => void)[] = [];
```

Now let's write a hook to subscribe to data changes.

```typescript
export function useCommonState<T extends object>(stateObj: T) {
  // more efficient than `useEffect` since we don't have any deps
  React.useInsertionEffect(() => {
    const cb = () => {
      const val = stateMap.get(stateObj);
      _setState(val!);
    };
    // subscribe to events
    subscribers.push(cb);

    return () => {
      subscribers.slice(subscribers.indexOf(cb), 1);
    };
  }, []);
}
```

Now let's add logic related to get and set state

```typescript
// all instances of hook will point to same object reference
const [state, _setState] = React.useState<typeof stateObj>(() => {
  const val = stateMap.get(stateObj) as T;
  if (!val) {
    stateMap.set(stateObj, stateObj);
    return stateObj;
  }
  return val;
});

const setState = React.useCallback((newVal: object) => {
  // update value
  stateMap.set(stateObj, newVal);
  // notify all hook instances
  subscribers.forEach((sub) => sub());
}, []);

return { state, setState };
```

And now can use it in 3 components like

```typescript
import { state as myState } from './state';
//...

const { state, setState } = useCommonState(myState);

<button
  onClick={() => setState({ count: state.count + 1 })}
  className="p-2 border"
>
  +
</button>
// ...
Component A<div>Count: {state.count}</div>
```

![Final app](https://asmyshlyaev177.dev/_astro/ezs79hh5m9xhtbtb099s.D2JXziKX.gif)

You can see how it works here [https://stackblitz.com/\~/github.com/asmyshlyaev177/react-common-state-example](https://stackblitz.com/~/github.com/asmyshlyaev177/react-common-state-example)
Or here [https://codesandbox.io/p/github/asmyshlyaev177/react-common-state-example/main](https://codesandbox.io/p/github/asmyshlyaev177/react-common-state-example/main)
Or in github [https://github.com/asmyshlyaev177/react-common-state-example](https://github.com/asmyshlyaev177/react-common-state-example)

Check out my library for NextJS based on this principle [https://github.com/asmyshlyaev177/state-in-url](https://github.com/asmyshlyaev177/state-in-url)

Tnx for reading.

---

Author: Aleksandr Smyshliaev — <https://asmyshlyaev177.dev>
More posts: <https://asmyshlyaev177.dev/blog> · Site summary for LLMs: <https://asmyshlyaev177.dev/llms.txt>
