Live code editors are one of the most powerful tools in a web developer's arsenal. From documentation sites with interactive examples to full-fledged platforms like CodePen and JSFiddle, the ability to write and execute code in the browser provides instant feedback and a fantastic learning experience.
But have you ever wondered how they work? The magic behind them is real-time code transpilation and bundling. A user types modern JavaScript, TypeScript, or JSX, and something behind the scenes needs to convert it into plain JavaScript that a browser can understand. Traditionally, this required a complex server setup, managing Node.js dependencies, and wrestling with build tools like Webpack or Rollup.
Today, we'll explore a modern, simpler approach. We'll build a functional, live React code editor using esbuild.do, a powerful API that provides blazing-fast JavaScript bundling without the server-side headaches.
esbuild.do is an agentic workflow that exposes the power of esbuild—an extremely fast JavaScript bundler and minifier—through a simple REST API. Instead of installing esbuild, managing node_modules, and configuring build scripts, you can send your code to the API and get back browser-ready, bundled code in milliseconds.
This is perfect for our use case because:
We're going to build a web page with two main components:
Let's get started!
First, we need a basic HTML structure. Create an index.html file and add the following code. This sets up our text editor, a "Run" button, and the iframe for the output.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live React Editor with esbuild.do</title>
<style>
body { font-family: sans-serif; display: flex; flex-direction: column; height: 100vh; margin: 0; }
#app { display: flex; flex: 1; height: 100%; }
.pane { flex: 1; height: 100%; overflow: auto; border-left: 1px solid #ccc; }
textarea { width: 100%; height: 100%; border: none; font-family: monospace; font-size: 16px; padding: 1rem; resize: none; }
iframe { width: 100%; height: 100%; border: none; }
header { padding: 0.5rem 1rem; background-color: #f7f7f7; border-bottom: 1px solid #ccc; text-align: right; }
button { background-color: #007bff; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; }
button:hover { background-color: #0056b3; }
</style>
</head>
<body>
<header>
<button id="runButton">Run</button>
</header>
<div id="app">
<div class="pane">
<textarea id="codeInput" placeholder="Write your React code here..."></textarea>
</div>
<div class="pane">
<iframe id="outputFrame" title="Code Output"></iframe>
</div>
</div>
<script type="module" src="app.js"></script>
</body>
</html>
Now for the core logic. We need to write JavaScript that:
To use the API, we'll install the handy @do-inc/sdk. In your project, run:
npm install @do-inc/sdk
Now, create a file named app.js and add the following:
// app.js
import { doing } from '@do-inc/sdk';
const codeInput = document.getElementById('codeInput');
const runButton = document.getElementById('runButton');
const outputFrame = document.getElementById('outputFrame');
// Add some default React code to the editor
const startingCode = `import React from 'https://esm.sh/react';
import ReactDOM from 'https://esm.sh/react-dom/client';
function App() {
const [count, setCount] = React.useState(0);
return (
<div>
<h1>Hello from esbuild.do!</h1>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
`;
codeInput.value = startingCode;
runButton.addEventListener('click', async () => {
const userCode = codeInput.value;
try {
// Show a loading state
outputFrame.srcdoc = `<html><body><p>Bundling...</p></body></html>`;
// Call the esbuild.do API
const { bundledCode, hasError } = await doing.esbuild({
entryPoints: ['index.jsx'],
sourceFiles: {
'index.jsx': userCode,
},
bundle: true,
minify: true,
platform: 'browser',
format: 'iife' // Immediately Invoked Function Expression
});
if (hasError) {
throw new Error(bundledCode); // On error, bundledCode contains the error message
}
// Prepare the HTML to inject into the iframe
const iframeContent = `
<html>
<head>
<style>body { font-family: sans-serif; }</style>
</head>
<body>
<div id="root"></div>
<script type="module">
${bundledCode}
</script>
</body>
</html>
`;
// Render the bundled code in the iframe
outputFrame.srcdoc = iframeContent;
} catch (error) {
console.error('Bundling failed:', error);
// Display the error directly in the iframe for easy debugging
outputFrame.srcdoc = `<html><body><pre style="color: red;">${error.message}</pre></body></html>`;
}
});
// Run the code once on initial load
runButton.click();
Let's break down the key doing.esbuild call:
Serve your index.html file using a simple local server. If you have Node.js, you can use npx serve in your project directory.
Open your browser, and you should see your live editor in action! You can now modify the JSX code, click "Run," and watch the output update instantly.
Building this tool was surprisingly simple. Here's why the esbuild.do approach is so effective:
By offloading the complex build step to a dedicated, high-performance API, we were able to focus entirely on the user-facing application. Whether you're enhancing your documentation, building educational tools, or creating the next great code playground, esbuild.do provides the performance and simplicity you need.
Ready to build your own on-demand tools? Head over to esbuild.do to learn more and get started.