Orbz in microfrontends
A web component is a useful microfrontend boundary because its runtime contract belongs to the browser rather than to one framework. Orbz can be rendered by a Vue remote, updated by an Angular shell, and replaced later by a React surface without changing the tag or its attributes.
Orbz is intentionally a leaf visual component, not a routing system or a
complete microfrontend platform. Use your existing composition strategy for
navigation, data, identity, and deployment. Use orb-z as the shared assistant
presence at the edge of those systems.
The shared contract
The most durable integration is platform-level markup:
<orb-z
state="idle"
size="18rem"
preset="neongate"
reduced-motion="system"
></orb-z>The shell or remote updates public properties when domain state changes:
import type { OrbzElement, OrbzState } from "@neongate-ai/orbz";
export function renderAssistantState(state: OrbzState) {
const orb = document.querySelector<OrbzElement>("orb-z");
if (orb) orb.state = state;
}No framework object crosses the boundary. The contract is strings, numbers, boolean attributes, and three methods.
Choose who registers the element
There are three practical models.
1. The shell registers Orbz
The application shell imports the browser entry once:
import "@neongate-ai/orbz/browser";Remotes only render <orb-z>. This minimizes duplicate package code and gives
the shell explicit ownership of the version. It is the clearest default when
the shell already owns global design primitives.
2. Each remote imports the same pinned version
Every remote can depend on and import Orbz independently. Registration is
idempotent, so later imports see the existing orb-z definition instead of
defining it again.
Coordinate an exact package version across deployments. The Custom Elements registry cannot replace an already-defined tag: the first version registered on the page wins. Version skew can therefore make behavior depend on remote load order even though registration itself does not throw.
3. Share Orbz through your bundler or import map
Module Federation, an import map, or another runtime sharing mechanism can provide one package instance. This can reduce duplicate bytes, but it is an optimization—not a requirement of Orbz. Keep the version rule explicit even when tooling marks the package as a singleton.
Keep ownership clear
Choose one owner for each responsibility:
| Concern | Recommended owner |
|---|---|
Registering orb-z | Shell, or a coordinated shared dependency |
| Voice session and model calls | Assistant feature or platform service |
| Mapping domain state to Orbz state | Feature that owns the session |
| Orb size and preset | Surface that owns layout and brand context |
| Status text and controls | The rendering remote |
| Orbz animation and internal styles | Orbz itself |
Avoid having multiple remotes write to the same element. A shared service can publish assistant domain state, but the remote that renders the orb should be the only code that maps and applies that state.
Communicate domain state, not DOM instructions
A weak contract publishes commands such as “set the orb to purple” or “pause layer three.” That leaks presentation into the platform bus.
A stronger contract publishes meaningful application state:
type AssistantStatus =
| { phase: "ready" }
| { phase: "capturing" }
| { phase: "processing" }
| { phase: "playing" }
| { phase: "offline" }
| { phase: "error"; message: string };The rendering remote maps capturing to listening, processing to
thinking, and so on. Errors remain real text and actions rather than becoming
an undocumented animation.
Independent deployments
Repository structure and deployment structure are separate choices. A shared repository may make local orchestration convenient, while each example or microfrontend remains an independent Vercel project. Conversely, unrelated repositories can consume the same npm version and render an identical orb.
For an independently deployed surface:
- add
@neongate-ai/orbzto that application’s own dependencies; - pin or centrally govern the allowed version;
- import
/browserin a browser entry, or/reactin a React Client Component; - expose assistant state through the application’s existing data contract;
- deploy without importing Orbz source or another example application.
The live Vanilla , React , Vue , Svelte , Angular , and Next.js showcases demonstrate six independent hosts using the same component surface.
Failure and fallback behavior
Before registration, an unknown custom element remains valid inert HTML. This is a useful progressive boundary: surrounding labels and controls can still render while the component bundle loads.
Design the host so that the assistant is understandable without the animation:
<div role="status" aria-live="polite">
<orb-z state="thinking"></orb-z>
<span>Assistant is preparing a response</span>
</div>If a remote fails to load, the text still communicates state. If Orbz registers later, the element upgrades in place.
Microfrontend checklist
- Use the npm package as the boundary; do not import
src/from another app. - Coordinate one exact Orbz version across concurrently loaded remotes.
- Register in one known place when practical.
- Keep one writer for each rendered orb.
- Publish domain state over the application bus, not internal visual details.
- Keep status text and controls outside the closed Shadow DOM.
- Test host upgrades with reduced motion and with JavaScript delayed.
- Treat the tag name and documented attributes as the stable integration API.