Zustand adapter to share state between pages (content script, injected script, popup, devtools, etc..) and background in web extensions.
- Runtime contexts: window (injected script), popup, devtools, content script, background, options, sidepanel (planned)
- Browsers: Chrome, Firefox, Safari, Opera, Edge + others supported by webextension-polyfill
npm install -S @webext-pegasus/transport @webext-pegasus/store-zustand
- Create a store based on https://github.com/pmndrs/zustand.
- You can create a store either reactive way or vanilla.
- Wrap the store with
wrapStore
. Import the store from the background. - You should await for the store to connect to the background.
That's it! Now your store is available from everywhere.
store.ts
import { create } from 'zustand'
// or import { createStore } from 'zustand/vanilla'
import { wrapStore } from 'webext-zustand'
interface BearState {
bears: number
increase: (by: number) => void
}
export const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
}))
export const STORE_NAME = 'GlobalBearStore';
export function bearStoreReady = () => pegasusZustandStoreReady(STORE_NAME, useBearStore);
background.ts
import { initPegasusTransport } from '@webext-pegasus/transport/background';
import { initPegasusZustandStoreBackend } from '@webext-pegasus/store-zustand';
import {useBearStore as store, STORE_NAME} from './store';
initPegasusTransport();
initPegasusZustandStoreBackend(STORE_NAME, store);
// listen state changes
store.subscribe((state) => {
// console.log(state);
});
// dispatch
// store.getState().increase(2);
popup.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import {initPegasusTransport} from '@webext-pegasus/transport/popup';
import { useBearStore, bearStoreReady } from './store';
const Popup = () => {
const bears = useBearStore((state) => state.bears);
const increase = useBearStore((state) => state.increase);
return (
<div>
Popup
<div>
<span>Bears: {bears}</span>
<br />
<button onClick={() => increase(1)}>Increment +</button>
</div>
</div>
);
};
// Init surface specific APIs
initPegasusTransport();
bearStoreReady().then(() => {
createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<Popup />
</React.StrictMode>
);
});
content-script.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import {initPegasusTransport} from '@webext-pegasus/transport/content-script';
import { useBearStore, bearStoreReady } from './store';
const Content = () => {
const bears = useBearStore((state) => state.bears);
const increase = useBearStore((state) => state.increase);
return (
<div>
Content
<div>
<span>Bears: {bears}</span>
<br />
<button onClick={() => increase(1)}>Increment +</button>
</div>
</div>
);
};
// Init surface specific APIs
initPegasusTransport();
bearStoreReady().then(() => {
const root = document.createElement("div");
document.body.prepend(root);
createRoot(root).render(
<React.StrictMode>
<Content />
</React.StrictMode>
);
});
This library is based on the Sinan Bekar's implementation of the webext-zustand. However it adds support of the injected scripts & utilizes newer, simplified transport layer.