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.
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 primitiveshadowRealm.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
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.