API Reference
The virtual-frame package exposes a low-level class (VirtualFrame), a custom element (<virtual-frame>), and a cross-origin bridge entry point. Most applications use a framework wrapper (see the framework guides) and never touch these APIs directly — reach for them when you need fine-grained control over projection lifetime, roots, or transports.
VirtualFrame class
import { VirtualFrame } from "virtual-frame";The projection engine. Given a source <iframe> and a host element, it mirrors the iframe's live DOM into the host — with mutation tracking, event re-dispatch, CSS rewriting, and optional Shadow DOM isolation.
Constructor
new VirtualFrame(
iframe: HTMLIFrameElement,
host: HTMLElement,
options?: VirtualFrameOptions,
)Projection starts immediately; the constructor calls init() internally. Subscribe to the iframe's load event or await navigation separately if you need a "ready" signal.
| Parameter | Type | Description |
|---|---|---|
iframe | HTMLIFrameElement | Source iframe whose document will be projected |
host | HTMLElement | Container element that receives the projected DOM |
options | VirtualFrameOptions | See Options. Optional. |
Options
The VirtualFrameOptions type:
interface VirtualFrameOptions {
isolate?: "open" | "closed";
selector?: string;
streamingFps?: number | Record<string, number>;
}| Option | Type | Default | Description |
|---|---|---|---|
isolate | "open" | "closed" | undefined | Attach a Shadow DOM of the given mode to the host for CSS isolation. Omit to render into the host's light DOM. See Shadow DOM. |
selector | string | undefined | CSS selector to project only a matching subtree of the iframe document. See Selector Projection. |
streamingFps | number | Record<string, number> | undefined | Frames-per-second for canvas/video streams. undefined means smooth per-frame (rAF) rendering same-origin; cross-origin falls back to ~5 FPS (set an explicit number for higher). A { selector: fps } map allows per-element rates. See Streaming FPS. |
Custom element only
The <virtual-frame> element also accepts a proxy attribute (same-origin fetch/XHR rewrite prefix). This is not part of VirtualFrameOptions — it's applied to the generated env shim before the iframe loads. See <virtual-frame> below and Cross-Origin.
Properties
Read-only after construction.
| Property | Type | Description |
|---|---|---|
iframe | HTMLIFrameElement | The source iframe passed to the constructor. |
host | HTMLElement | The host element receiving projected content. |
isolate | "open" | "closed" | undefined | Shadow DOM mode in use. |
selector | string | null | CSS selector, normalized to null when omitted. |
streamingFps | number | Record<string, number> | undefined | The FPS configuration in effect. |
shadowRoot | ShadowRoot | null | Open shadow root, when isolate: "open". For closed mode use getShadowRoot(). |
isInitialized | boolean | true after init() has completed at least once. |
Methods
destroy()
destroy(): voidStop projecting and release resources: detach the MutationObserver, remove event/message listeners, stop canvas/video capture streams, delete injected FontFace entries, and clear the host subtree. Safe to call multiple times. After destroy() the instance can be revived with refresh().
refresh()
refresh(): voidEquivalent to destroy(); init(). Use when the source iframe has changed in a way the mutation observer can't see (e.g., you replaced the iframe's src programmatically and want a clean re-projection without recreating the VirtualFrame instance).
getShadowRoot()
getShadowRoot(): ShadowRoot | nullReturns the shadow root attached to the host, regardless of "open" or "closed" mode. Use this when isolate: "closed" prevents access via host.shadowRoot. Returns null if isolate was not set.
init() internal
Called automatically by the constructor and by refresh(). You typically don't need to invoke this directly; it is documented for completeness.
<virtual-frame> custom element
import "virtual-frame/element";Declarative wrapper around VirtualFrame. Each element manages its own hidden <iframe> (shared between sibling elements pointing at the same src to avoid duplicate loads) and projects into its own subtree.
Attributes
HTML attributes use kebab-case and always stringify. The element maps them to camelCase options at setup time.
| Attribute | Maps to | Description |
|---|---|---|
src | — | URL of the remote document, or #id to reference an existing <iframe> / element in-page. |
isolate | isolate | "open" or "closed". |
selector | selector | CSS selector to project a subtree. |
streaming-fps | streamingFps | Either a number (e.g. streaming-fps="30") or a JSON object (e.g. streaming-fps='{"canvas":30,"video":60}'). |
proxy | — (env shim) | Same-origin proxy prefix for fetch/XHR rewriting. See Cross-Origin. |
Any attribute change on a connected element triggers a teardown + re-setup on the next microtask. To swap the source without restarting projection, prefer calling refresh() on the underlying VirtualFrame.
Methods
refresh()
element.refresh(): voidEquivalent to the VirtualFrame method of the same name — force a full re-projection.
Bridge entry point
// Served from the remote origin before any framework code
import "virtual-frame/bridge";The bridge is a small script that runs inside the projected document when it lives on a different origin than the host. It serialises DOM, CSS, events, and input back to the host via postMessage. See Cross-Origin for the end-to-end flow and the message protocol.
Types
VirtualFrameOptions
export interface VirtualFrameOptions {
isolate?: "open" | "closed";
selector?: string;
streamingFps?: number | Record<string, number>;
}See Options.
EnvShimOptions
export interface EnvShimOptions {
/**
* Same-origin proxy prefix. When set, the fetch/XHR shim rewrites
* host-origin requests to `location.origin + proxyBase + pathname`,
* keeping traffic same-origin and avoiding CORS.
*
* The host server must proxy `proxyBase/:path*` → `remoteOrigin/:path*`.
*/
proxyBase?: string;
}Consumed by the internal _buildEnvShim() helper that composes the <script> injected into the projected iframe before any framework code runs. Framework integrations and the custom element expose this as proxy / proxyBase.
SSR entry point
import {} from /* … */ "virtual-frame/ssr";Server-side helpers used by the Next.js, Nuxt, SvelteKit, TanStack Start, and other SSR integrations to seed projection payloads at render time. Most users consume these transitively through a framework wrapper. See the SSR guide for the primitives (fetchVirtualFrame, renderVirtualFrame) and the Next.js guide for a framework-integrated walkthrough.
Framework packages
Each framework package wraps the core engine with idiomatic bindings (components, hooks, signals, stores). See the guide for the package you use:
| Package | Guide |
|---|---|
@virtual-frame/react | React |
@virtual-frame/next | Next.js |
@virtual-frame/react-router | React Router |
@virtual-frame/tanstack-start | TanStack Start |
@virtual-frame/react-server | @lazarv/react-server |
@virtual-frame/vue | Vue |
@virtual-frame/nuxt | Nuxt |
@virtual-frame/svelte | Svelte |
@virtual-frame/sveltekit | SvelteKit |
@virtual-frame/solid | Solid |
@virtual-frame/solid-start | SolidStart |
@virtual-frame/angular | Angular |
@virtual-frame/analog | Analog |
@virtual-frame/store | Shared Store |