Back to News & Insights
Web Development September 30, 2026 · 8 min read

Running untrusted React components in the browser

Page builders, plugin systems and customizable dashboards all have the same problem somewhere in...

Running untrusted React components in the browser

Page builders, plugin systems and customizable dashboards all have the same problem somewhere in them: users write UI code, and your app has to run it. That code might be broken or it might be hostile, and either way the rest of the page has to keep working.

I built a small playground to work through that problem end to end. You write a React component in TSX, it compiles and renders live, and a gallery of examples tries to break it in nine different ways. This note walks through how it works, layer by layer, and what each layer does and doesn't protect against.

Before choosing any tools, I listed what a user's component could do to the page hosting it: Fail to compile. A syntax error or a half-typed line. Import things it shouldn't. Anything from a network client to your app's internal modules. Get the wrong data. Props that don't match what the component expects. Throw. During render, or later from a click handler or a timer. Hang. An infinite loop, or a regex that backtracks forever. Reach out. Read the host's DOM, cookies or storage, or send data somewhere with fetch.

Each of these needs a different defense, and no single one covers them all. An error boundary doesn't stop a fetch, and a sandboxed iframe doesn't stop an infinite loop. So the playground is built as layers, each with one job:

Numbers match the sections below. Compiling happens in the host; everything that runs user code happens in the sandbox. Compile in the browser

The code starts as TSX, which browsers can't run. I wanted the demo to be a static site with no server, so compiling had to happen in the page.

I used Sucrase. It strips TypeScript types and rewrites JSX, and it's small and fast because that's all it does. esbuild-wasm and Babel standalone can do more, but both are much bigger downloads, and a live preview doesn't need bundling or older-browser output.

The part that matters most for safety is Sucrase's imports transform. It rewrites every import into a require() call:

That gives the runtime a single place where every dependency is requested, which the next layer uses.

Compiling runs in the host rather than the sandbox. It's a pure string-to-string transform, so it's safe to run there, and syntax errors still show up with a line and column even if the sandbox is down.

The tradeoff: Sucrase doesn't type-check. Type errors in user code are silently ignored, which is fine for a preview but worth knowing. Scope what the code can import

The obvious approach is to block the dangerous modules. That list never ends. The playground does the opposite: user code gets a fixed set of modules (react, zod, and a tiny UI kit), and everything else doesn't exist.

This is checked twice. First, the host scans the compiled code for require('...') calls and rejects anything off the list before the code is sent anywhere. The user gets a clear message and nothing runs.

A static scan misses computed names like require(someVar), so the sandbox enforces the same list again at runtime. The code runs with a require that only knows about the allowed modules:

Object.hasOwn matters there. A plain modules[name] lookup would also find inherited properties, so require('constructor') would return something.

One limit to be honest about: the allowlist covers modules, not globals. User code can still reach window, document and fetch without importing anything. Containing those is the job of layer 4. Validate props against a contract

A component that gets the wrong props usually crashes somewhere deep inside, with an error that says nothing about the actual problem. So each module can export a contract alongside the component:

Before every render, the props are checked against propsSchema. If they don't match, the component never runs, and the user sees a list of what's wrong instead of a stack trace:

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation