Frontend Development 10 min read

Understanding the ShadowRealm API: Isolated JavaScript Execution Environments

This article introduces the ShadowRealm API, a new JavaScript proposal that creates independent, highly isolated realms for executing code, explains its type signatures, demonstrates .evaluate() and .importValue() methods with practical examples, and compares it with eval, Web Workers, iframes, and Node.js vm.

IT Services Circle
IT Services Circle
IT Services Circle
Understanding the ShadowRealm API: Isolated JavaScript Execution Environments

Today we look at a new JavaScript proposal entering stage 3: the ShadowRealm API . A realm represents an independent JavaScript execution environment with its own variable scope.

For example, the following code shows that each iframe has a separate global object, and objects like Array differ between realms:

<body>
  <iframe></iframe>
  <script>
    const win = frames[0].window;
    console.assert(win.globalThis !== globalThis); // true
    console.assert(win.Array !== Array); // true
  </script>
</body>

The ShadowRealm API allows a JS runtime to create multiple highly isolated realms, each with its own global and built‑in objects.

Its type signature is:

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

Each ShadowRealm instance provides two methods:

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

.importValue() : asynchronously imports a module and returns a Promise .

shadowRealm.evaluate()

The signature is evaluate(sourceText: string): PrimitiveValueOrCallable . It works like eval() but runs the code inside the realm’s own environment:

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

If the evaluated code returns a function, the function is wrapped so it can be called from the outer 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');

Values passed into a ShadowRealm must be primitive or callable; otherwise a TypeError is thrown.

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

shadowRealm.importValue()

The signature is importValue(specifier: string, bindingName: string): Promise . It can import an external module and return the exported value:

// 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);
}

Like .evaluate() , values crossing realms must be primitive or callable.

What Can ShadowRealms Be Used For?

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

Creating isolated programming environments for user code.

Executing untrusted code on servers safely.

Running tests without affecting the outer JS environment.

Web scraping and web‑app testing in isolated realms.

Comparison with Other Isolation Mechanisms

eval() and Function

ShadowRealms are similar to eval() and Function but provide a separate global environment, protecting the outer context.

Web Workers

Web Workers offer stronger isolation by running code in a separate thread with asynchronous communication, but for lightweight tasks ShadowRealms are more convenient and synchronous.

iframe

Each iframe has its own environment, but it requires DOM manipulation, includes a full DOM, and is limited to browsers.

<body>
  <iframe></iframe>
  <script>
    globalThis.realm = 'incubator';
    const iframeRealm = frames[0].window;
    iframeRealm.globalThis.realm = 'ConardLi';
    console.log(iframeRealm.eval('globalThis.realm')); // 'ConardLi'
  </script>
</body>

Node.js vm Module

The Node.js vm module is similar to ShadowRealms but offers more features like engine caching and import interception; however, it only works in Node.js environments.

Usage Example: Running Tests in a ShadowRealm

A simple test library defines test and runTests functions. Tests are collected in my-test.js and executed inside a ShadowRealm:

// test-lib.js
export function test(description, callback) { testDescs.push({description, callback}); }
export function runTests() { /* iterate testDescs and return results */ }

// 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;

The runner imports the test module and the test library, then runs the tests:

// 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

The jsdom library creates a simulated browser environment for testing web apps. It currently uses Node.js vm , but may 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
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

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.