Fundamentals 9 min read

Understanding the ShadowRealm API: Isolated JavaScript Realms and Their Usage

The article introduces the ShadowRealm API, a new JavaScript proposal that creates highly isolated execution realms, explains its type signatures, demonstrates .evaluate() and .importValue() methods with code examples, compares it to eval, Function, iframes, Web Workers, and Node.js vm, and outlines practical use cases such as plugin execution, testing, and web scraping.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding the ShadowRealm API: Isolated JavaScript Realms and Their Usage

In this article, ConardLi presents the ShadowRealm API , a new JavaScript proposal that enables the creation of multiple highly isolated JavaScript realms, each with its own global and built‑in objects.

A realm represents an independent JavaScript execution environment, similar to the environment provided by an iframe . The article shows a simple iframe example where the global objects of the main page and the iframe differ, illustrating the concept of separate realms.

ShadowRealm API

The API defines a ShadowRealm class with the following type signature:

declare class ShadowRealm {
  constructor();
  evaluate(sourceText: string): PrimitiveValueOrCallable;
  importValue(specifier: string, bindingName: string): Promise
;
}

Each ShadowRealm instance provides two ways to run code:

.evaluate() : synchronously executes a code string, similar to eval() .

.importValue() : asynchronously loads a module and returns a Promise that resolves to a value or callable.

.evaluate()

The signature is evaluate(sourceText: string): PrimitiveValueOrCallable . It works like eval() but runs the code inside the realm’s isolated environment, so global objects such as globalThis and Array are distinct from those of the outer realm.

const sr = new ShadowRealm();
console.assert(sr.evaluate(`'ab' + 'cd'`) === 'abcd');

When the evaluated code returns a function, the function is wrapped so it can be called from the outer realm while still executing inside the realm.

globalThis.realm = 'incubator realm';
const sr = new ShadowRealm();
sr.evaluate(`globalThis.realm = 'ConardLi realm'`);
const wrappedFunc = sr.evaluate(`() => globalThis.realm`);
console.assert(wrappedFunc() === 'ConardLi realm');

Only primitive values or callables can be passed between realms; otherwise a TypeError is thrown.

new ShadowRealm().evaluate('[]'); // TypeError: value passing between realms must be callable or primitive

.importValue()

The signature is importValue(specifier: string, bindingName: string): Promise . It allows importing an external module, executing it in the realm, and obtaining an exported binding.

// main.js
const sr = new ShadowRealm();
const wrappedSum = await sr.importValue('./my-module.js', 'sum');
console.assert(wrappedSum('hi', ' ', 'folks', '!') === 'hi ConardLi!');

// my-module.js
export function sum(...values) {
  return values.reduce((prev, value) => prev + value);
}

As with .evaluate() , values crossing the realm boundary must be primitives or callables.

What Can ShadowRealms Be Used For?

Running third‑party plugins in Web IDEs or drawing applications.

Creating a sandboxed programming environment for user code.

Executing untrusted code on a server safely.

Isolated test execution where each test suite starts in a fresh realm.

Web scraping and web‑application testing without affecting the main page.

Comparison with Other Isolation Mechanisms

eval() and Function

ShadowRealms are similar to eval() and Function but provide a separate realm, protecting the outer environment from side effects.

Web Workers

Web Workers run in a separate thread with asynchronous communication, offering stronger isolation than ShadowRealms, which execute synchronously in the same thread.

iframe

Each iframe has its own realm, but it requires DOM manipulation, is limited to browsers, and includes a full DOM, which can be heavyweight.

Node.js vm Module

The Node.js vm module resembles ShadowRealms and adds features like engine caching and import interception, but it only works in Node.js environments.

Practical Example: Running Tests Inside a ShadowRealm

The article provides a small test library and demonstrates loading a test module with .importValue() , then executing the collected tests via runTests() inside the realm.

// test-lib.js
export function test(description, callback) { /* ... */ }
export function runTests() { /* ... */ }

// my-test.js
import { test } from './test-lib.js';
import * as assert from './assertions.js';

test('succeeds', () => { assert.equal(3, 3); });

test('fails', () => { assert.equal(1, 3); });
export default true;

// test-runner.js
async function runTestModule(moduleSpecifier) {
  const sr = new ShadowRealm();
  await sr.importValue(moduleSpecifier, 'default');
  const runTests = await sr.importValue('./test-lib.js', 'runTests');
  const result = runTests();
  console.log(result);
}
await runTestModule('./my-test.js');

Running Web Applications in ShadowRealms

Libraries such as jsdom currently use the Node.js vm module to simulate a browser environment; they could switch to ShadowRealms for cross‑platform support.

References

https://2ality.com/2022/04/shadow-realms.html

https://dev.to/smpnjn/future-javascript-shadowrealms-20mg

JavaScriptRuntimesecurityWeb APIisolationShadowRealm
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.