Web Components
Source: docs/architecture/web-components.md
Web Components
Web Components are the boundary contract between the Korporus shell and federated apps. Each app registers custom HTML elements that the shell mounts into its layout slots.
The Three Slots
Every Korporus app can provide up to three slots:
| Slot | Tag Name Convention | Purpose |
|---|---|---|
| titlebar | {app-id}-titlebar |
Fixed header area (56px height) |
| main | {app-id}-main |
Primary content area (fills remaining space) |
| settings | {app-id}-settings |
Optional right sidebar (slides in from the shell chrome) |
The registerCustomElement Wrapper
The @korporus/web-component-wrapper package bridges React components to custom elements:
import { registerCustomElement } from "@korporus/web-component-wrapper";
import { MyMain } from "./components/MyMain";
registerCustomElement("my-app-main", MyMain);Under the hood this:
- Defines a custom
HTMLElementclass - On
connectedCallback, creates a React root and renders your component - On
disconnectedCallback, unmounts the React root - Converts kebab-case HTML attributes to camelCase props
- Uses light DOM by default (no Shadow DOM) for simpler styling
How the Shell Mounts Slots
The shell's AppView component creates custom elements imperatively:
const el = document.createElement("my-app-main");
containerRef.current.appendChild(el);This triggers the connectedCallback and your React component mounts. When the user navigates away, the element is removed, triggering disconnectedCallback and cleanup.
Shared State Across Slots
Since all three slot components live in the same JavaScript context (they're loaded from the same MF remote), they can share state via Zustand:
// store.ts — shared by all slots
export const useMyStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));When one slot updates the store, all slots re-render.