react-exe v1.0.11
React-EXE
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.
Features
- 🚀 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
Installation
npm install react-exe
# or
yarn add react-exe
# or
pnpm add react-exe
Basic Usage
import { CodeExecutor } from "react-exe";
const code = `
export default function HelloWorld() {
return (
<div className="p-4 bg-blue-100 rounded">
<h1 className="text-2xl font-bold">Hello World!</h1>
</div>
);
}
`;
function App() {
return <CodeExecutor code={code} config={{ enableTailwind: true }} />;
}
Advanced Usage
With External Dependencies
import { CodeExecutor } from "react-exe";
import * as echarts from "echarts";
import * as framerMotion from "framer-motion";
const code = `
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>
);
}
`;
function App() {
return (
<CodeExecutor
code={code}
config={{
dependencies: {
"framer-motion": framerMotion,
echarts: echarts,
},
enableTailwind: true,
containerClassName: "min-h-[400px]",
containerStyle: {
padding: "20px",
background: "#f9fafb",
},
}}
/>
);
}
With absolute imports and wildcard patterns
import { CodeExecutor } from "react-exe";
import * as echarts from "echarts";
import * as framerMotion from "framer-motion";
import * as uiComponents from "../ShadcnComps";
const code = `
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>
);
}
`;
function App() {
return (
<CodeExecutor
code={code}
config={{
dependencies: {
"framer-motion": framerMotion,
echarts: echarts,
"@/components/ui/*": uiComponents,
},
enableTailwind: true,
containerClassName: "min-h-[400px]",
containerStyle: {
padding: "20px",
background: "#f9fafb",
},
}}
/>
);
}
With Multiple Files
React-EXE supports multiple files with cross-imports, allowing you to build more complex components and applications:
import { CodeExecutor } from "react-exe";
import * as framerMotion from "framer-motion";
// Define multiple files as an array of code files
const files = [
{
name: "App.tsx", // Main entry file
content: `
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;
`,
},
];
function App() {
return (
<CodeExecutor
code={files}
config={{
dependencies: {
"framer-motion": framerMotion,
},
enableTailwind: true,
containerClassName: "rounded-lg overflow-hidden",
}}
/>
);
}
Creating a Project Structure with Multiple Files
For more complex applications, you can organize your files in a project-like structure:
import { CodeExecutor } from "react-exe";
import * as reactRouter from "react-router-dom";
import * as framerMotion from "framer-motion";
const files = [
{
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;
`,
},
];
function App() {
return (
<CodeExecutor
code={files}
config={{
dependencies: {
"react-router-dom": reactRouter,
"framer-motion": framerMotion,
},
enableTailwind: true,
}}
/>
);
}
Using Custom Hooks and Utilities in Multi-File Apps
You can also create and use custom hooks, utilities, and TypeScript types across multiple files:
import { CodeExecutor } from "react-exe";
const files = [
{
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 data
export 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;
`,
},
];
function App() {
return (
<CodeExecutor
code={files}
config={{
enableTailwind: true,
}}
/>
);
}
With Custom Error Handling
import { CodeExecutor } from "react-exe";
function App() {
return (
<CodeExecutor
code={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 service
trackError(error);
},
// Custom security patterns
securityPatterns: [
/localStorage/i,
/sessionStorage/i,
/window\.location/i,
],
}}
/>
);
}
Configuration Options
The config
prop accepts the following options:
interface CodeExecutorConfig {
// External dependencies available to the rendered component
dependencies?: Record<string, any>;
// Enable Tailwind CSS support
enableTailwind?: boolean;
// Custom className for the container
containerClassName?: string;
// Custom inline styles for the container
containerStyle?: React.CSSProperties;
// Custom className for error messages
errorClassName?: string;
// Custom inline styles for error messages
errorStyle?: React.CSSProperties;
// Custom security patterns to block potentially malicious code
securityPatterns?: RegExp[];
// Error callback function
onError?: (error: Error) => void;
}
Code Input Types
React-EXE accepts code in two formats:
Single File: Pass a string containing the React component code
// Single file as a string const code = ` export default function App() { return <div>Hello World</div>; } `;
Multiple Files: Pass an array of CodeFile objects:
// Multiple files const code = [ { 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
CodeFile
interface:interface CodeFile { name: string; // File name with extension (used for imports) content: string; // File content isEntry?: boolean; // Whether this is the entry point (defaults to first file if not specified) }
Security
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:
const defaultSecurityPatterns = [
/document\.cookie/i,
/window\.document\.cookie/i,
/eval\(/i,
/Function\(/i,
/document\.write/i,
/document\.location/i,
];
TypeScript Support
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";
const config: CodeExecutorConfig = {
enableTailwind: true,
dependencies: {
"my-component": MyComponent,
},
};
const files: CodeFile[] = [
{
name: "App.tsx",
content: `export default function App() { return <div>Hello</div>; }`,
isEntry: true,
},
];
function App() {
return <CodeExecutor code={files} config={config} />;
}
License
MIT © Vikrant
Made with ❤️ by Vikrant
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago