remote-dom
Take a tree of DOM elements built in a sandboxed JavaScript environment and mirror it onto the real DOM in a different environment, without ever exposing the main thread.
Repository Health
Technical Analysis
@remote-dom/core is the framework-agnostic engine behind Remote DOM, Shopify’s approach to running untrusted or resource-heavy UI code off the main thread while still letting it render real interface elements. Code running in an iframe or Web Worker builds a tree of custom elements using a RemoteElement base class and a minimal DOM polyfill; every attribute, property, event listener, and method call is serialized over a RemoteConnection and replayed by a receiver in the host environment, which reconstructs matching elements (or a plain JS tree) on the visible page.
The package ships as a set of narrow, tree-shakeable entry points — elements, receivers, polyfill, and html — rather than one monolithic API, so a consumer only pulls in the sandbox-side code, the host-side code, or both depending on which side of the boundary they’re implementing. Receivers include a DOMRemoteReceiver that mirrors elements 1:1 as real custom elements, and a plain-object RemoteReceiver for frameworks (React, Preact, Vue, Svelte) that want to render the tree themselves via their own reconciliation.
This is the same primitive Shopify uses to let merchants run arbitrary UI extension code (in checkout, admin, and elsewhere) inside a sandboxed iframe or worker while still rendering native-feeling components on the host page — the merchant’s code never touches the real DOM or gets access to the host page’s globals, only a restricted, serializable subset of DOM APIs.
What You Get
RemoteElement— a subclassableHTMLElementbase class for defining custom elements whose attributes, properties, methods, and events are automatically synchronized to the host environmentRemoteConnection/createRemoteConnection— a small serializable protocol (insert child, remove child, update text, update property/attribute/event/method) that both sides implement to exchange mutationsDOMRemoteReceiver— a host-side receiver that mirrors the remote tree as real DOM custom elements, so the host page ends up with matching<ui-button>-style elements automaticallyRemoteReceiver— a plain-JavaScript-object receiver for frameworks (React, Preact, Vue, Svelte) that want to reconcile the remote tree with their own rendering, rather than writing directly to the DOM@remote-dom/core/polyfill— a minimal DOM/customElementspolyfill so the sandboxed side can run in environments without real DOM globals, like a Web Worker@remote-dom/core/html— helpers for building and serializing remote trees from HTML-like syntax without hand-writingcreateElementcalls
Common Use Cases
- Running third-party or merchant-authored UI extension code in a sandboxed iframe or worker while still rendering it as native-feeling elements on the host page (Shopify’s own checkout/admin UI extensions)
- Moving expensive UI logic off the main thread into a Web Worker while keeping the rendered output indistinguishable from main-thread DOM
- Building a plugin or extensibility system where third-party code can render UI without being granted access to the host page’s real DOM, globals, or other extensions
- Bridging a sandboxed micro-frontend to a host application written in a different framework than the sandboxed code (e.g. a Preact sandbox rendered into a React host via the plain-object receiver)
Under The Hood
Architecture
The library is built around a strict producer/consumer split across an isolation boundary: the “remote” side subclasses RemoteElement (itself a subclass of the real or polyfilled HTMLElement) and reports every attribute/property/event/method change through internal hooks in elements/internals.ts; those changes are packed into typed mutation-record tuples (connection.ts, constants.ts) and pushed across a RemoteConnection implemented by whatever transport the app chooses (postMessage, a worker channel, etc.). On the host side, a receiver (receivers/RemoteReceiver.ts or receivers/DOMRemoteReceiver.ts) implements the same RemoteConnectionHandler interface and applies each mutation record to either a plain versioned JS tree or real DOM nodes. This keeps the core package framework-agnostic — it has no opinion on React/Preact/Vue reconciliation — while still giving each host framework enough to build a thin adapter (which is exactly what the sibling @remote-dom/react and @remote-dom/preact packages do).
Tech Stack
Written entirely in TypeScript (98%+ of the codebase) with no runtime framework dependency in @remote-dom/core itself; it depends only on the sibling @remote-dom/polyfill package and optionally @preact/signals-core as an opt-in peer dependency for signal-based property updates. The monorepo is managed with pnpm workspaces and Changesets for versioned releases, built per-package with Rollup (rollup.config.js) into ESM, CJS, and “esnext” output targets plus generated .d.ts types, and tested with Vitest plus a jsdom environment; a separate wpt-runner package runs the actual Web Platform Tests suite against the polyfill for spec conformance.
Code Quality
The package has a real, if not exhaustive, unit test suite (source/**/tests/*.test.ts) covering the element lifecycle, the RemoteMutationObserver, HTML serialization, and the plain-object RemoteReceiver, run under Vitest with @vitest/coverage-v8. Public APIs are extensively documented with JSDoc comments directly in the source (visible throughout RemoteElement.ts and the type definitions), TypeScript is used in strict mode throughout, Prettier enforces consistent formatting repo-wide, and GitHub Actions CI runs checks, tests, and a Changesets-based release/reminder workflow on every PR.
What Makes It Unique
Rather than shipping a single opinionated sandbxing solution, Remote DOM factors the hard part — a minimal, serializable subset of the DOM/custom-elements API plus a mutation-diffing protocol — into a standalone package that any transport (iframe postMessage, worker, even a network hop) and any host framework can sit on top of. Its polyfill and wpt-runner package are validated directly against the Web Platform Tests suite, an unusually rigorous conformance bar for a project of this size, and its production use inside Shopify’s own extensibility platform (checkout and admin UI extensions) demonstrates the approach at scale rather than as a research prototype.