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)

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

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). Angular exposes the same two mechanisms as directives instead of components/hooks: *portal/*tunnelIn and <tunnel-out>. Svelte exposes only the second mechanism — createTunnel, as TunnelIn/ TunnelOut components taking an explicit tunnel prop — it has no same-surface portal primitive at all.