Web Component Slots
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.
Slot Contract
Every Korporus app provides these slots:
| Slot | Tag Name Convention | Purpose |
|---|---|---|
| menubar | {app-id}-menubar |
App-specific controls rendered in the shell's top menu bar |
| main | {app-id}-main |
Primary content area (fills remaining space) |
| settings (optional) | {app-id}-settings |
App-specific settings view mounted at /app/{app-id}/settings |
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 both 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.