Skip to main content

Surfaces

Extensions can render on two surfaces. Each surface is an independent React app that calls Lumio.render() with a target parameter.

SurfaceTargetFileRendering contextInteractivity
Editor"editor"src/editor.tsxDashboard sidebarFull (all components)
Layer"layer"src/layer.tsxBrowser SourceRead-only (inputs disabled)

Shared state​

Both surfaces share useExtensionStorage(). When the editor writes a value, the layer receives it in real time via WebSocket (ext-storage:\{install_id\} channel). The layer reads storage but does not write it - see Shared Storage. For example:

  • A toggle in the editor immediately shows/hides an element in the layer
  • All surfaces always read the same storage values

Declaring targets​

List the surfaces you use in lumio.config.json:

{
"targets": ["editor", "layer"]
}

Only listed targets are bundled and deployed. editor and layer are the only targets.

Viewers​

Lumio has no viewer page. Viewers reach an extension only through chat commands and channel point rewards, which a bot module handles; the handler can push the result to the editor and the layer. See Viewer Input.

Valid combinations:

CombinationUse case
["editor"]Configuration-only panel with no visual output
["layer"]Display-only overlay with no settings UI
["editor", "layer"]Standard overlay with configuration panel (most common)

Render timeout​

Lumio.render() must be called within 10 seconds of page load. If the timeout expires, the extension is terminated and the user sees an error message. Avoid long async initialization before calling Lumio.render() - instead, render immediately with a loading state.

// Good: render immediately, load data inside the component
function Settings() {
const [title] = useExtensionStorage("title", "My Extension");
// returns the initial value on first render, then the stored value once loaded
return <CompactView title={title}>...</CompactView>;
}
Lumio.render(<Settings />, { target: "editor" });

// Bad: waiting for async work before rendering
async function init() {
await someAsyncSetup(); // may take too long!
Lumio.render(<Settings />, { target: "editor" });
}
init();

No direct DOM access​

Extensions must use only SDK components. The React reconciler serializes the component tree to a JSON intermediate representation and the Lumio host renders actual DOM elements in its iframe sandbox. Direct document.createElement() or document.getElementById() calls have no effect inside the sandbox.

Surface isolation​

Each surface is rendered in its own iframe with:

  • Separate JavaScript execution context
  • No shared memory between surfaces
  • Communication only through useExtensionStorage() and server functions
  • Content Security Policy enforced by the host

See Editor Panel, Layer, and Shared Storage for surface-specific documentation.