eval, no new Function, no dynamically compiled code. When a feature genuinely needs those (a templating engine, a code playground, running AI-generated snippets), Manifest V3 provides sandbox pages: extension pages that trade away chrome.* API access for a relaxed CSP.
Declare the page
manifest.json
sandbox.html like any other manifest-declared HTML entrypoint: reference your .ts/.tsx directly and it builds. See the HTML guide for the entrypoint pattern.
What a sandbox page can and cannot do
| Capability | Sandbox page |
|---|---|
eval, new Function | Yes |
| Unique origin (isolated from pages) | Yes |
chrome.* extension APIs | No |
Direct chrome.storage access | No |
postMessage with its embedder | Yes |
Embed and communicate
Load the sandbox in an iframe from a normal extension page, then talk overpostMessage:
newtab.html
newtab.ts
sandbox.ts
chrome.* access and acts as the broker: it reads storage, calls APIs, and passes plain data in and out of the sandbox.
Not the same thing: background.type
A common mix-up: "background": {"type": "module"} has nothing to do with sandboxing. It declares the background service worker as an ES module so import statements work at registration. Extension.js sets it automatically when your background entry uses ESM syntax, so you rarely write it by hand. If your confusion was about import failing in the service worker, see Background scripts; if it was about running dynamic code, you are in the right place.
Custom sandbox CSP
You can loosen or tighten the sandbox CSP further withcontent_security_policy.sandbox in the manifest. Keep script-src as narrow as the feature allows; the sandbox exists to contain dynamic code, not to disable security review. The security checklist covers what reviewers look for.

