Getting started
Install
npm install react-exe
Works with React 18 and 19. No bundler configuration is needed. If you added define: { "process.env": {} } to your Vite config for an older version, you can remove it.
Render a component from a string
import { CodeExecutor } from "react-exe";
const code = `
export default function Counter() {
const [n, setN] = React.useState(0);
return (
<div>
<p>{n}</p>
<button onClick={() => setN(n + 1)}>+</button>
</div>
);
}
`;
export default function App() {
return <CodeExecutor code={code} />;
}
React is always in scope inside the code string, so importing it is optional. The code can be TypeScript.
The entry file should default-export the component. Classes, memo(...) and forwardRef(...) components work too. Without a default export, an App export or a single capitalized component export is used.
Enable Tailwind CSS
<CodeExecutor
code={code}
config={{ enableTailwind: true }}
/>
Loads Tailwind inside the preview so all utility classes work.
Use npm packages
If the code imports a package that isn't in config.dependencies, react-exe loads it from npm via esm.sh:
const code = `
import { format } from "date-fns";
import { motion } from "framer-motion";
export default function Today() {
return (
<motion.p animate={{ opacity: [0, 1] }}>
{format(new Date(), "MMMM do, yyyy")}
</motion.p>
);
}
`;
- Packages that use React (framer-motion, Radix, lucide-react, ...) share your app's React instance, so hooks and context work.
- Pin a version in the import itself:
import { format } from "date-fns@3". Unpinned imports get the latest version. - CSS imports from packages work:
import "react-toastify/dist/ReactToastify.css". - Set
autoResolvePackage: falseto require every import to come fromdependencies.
Provide your own modules
Pass modules your app already has with dependencies. Keys ending in /* match any sub-path:
import * as ui from "./components/ui";
<CodeExecutor
code={code}
config={{
dependencies: {
// import { Button } from "@/components/ui/button"
"@/components/ui/*": ui,
},
}}
/>
Multi-file components
Pass an array of file objects when the component spans multiple files:
const files = [
{
name: "App.tsx",
content: `import Header from './components/Header';
import './styles.css';
export default function App() { return <Header />; }`,
isEntry: true,
},
{
name: "components/Header.tsx",
content: `export default function Header() { return <h1>Hello</h1>; }`,
},
{
name: "styles.css",
content: `h1 { color: #667eea; }`,
},
];
<CodeExecutor code={files} />
Files can import each other with relative paths, folder index files and @/ aliases (@/components/Header resolves to components/Header.tsx or src/components/Header.tsx). CSS and JSON files are supported.
Render without the iframe
<CodeExecutor
code={code}
config={{ sandbox: false }}
/>
Renders directly in your page instead of an iframe. Useful for tight embedding, but the code then shares your page's DOM and styles.
Security
The iframe is same-origin so your app's React can render into it. It isolates DOM and styles, but it is not a security boundary: code inside it can reach your page. Only render code you trust, or host the preview on a separate origin for untrusted input.
Next steps
- API reference for all
CodeExecutorprops and config options