An open API service indexing awesome lists of open source software.

https://github.com/vgulerianb/react-exe

A powerful React component executor that renders code with external dependencies and custom styling
https://github.com/vgulerianb/react-exe

artifacts claude javascript nextjs playground react runtime v0

Last synced: about 1 month ago
JSON representation

A powerful React component executor that renders code with external dependencies and custom styling

Awesome Lists containing this project

README

        

# 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.

Screenshot 2025-02-26 at 00 23 34

Try the live demo [here](https://react-exe-demo.vercel.app/).

## 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

```bash
npm install react-exe
# or
yarn add react-exe
# or
pnpm add react-exe
```

## Basic Usage

```tsx
import { CodeExecutor } from "react-exe";

const code = `
export default function HelloWorld() {
return (


Hello World!



);
}
`;

function App() {
return ;
}
```

## Advanced Usage

### With External Dependencies

```tsx
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 (



);
}
`;

function App() {
return (

);
}
```

### With absolute imports and wildcard patterns

```tsx
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 (



);
}
`;

function App() {
return (

);
}
```

### With Multiple Files

React-EXE supports multiple files with cross-imports, allowing you to build more complex components and applications:

```tsx
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 (




);
};

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 (

{title}



);
};

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 (


Counter Component




{count}







);
};

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 (

{label}

);
};

export default CounterButton;
`,
},
];

function App() {
return (

);
}
```

### Creating a Project Structure with Multiple Files

For more complex applications, you can organize your files in a project-like structure:

```tsx
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 (


}>
} />
} />
} />



);
};

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 (








);
};

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 (



Multi-File App



Home


About





);
};

export default Navbar;
`,
},
{
name: "components/Footer.tsx",
content: `
import React from 'react';

const Footer = () => {
return (


© {new Date().getFullYear()} React-EXE Demo


Built with multiple files




);
};

export default Footer;
`,
},
{
name: "pages/Home.tsx",
content: `
import React from 'react';
import { motion } from 'framer-motion';

const Home = () => {
return (

Welcome to the Home Page


This is a multi-file application example using React-EXE.



It demonstrates how you can create complex applications with multiple
components, pages, and even routing!




Features Demonstrated:



  • Multiple file structure

  • React Router integration

  • Animation with Framer Motion

  • Component composition

  • Styling with Tailwind CSS




);
};

export default Home;
`,
},
{
name: "pages/About.tsx",
content: `
import React from 'react';
import { motion } from 'framer-motion';

const About = () => {
return (

About Page



React-EXE is a powerful library for executing React components on the fly.
It supports multi-file applications like this one!




{[1, 2, 3].map((item) => (

Feature {item}



This is an example of a card that demonstrates Framer Motion animations
in a multi-file React component.



))}


);
};

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 (


404




Page Not Found



The page you're looking for doesn't exist or has been moved.




Return Home


);
};

export default NotFound;
`,
},
];

function App() {
return (

);
}
```

### Using Custom Hooks and Utilities in Multi-File Apps

You can also create and use custom hooks, utilities, and TypeScript types across multiple files:

```tsx
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 (










);
};

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(undefined);

const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState('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 (

{children}

);
};

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 (



{themes.map(({ value, label }) => (
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}

))}


);
};

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
) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(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 => {
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 1000));

// Mock data
const users: Record = {
'1': {
id: '1',
name: 'John Doe',
email: '[email protected]',
avatar: 'https://randomuser.me/api/portraits/men/32.jpg'
},
'2': {
id: '2',
name: 'Jane Smith',
email: '[email protected]',
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;
}

const UserProfile = ({ userId, fetchUserData }: UserProfileProps) => {
const { user, loading, error } = useUser(userId, fetchUserData);

if (loading) {
return (










);
}

if (error) {
return (

{error}



);
}

if (!user) {
return
No user found
;
}

return (



{user.name}

{user.name}


{user.email}







User ID: {user.id}




);
};

export default UserProfile;
`,
},
];

function App() {
return (

);
}
```

### With Custom Error Handling

```tsx
import { CodeExecutor } from "react-exe";

function App() {
return (
{
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:

```typescript
interface CodeExecutorConfig {
// External dependencies available to the rendered component
dependencies?: Record;

// 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:

1. **Single File**: Pass a string containing the React component code

```typescript
// Single file as a string
const code = `
export default function App() {
return

Hello World
;
}
`;
```

2. **Multiple Files**: Pass an array of CodeFile objects:

```typescript
// 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 Click me; }",
},
];
```

The `CodeFile` interface:

```typescript
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:

```typescript
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:

```tsx
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

Hello
; }`,
isEntry: true,
},
];

function App() {
return ;
}
```

## Used By [TuneChat](https://chat.tune.app/) to render Artifacts

Screenshot 2025-02-26 at 16 58 34

## License

MIT © [Vikrant](https://www.linkedin.com/in/vikrant-guleria/)

---

Made with ❤️ by [Vikrant](https://www.linkedin.com/in/vikrant-guleria/)