@z0devs/* is a composable Module Federation toolkit for React shells that load
trusted, curated plugins at runtime — micro-frontends authored by your own teams or by vetted
partners, onboarded through a host-controlled manifest.
Module Federation itself solves one thing extremely well: loading code from another build at runtime and sharing dependencies between the two. Everything above that — how a plugin announces its routes, how the host hands it a session, how you stop an incompatible plugin from booting, how a plugin runs on a laptop with no host at all — is left to you. Every team rebuilds that layer, and every team's version is subtly different.
This library is that layer, extracted, typed and tested.
Before you start: two things that will save you an hour
The packages are on a private registry. @z0devs/* is not on the public npm registry — you need
an access token and an .npmrc entry before any install or pnpm create command on this site will
work. Set that up first: Installation.
This is a client-side runtime — no SSR, no React Server Components, no Next.js App Router.
Remotes are fetched and initialized in the browser, and the shared store is a client React context.
Render the shell as a normal SPA (Vite, Rspack, Webpack, CRA). A Next.js app can host it only inside
a "use client" island that is excluded from prerendering.
Which half are you building?#
The two roles use almost disjoint parts of the library, and most confusion comes from reading the other one's page. Pick yours:
A remote that a host loads. You install one package, export an init, and can develop against a
stub store with no shell running.
The host that owns the manifest, defines the shared store, and mounts PluginRouteLoader. You
decide what plugins are allowed to see.
The Quickstart walks both, in order: steps 1–4 are the plugin, step 5 is the host.
Words used on this site#
These get used interchangeably in the wild, which makes federation docs harder to read than they need to be. Here they mean one thing each:
| Term | Means |
|---|---|
| Shell / host | The app that owns the manifest, defines the shared store and mounts PluginRouteLoader. One per deployment. Used interchangeably — "shell" in prose, shell in code. |
| Plugin | A remote built against this library: it exposes ./init and registers contributions. Every plugin is a remote; not every remote is a plugin. |
| Remote | Module Federation's term — any separate build exposing modules. Used when the point is the federation mechanics rather than the plugin contract. |
| Micro-frontend / MFE | The architectural pattern. Appears in type names (MFEManifestEntry) and prose, never as a distinct runtime concept. |
| Contribution | An item a plugin appends to a registry — a route, nav item, settings panel, toolbar slot, component or onboarding step. |
| Shared store | The defineSharedStore result: the values and registries a host publishes to its plugins. |
The mental model#
A plugin is a separate build at its own URL
Your plugin is an ordinary Module Federation remote: its own repo, its own bundler config, its own
deploy. It exposes an ./init module plus whatever pages it wants the host to render.
The host owns a manifest
The shell fetches a list of MFEManifestEntry records — name, remote entry URL, version, required
shell API range, vendor, permissions. That list is the curation boundary: nothing loads unless the
host put it there.
The host boots each plugin once
PluginRouteLoader validates every entry, drops the ones that don't fit, mounts each surviving
plugin's init component, and holds the app loader until they all report ready.
The plugin registers what it contributes
Inside init, the plugin adds routes, nav items, settings panels, toolbar slots and onboarding
steps through namespaced registries it receives as props — and removes them on cleanup.
Both sides share one typed store
The host declares a shared store (defineSharedStore); the plugin binds to it
(createPluginStoreBridge). One federation singleton, one contract, inference on both sides.
What you get#
Manifest validation, version gating, inter-plugin dependencies, retry policy, integrity checks, lifecycle telemetry.
Dynamic routingRemotes register React Router routes at runtime through a registry; the router rebuilds itself.
A typed host↔plugin contractdefineSharedStore + createPluginStoreBridge, with a slice type so a plugin's stub can't drift
from the real store.
Run a plugin with no host at all, against a fail-loud stub that throws on anything you didn't declare.
Who it is for#
- Platform teams building a shell that many product teams extend independently — each with their own release cadence.
- Products with a partner ecosystem where vetted third parties ship UI into your app through a reviewed manifest entry.
- Large single apps being split into independently deployable slices without splitting the UX.
Who it is not for#
Trusted plugins only
Plugins run in-process — same React tree, same origin, same window, same session. The library
gives you curation, version gating and supply-chain tamper detection, not a sandbox. If you need
to run code you would not run as first-party, you need an out-of-realm boundary (iframe or worker
with postMessage), which this library deliberately does not provide. See
Trust model.
It is also React-only by design. The federation runtime underneath is bundler-agnostic (Rspack, Webpack 5, Vite), but the host and plugin APIs are React components and hooks.
Where to go next#
The parts of a plugin host that Module Federation doesn't model, and how this fills them in.
QuickstartA running plugin and a host that loads it, from zero.
ArchitectureLayer model, boot sequence, and the seams that keep the library swappable.
Package referenceTen packages, what each one owns, and which ones you actually need.