Skip to content

Namespaces for micro-frontends

A BroadcastChannel is global to the origin, so a bus name is an identity. Two independently deployed apps that both call createSharedStore('cart', …) — or both omit the name and land on the default — are not two carts. They are one cart, with two teams writing to it.

Nothing warns about this, and nothing can: from the library’s side it is indistinguishable from the case it exists to serve, two tabs sharing state.

import { createNamespace } from "use-everywhere";
export const checkout = createNamespace("checkout");

Every name from that namespace is prefixed, so checkout.useSharedState('items', []) lives on the bus checkout:use-everywhere and can’t be reached by anything that didn’t ask for the checkout namespace by name.

Call it at module scope, once per app, and export it:

checkout/bus.ts
export const checkout = createNamespace("checkout");
// checkout/Cart.tsx
import { checkout } from "./bus";
function Cart() {
const [items, setItems] = checkout.useSharedState("items", []);
const peers = checkout.usePeers();
// …
}

It carries the full surface, not a reduced one — useSharedState, usePeers, useClientId, useLeader, useIsLeader, useLeaderEffect, createStoreHooks, defineChannel, getSharedStore, plus the core factories (createSharedStore, createChannel, createPresence, createLeader) for code outside React.

Options keep their meanings. A store or name you pass is a name within the namespace:

checkout.useSharedState("theme", "dark", { store: "settings" });
// bus "checkout:settings"

Three different words that all sound like “scope”, kept deliberately distinct:

Question it answersExample
NamespaceWhose bus is this?createNamespace('checkout')
Share scopeHow far does this value travel?{ scope: 'tabs' }
wire.scopeWhich engine does this wire belong to?'state', 'presence'

They compose without interacting:

checkout.useSharedState("draft", "", { scope: "tab" });
// ^ the checkout app's bus ^ but never leaves this tab

It is not a security boundary. Everything here is same-origin, and a namespace is a string — anything on the page can construct the same one deliberately. It prevents collision, not access. For the cross-origin case, see the security model.

It does not isolate you from version skew. Two deploys of the same namespace still meet, which is what you want — and if they speak different wire protocols they partition loudly. See Version skew & the wire contract.

busName() gives the real name, which is what devtools, observeBus and getTransportKind want:

observeBus(checkout.busName("settings"), (event) => console.log(event));
// listens on "checkout:settings"