Skip to content

How to: share content across surfaces

You want to render content somewhere other than its own JSX/template position — a toast, an overlay — and need to know which mechanism applies.

Same surface: createPortal (React) / Teleport (Vue) / *portal (Angular) / Portal (Svelte, Solid)

Section titled “Same surface: createPortal (React) / Teleport (Vue) / *portal (Angular) / Portal (Svelte, Solid)”

Both move content to an already-mounted node within the same mounted surface as the call site:

// React
const [overlay, setOverlay] = useState<IHostInstance | null>(null);
// ...
<view ref={setOverlay} />
{overlay ? createPortal(<text>ported in</text>, overlay) : null}

Use a state/ref callback (useState, not useRef, on React) so the target resolves once it actually commits — a plain ref is null on the entire first render.

For content that must reach a genuinely different, separately-mounted surface, use createTunnel() instead — a shared store, not a node reference:

// React — module-level singleton, importable from any surface
export const overlayTunnel = createTunnel();
// wherever it should paint:
<overlayTunnel.Out />
// wherever the content originates, any surface:
<overlayTunnel.In><ToastCard /></overlayTunnel.In>

In/Out are components, not hooks/composables — an earlier hook-based React version caused a genuine infinite render loop (the shared store’s notify() re-rendering the same component that also called the write side). As separate components, Out’s forced re-render never bounces back into In, even when they’re siblings.

  • Overlay host lives in the same tree you’re already rendering → createPortal / Teleport.
  • Content needs to reach a different mount() root entirely (split-screen, an always-on-top system surface) → createTunnel.

Import both from your adapter’s package (@symbiote-native/react, @symbiote-native/vue, @symbiote-native/angular, @symbiote-native/svelte, @symbiote-native/solid). Angular exposes the same two mechanisms as directives instead of components/hooks: *portal/*tunnelIn and <tunnel-out>. Solid has both mechanisms too, spelled <Portal mount={...}> and createTunnel()’s In/Out; same-surface portal now ships on every adapter except Svelte, which exposes only the second mechanism, createTunnel, as TunnelIn/TunnelOut components taking an explicit tunnel prop.