Execute React components on the fly with external dependencies, custom styling, and TypeScript support. Perfect for creating live code previews, documentation, or interactive code playgrounds.

Try the live demo here.
- 🚀 Execute React components from string code
- 📦 Support for external dependencies
- 🎨 Tailwind CSS support
- 🔒 Built-in security checks
- 💅 Customizable styling
- 📝 TypeScript support
- ⚡ Live rendering
- 🐛 Error boundary protection
- 📄 Multi-file support
npm install react-exe
# or
yarn add react-exe
# or
pnpm add react-exeIf you're using Vite, you need to add the following configuration to your vite.config.js or vite.config.ts:
import{defineConfig}from"vite";exportdefaultdefineConfig({define: {"process.env": {},},// ... rest of your config});This is required to ensure proper functionality in Vite projects.
import{CodeExecutor}from"react-exe";constcode=`export default function HelloWorld() { return ( <div className="p-4 bg-blue-100 rounded"> <h1 className="text-2xl font-bold">Hello World!</h1> </div> );}`;functionApp(){return<CodeExecutorcode={code}config={{enableTailwind: true}}/>;}import{CodeExecutor}from"react-exe";import*asechartsfrom"echarts";import*asframerMotionfrom"framer-motion";constcode=`import { motion } from 'framer-motion';import { LineChart } from 'echarts';export default function Dashboard() { return ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="p-6 space-y-4" > <LineChart option={{ xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] }, yAxis: { type: 'value' }, series: [{ data: [150, 230, 224], type: 'line' }] }} style={{ height: '300px' }} /> </motion.div> );}`;functionApp(){return(<CodeExecutorcode={code}config={{dependencies: {"framer-motion": framerMotion,echarts: echarts,},enableTailwind: true,containerClassName: "min-h-[400px]",containerStyle: {padding: "20px",background: "#f9fafb",},}}/>);}By default, react-exe automatically resolves missing packages from CDN (jsDelivr or unpkg). This means you don't need to manually provide all dependencies:
import{CodeExecutor}from"react-exe";constcode=`import { useState } from 'react';import { motion } from 'framer-motion';import * as echarts from 'echarts';export default function Dashboard() { const [count, setCount] = useState(0); return ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="p-6" > <h1>Count: {count}</h1> <button onClick={() => setCount(count + 1)}> Increment </button> </motion.div> );}`;functionApp(){return(<CodeExecutorcode={code}config={{enableTailwind: true,// autoResolvePackage defaults to true, so framer-motion will be// automatically fetched from CDN if not provided}}/>);}How it works:
- When a package is imported but not provided in
dependencies,react-exeautomatically attempts to fetch it from jsDelivr CDN - If jsDelivr fails, it falls back to unpkg
- Packages are cached to avoid redundant network requests
- A loading indicator is shown while dependencies are being resolved
Disable automatic resolution:
If you want to disable automatic package resolution and require all dependencies to be provided manually:
<CodeExecutorcode={code}config={{autoResolvePackage: false,// Disable automatic CDN resolutiondependencies: {// All dependencies must be provided manually"framer-motion": framerMotion,},}}/>Note: Automatic resolution works best with packages that support ES modules. Some packages may require manual configuration.
Package Compatibility Guide:
The auto-resolution feature works best with different types of packages:
✅ Works Great (Recommended for auto-resolution):
- Utility libraries:
date-fns,lodash-es,ramda,validator,uuid - Formatting:
numeral,dayjs,luxon,currency.js - Simple React components:
react-icons,react-syntax-highlighter - Data visualization:
recharts,victory(when using manual React) - Math/Logic:
mathjs,big.js,decimal.js
- Complex React libraries:
framer-motion,react-spring(better to provide manually due to React context requirements) - CSS-in-JS:
@emotion/styled,styled-components(may need configuration) - UI frameworks:
@mui/material,antd(large dependencies, better to provide manually)
❌ Won't Work (Browser limitations):
- Node.js-specific:
fs,path,crypto,process(not available in browser) - Build-time only:
@svgr/webpack, bundler plugins - Native modules: Packages with
.nodebindings
Best Practice: For production apps, manually provide critical dependencies for better control and faster load times. Use auto-resolution for quick prototyping or simple utilities.
React-EXE renders user code inside a sandboxed iframe by default. This keeps previews isolated from your application DOM and prevents malicious scripts from mutating global state.
import{CodeExecutor}from"react-exe";functionApp(){return(<CodeExecutorcode={`export default function Demo() { return <div>Sandboxed output</div>; }`}config={{sandbox: true,// DefaultenableTailwind: true,}}/>);}Sandbox benefits
- Independent DOM tree and CSS scope per preview
- Prevents access to the parent
document/window - Works with automatic CDN dependency resolution
- Tailwind is loaded inside the iframe when enabled
Disable sandbox mode only when you trust the code and require full access to the parent context (for example, to share in-memory objects or DOM nodes).
<CodeExecutorcode={code}config={{sandbox: false,// Runs directly in the parent documentdependencies: {
echarts,"framer-motion": motion,},}}/>sandbox is false, executed code runs alongside your app and can read or mutate the parent DOM/window.
import{CodeExecutor}from"react-exe";import*asechartsfrom"echarts";import*asframerMotionfrom"framer-motion";import*asuiComponentsfrom"../ShadcnComps";constcode=`import { motion } from 'framer-motion';import { LineChart } from 'echarts';import { Button } from "@/components/ui/button"export default function Dashboard() { return ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="p-6 space-y-4" > <LineChart option={{ xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] }, yAxis: { type: 'value' }, series: [{ data: [150, 230, 224], type: 'line' }] }} style={{ height: '300px' }} /> </motion.div> );}`;functionApp(){return(<CodeExecutorcode={code}config={{dependencies: {"framer-motion": framerMotion,echarts: echarts,"@/components/ui/*": uiComponents,},enableTailwind: true,containerClassName: "min-h-[400px]",containerStyle: {padding: "20px",background: "#f9fafb",},}}/>);}React-EXE supports multiple files with cross-imports, allowing you to build more complex components and applications:
import{CodeExecutor}from"react-exe";import*asframerMotionfrom"framer-motion";// Define multiple files as an array of code filesconstfiles=[{name: "App.tsx",// Main entry filecontent: `import React from 'react';import { motion } from 'framer-motion';import Header from './Header';import Counter from './Counter';const App = () => { return ( <motion.div className="min-h-screen bg-gray-100 p-4" initial={{ opacity: 0 }} animate={{ opacity: 1 }} > <Header title="Multi-File App Example" /> <Counter /> </motion.div> );};export default App; `,isEntry: true,// Mark this as the entry point},{name: "Header.tsx",content: `import React from 'react';interface HeaderProps { title: string;}const Header = ({ title }: HeaderProps) => { return ( <header className="bg-white p-4 mb-4 rounded-lg shadow-md"> <h1 className="text-2xl font-bold text-gray-800">{title}</h1> </header> );};export default Header; `,},{name: "Counter.tsx",content: `import React, { useState } from 'react';import { motion } from 'framer-motion';import CounterButton from './CounterButton';const Counter = () => { const [count, setCount] = useState(0); const increment = () => setCount(prev => prev + 1); const decrement = () => setCount(prev => prev - 1); return ( <div className="bg-white p-6 rounded-lg shadow-md"> <h2 className="text-xl font-semibold mb-4">Counter Component</h2> <motion.div className="text-center text-3xl font-bold my-4" key={count} initial={{ scale: 0.8, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} > {count} </motion.div> <div className="flex justify-center gap-4"> <CounterButton onClick={decrement} label="Decrease" variant="danger" /> <CounterButton onClick={increment} label="Increase" variant="success" /> </div> </div> );};export default Counter; `,},{name: "CounterButton.tsx",content: `import React from 'react';import { motion } from 'framer-motion';interface CounterButtonProps { onClick: () => void; label: string; variant?: 'primary' | 'success' | 'danger';}const CounterButton = ({ onClick, label, variant = 'primary' }: CounterButtonProps) => { const getButtonColor = () => { switch(variant) { case 'success': return 'bg-green-500 hover:bg-green-600'; case 'danger': return 'bg-red-500 hover:bg-red-600'; default: return 'bg-blue-500 hover:bg-blue-600'; } }; return ( <motion.button className={\`\${getButtonColor()} text-white py-2 px-4 rounded\`} onClick={onClick} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {label} </motion.button> );};export default CounterButton; `,},];functionApp(){return(<CodeExecutorcode={files}config={{dependencies: {"framer-motion": framerMotion,},enableTailwind: true,containerClassName: "rounded-lg overflow-hidden",}}/>);}For more complex applications, you can organize your files in a project-like structure:
import{CodeExecutor}from"react-exe";import*asreactRouterfrom"react-router-dom";import*asframerMotionfrom"framer-motion";constfiles=[{name: "App.tsx",content: `import React from 'react';import { BrowserRouter, Routes, Route } from 'react-router-dom';import Layout from './components/Layout';import Home from './pages/Home';import About from './pages/About';import NotFound from './pages/NotFound';const App = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout />}> <Route index element={<Home />} /> <Route path="about" element={<About />} /> <Route path="*" element={<NotFound />} /> </Route> </Routes> </BrowserRouter> );};export default App; `,isEntry: true,},{name: "components/Layout.tsx",content: `import React from 'react';import { Outlet } from 'react-router-dom';import Navbar from './Navbar';import Footer from './Footer';const Layout = () => { return ( <div className="min-h-screen flex flex-col"> <Navbar /> <main className="flex-grow container mx-auto px-4 py-8"> <Outlet /> </main> <Footer /> </div> );};export default Layout; `,},{name: "components/Navbar.tsx",content: `import React from 'react';import { Link, useLocation } from 'react-router-dom';const Navbar = () => { const location = useLocation(); const isActive = (path: string) => { return location.pathname === path ? 'text-white bg-indigo-700' : 'text-indigo-200 hover:text-white hover:bg-indigo-600'; }; return ( <nav className="bg-indigo-800 text-white shadow-md"> <div className="container mx-auto px-4"> <div className="flex justify-between items-center h-16"> <Link to="/" className="font-bold text-xl">Multi-File App</Link> <div className="flex space-x-4"> <Link to="/" className={\`px-3 py-2 rounded-md \${isActive('/')}\`} > Home </Link> <Link to="/about" className={\`px-3 py-2 rounded-md \${isActive('/about')}\`} > About </Link> </div> </div> </div> </nav> );};export default Navbar; `,},{name: "components/Footer.tsx",content: `import React from 'react';const Footer = () => { return ( <footer className="bg-gray-800 text-white py-6"> <div className="container mx-auto px-4 text-center"> <p>© {new Date().getFullYear()} React-EXE Demo</p> <p className="text-gray-400 text-sm mt-1">Built with multiple files</p> </div> </footer> );};export default Footer; `,},{name: "pages/Home.tsx",content: `import React from 'react';import { motion } from 'framer-motion';const Home = () => { return ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }} > <h1 className="text-3xl font-bold mb-6">Welcome to the Home Page</h1> <p className="mb-4">This is a multi-file application example using React-EXE.</p> <p className="mb-4"> It demonstrates how you can create complex applications with multiple components, pages, and even routing! </p> <div className="mt-8 p-6 bg-indigo-50 rounded-lg shadow-sm"> <h2 className="text-xl font-semibold mb-4">Features Demonstrated:</h2> <ul className="list-disc pl-5 space-y-2"> <li>Multiple file structure</li> <li>React Router integration</li> <li>Animation with Framer Motion</li> <li>Component composition</li> <li>Styling with Tailwind CSS</li> </ul> </div> </motion.div> );};export default Home; `,},{name: "pages/About.tsx",content: `import React from 'react';import { motion } from 'framer-motion';const About = () => { return ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }} > <h1 className="text-3xl font-bold mb-6">About Page</h1> <p className="mb-4"> React-EXE is a powerful library for executing React components on the fly. It supports multi-file applications like this one! </p> <motion.div className="mt-8 grid grid-cols-1 md:grid-cols-3 gap-4" variants={{ hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.2 } } }} initial="hidden" animate="show" > {[1, 2, 3].map((item) => ( <motion.div key={item} className="bg-white p-6 rounded-lg shadow-md" variants={{ hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0 } }} > <h3 className="font-bold text-lg mb-2">Feature {item}</h3> <p className="text-gray-600"> This is an example of a card that demonstrates Framer Motion animations in a multi-file React component. </p> </motion.div> ))} </motion.div> </motion.div> );};export default About; `,},{name: "pages/NotFound.tsx",content: `import React from 'react';import { Link } from 'react-router-dom';import { motion } from 'framer-motion';const NotFound = () => { return ( <motion.div className="text-center py-12" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }} > <motion.div initial={{ scale: 0.8 }} animate={{ scale: 1 }} transition={{ type: "spring", stiffness: 200, damping: 10 }} > <h1 className="text-9xl font-bold text-indigo-200">404</h1> </motion.div> <h2 className="text-3xl font-bold mb-4">Page Not Found</h2> <p className="text-gray-600 mb-8"> The page you're looking for doesn't exist or has been moved. </p> <Link to="/" className="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700 transition-colors" > Return Home </Link> </motion.div> );};export default NotFound; `,},];functionApp(){return(<CodeExecutorcode={files}config={{dependencies: {"react-router-dom": reactRouter,"framer-motion": framerMotion,},enableTailwind: true,}}/>);}You can also create and use custom hooks, utilities, and TypeScript types across multiple files:
import{CodeExecutor}from"react-exe";constfiles=[{name: "App.tsx",content: `import React from 'react';import ThemeProvider from './theme/ThemeProvider';import ThemeSwitcher from './components/ThemeSwitcher';import UserProfile from './components/UserProfile';import { fetchUserData } from './utils/api';const App = () => { return ( <ThemeProvider> <div className="min-h-screen p-6"> <div className="max-w-lg mx-auto"> <div className="flex justify-end mb-6"> <ThemeSwitcher /> </div> <UserProfile userId="1" fetchUserData={fetchUserData} /> </div> </div> </ThemeProvider> );};export default App; `,isEntry: true,},{name: "types/index.ts",content: `export interface User { id: string; name: string; email: string; avatar: string;}export type Theme = 'light' | 'dark' | 'system';export interface ThemeContextType { theme: Theme; setTheme: (theme: Theme) => void;} `,},{name: "theme/ThemeProvider.tsx",content: `import React, { createContext, useContext, useState, useEffect } from 'react';import { Theme, ThemeContextType } from '../types';const ThemeContext = createContext<ThemeContextType | undefined>(undefined);const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [theme, setTheme] = useState<Theme>('system'); useEffect(() => { const applyTheme = (newTheme: Theme) => { const root = window.document.documentElement; // Remove any existing theme classes root.classList.remove('light', 'dark'); // Apply the appropriate theme if (newTheme === 'system') { const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; root.classList.add(systemTheme); } else { root.classList.add(newTheme); } }; applyTheme(theme); // Listen for system theme changes const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handleChange = () => { if (theme === 'system') { applyTheme('system'); } }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, [theme]); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> );};export const useTheme = () => { const context = useContext(ThemeContext); if (context === undefined) { throw new Error('useTheme must be used within a ThemeProvider'); } return context;};export default ThemeProvider; `,},{name: "components/ThemeSwitcher.tsx",content: `import React from 'react';import { useTheme } from '../theme/ThemeProvider';import { Theme } from '../types';const ThemeSwitcher = () => { const { theme, setTheme } = useTheme(); const themes: { value: Theme; label: string }[] = [ { value: 'light', label: '☀️ Light' }, { value: 'dark', label: '🌙 Dark' }, { value: 'system', label: '🖥️ System' } ]; return ( <div className="bg-white dark:bg-gray-800 p-3 rounded-lg shadow-md inline-block"> <div className="flex space-x-2"> {themes.map(({ value, label }) => ( <button key={value} onClick={() => setTheme(value)} className={\`px-3 py-1 rounded-md \${ theme === value ? 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-200' : 'hover:bg-gray-100 dark:hover:bg-gray-700' }\`} > {label} </button> ))} </div> </div> );};export default ThemeSwitcher; `,},{name: "hooks/useUser.ts",content: `import { useState, useEffect } from 'react';import { User } from '../types';export const useUser = ( userId: string, fetchUserData: (id: string) => Promise<User>) => { const [user, setUser] = useState<User | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { let isMounted = true; const loadUser = async () => { try { setLoading(true); const userData = await fetchUserData(userId); if (isMounted) { setUser(userData); setError(null); } } catch (err) { if (isMounted) { setError('Failed to load user'); setUser(null); } } finally { if (isMounted) { setLoading(false); } } }; loadUser(); return () => { isMounted = false; }; }, [userId, fetchUserData]); return { user, loading, error };}; `,},{name: "utils/api.ts",content: `import { User } from '../types';// Simulate API call with mock dataexport const fetchUserData = async (userId: string): Promise<User> => { // Simulate network delay await new Promise(resolve => setTimeout(resolve, 1000)); // Mock data const users: Record<string, User> = { '1': { id: '1', name: 'John Doe', email: 'john@example.com', avatar: 'https://randomuser.me/api/portraits/men/32.jpg' }, '2': { id: '2', name: 'Jane Smith', email: 'jane@example.com', avatar: 'https://randomuser.me/api/portraits/women/44.jpg' } }; const user = users[userId]; if (!user) { throw new Error(\`User with ID \${userId} not found\`); } return user;}; `,},{name: "components/UserProfile.tsx",content: `import React from 'react';import { useUser } from '../hooks/useUser';import { User } from '../types';interface UserProfileProps { userId: string; fetchUserData: (id: string) => Promise<User>;}const UserProfile = ({ userId, fetchUserData }: UserProfileProps) => { const { user, loading, error } = useUser(userId, fetchUserData); if (loading) { return ( <div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 animate-pulse"> <div className="flex items-center space-x-4"> <div className="rounded-full bg-gray-300 dark:bg-gray-600 h-16 w-16"></div> <div className="flex-1 space-y-3"> <div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-3/4"></div> <div className="h-3 bg-gray-300 dark:bg-gray-600 rounded w-1/2"></div> </div> </div> </div> ); } if (error) { return ( <div className="bg-red-100 dark:bg-red-900 border-l-4 border-red-500 text-red-700 dark:text-red-200 p-4 rounded"> <p>{error}</p> </div> ); } if (!user) { return <div>No user found</div>; } return ( <div className="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden"> <div className="p-6"> <div className="flex items-center space-x-4"> <img src={user.avatar} alt={user.name} className="h-16 w-16 rounded-full border-2 border-indigo-500" /> <div> <h2 className="text-xl font-bold text-gray-900 dark:text-white">{user.name}</h2> <p className="text-gray-600 dark:text-gray-300">{user.email}</p> </div> </div> </div> <div className="bg-gray-50 dark:bg-gray-900 px-6 py-4"> <p className="text-sm text-gray-500 dark:text-gray-400"> User ID: {user.id} </p> </div> </div> );};export default UserProfile; `,},];functionApp(){return(<CodeExecutorcode={files}config={{enableTailwind: true,}}/>);}import{CodeExecutor}from"react-exe";functionApp(){return(<CodeExecutorcode={code}config={{enableTailwind: true,errorClassName: "my-error-class",errorStyle: {background: "#fee2e2",border: "2px solid #ef4444",},onError: (error)=>{console.error("Component error:",error);// Send to error tracking servicetrackError(error);},// Custom security patternssecurityPatterns: [/localStorage/i,/sessionStorage/i,/window\.location/i,],}}/>);}The config prop accepts the following options:
interfaceCodeExecutorConfig{// External dependencies available to the rendered componentdependencies?: Record<string,any>;// Enable Tailwind CSS supportenableTailwind?: boolean;// Automatically resolve missing packages from CDN (jsDelivr/unpkg)// Default: trueautoResolvePackage?: boolean;// Execute code inside isolated iframe sandbox// Default: truesandbox?: boolean;// Custom className for the containercontainerClassName?: string;// Custom inline styles for the containercontainerStyle?: React.CSSProperties;// Custom className for error messageserrorClassName?: string;// Custom inline styles for error messageserrorStyle?: React.CSSProperties;// Custom security patterns to block potentially malicious codesecurityPatterns?: RegExp[];// Error callback functiononError?: (error: Error)=>void;}React-EXE accepts code in two formats:
Single File: Pass a string containing the React component code
// Single file as a stringconstcode=`export default function App() { return <div>Hello World</div>;}`;
Multiple Files: Pass an array of CodeFile objects:
// Multiple filesconstcode=[{name: "App.tsx",content: "import React from 'react';\nimport Button from './Button';\n...",isEntry: true,// Mark this as the entry point},{name: "Button.tsx",content: "export default function Button() { return <button>Click me</button>; }",},];
The
CodeFileinterface:interfaceCodeFile{name: string;// File name with extension (used for imports)content: string;// File contentisEntry?: boolean;// Whether this is the entry point (defaults to first file if not specified)}
React-EXE includes built-in security measures:
- Default security patterns to block potentially harmful code
- Custom security pattern support
- Error boundary protection
Default blocked patterns include:
constdefaultSecurityPatterns=[/document\.cookie/i,/window\.document\.cookie/i,/eval\(/i,/Function\(/i,/document\.write/i,/document\.location/i,];React-EXE is written in TypeScript and includes type definitions. For the best development experience, use TypeScript in your project:
import{CodeExecutor,CodeExecutorConfig,CodeFile}from"react-exe";constconfig: CodeExecutorConfig={enableTailwind: true,dependencies: {"my-component": MyComponent,},};constfiles: CodeFile[]=[{name: "App.tsx",content: `export default function App() { return <div>Hello</div>; }`,isEntry: true,},];functionApp(){return<CodeExecutorcode={files}config={config}/>;}Used By TuneChat to render Artifacts
