CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-live

A production-focused playground for live editing React code with real-time preview capabilities

Pending
Overview
Eval results
Files

code-transformation.mddocs/

Code Transformation

Low-level utilities for transforming and executing JSX/TypeScript code in real-time. These functions power the LiveProvider component but are also available for custom implementations and advanced use cases.

Capabilities

generateElement

Synchronously transforms and executes JSX code to produce a React component. This function handles the complete pipeline from raw code to executable React elements, including TypeScript compilation, JSX transformation, and safe evaluation.

/**
 * Synchronously transforms and executes JSX code to produce a React component
 * @param options - Code transformation options
 * @param errorCallback - Function called when transformation or execution errors occur
 * @returns React ComponentType ready for rendering
 */
function generateElement(
  options: {
    /** The JSX/TypeScript code to transform and execute */
    code: string;
    /** Variables available in the code execution context */
    scope?: Record<string, unknown>;
    /** Whether to enable TypeScript transformations */
    enableTypeScript: boolean;
  },
  errorCallback: (error: Error) => void
): ComponentType;

Usage Examples:

import { generateElement } from "react-live";

// Basic JSX transformation
const simpleCode = '<h1>Hello World</h1>';
const SimpleComponent = generateElement(
  { 
    code: simpleCode, 
    enableTypeScript: false 
  },
  (error) => console.error('Generation failed:', error)
);

// With scope variables
const scopedCode = `
  <div>
    <Button onClick={handleClick}>
      {buttonText}
    </Button>
  </div>
`;

const ScopedComponent = generateElement(
  {
    code: scopedCode,
    scope: {
      Button: MyButtonComponent,
      handleClick: () => alert('Clicked!'),
      buttonText: 'Click Me'
    },
    enableTypeScript: true
  },
  (error) => console.error('Generation failed:', error)
);

// TypeScript with interfaces
const typedCode = `
  interface User {
    name: string;
    age: number;
  }
  
  const user: User = { name: 'Alice', age: 30 };
  
  <div>
    <h2>{user.name}</h2>
    <p>Age: {user.age}</p>
  </div>
`;

const TypedComponent = generateElement(
  {
    code: typedCode,
    enableTypeScript: true
  },
  (error) => console.error('TypeScript error:', error)
);

// Custom error handling
function createComponent(code: string) {
  let hasError = false;
  let errorMessage = '';
  
  const component = generateElement(
    { code, enableTypeScript: true },
    (error) => {
      hasError = true;
      errorMessage = error.message;
    }
  );
  
  return { component, hasError, errorMessage };
}

renderElementAsync

Asynchronously renders code that uses the render() function pattern (no-inline mode). This is used for more complex scenarios where the code needs to explicitly call a render function rather than returning JSX directly.

/**
 * Asynchronously renders code that uses the render() function (no-inline mode)
 * @param options - Code transformation options
 * @param resultCallback - Function called with the successfully rendered component
 * @param errorCallback - Function called when transformation or execution errors occur
 */
function renderElementAsync(
  options: {
    /** The code containing render() calls */
    code: string;
    /** Variables available in the code execution context */
    scope?: Record<string, unknown>;
    /** Whether to enable TypeScript transformations */
    enableTypeScript: boolean;
  },
  resultCallback: (component: ComponentType) => void,
  errorCallback: (error: Error) => void
): void;

Usage Examples:

import { renderElementAsync } from "react-live";

// Basic no-inline rendering
const noInlineCode = `
  function Welcome({ name }) {
    return <h1>Hello, {name}!</h1>;
  }
  
  render(<Welcome name="World" />);
`;

renderElementAsync(
  {
    code: noInlineCode,
    enableTypeScript: true
  },
  (component) => {
    console.log('Component ready:', component);
    // Use component for rendering
  },
  (error) => {
    console.error('Rendering failed:', error);
  }
);

// With complex component logic
const complexCode = `
  function UserCard({ user }) {
    const [expanded, setExpanded] = useState(false);
    
    return (
      <div className="user-card">
        <h3>{user.name}</h3>
        <button onClick={() => setExpanded(!expanded)}>
          {expanded ? 'Collapse' : 'Expand'}
        </button>
        {expanded && (
          <div>
            <p>Email: {user.email}</p>
            <p>Age: {user.age}</p>
          </div>
        )}
      </div>
    );
  }
  
  const sampleUser = {
    name: 'Alice Johnson',
    email: 'alice@example.com',
    age: 28
  };
  
  render(<UserCard user={sampleUser} />);
`;

renderElementAsync(
  {
    code: complexCode,
    scope: {
      useState: React.useState
    },
    enableTypeScript: true
  },
  (component) => {
    // Component with hooks ready to render
    setCurrentComponent(component);
  },
  (error) => {
    setErrorMessage(error.message);
  }
);

// Multiple component rendering
const multiComponentCode = `
  function Header() {
    return <h1>My App</h1>;
  }
  
  function Footer() {
    return <footer>© 2024</footer>;
  }
  
  function App() {
    return (
      <div>
        <Header />
        <main>
          <p>Welcome to my application!</p>
        </main>
        <Footer />
      </div>
    );
  }
  
  render(<App />);
`;

renderElementAsync(
  {
    code: multiComponentCode,
    enableTypeScript: false
  },
  (component) => {
    // Multi-component app ready
    mountComponent(component);
  },
  (error) => {
    displayError(error.message);
  }
);

// Error handling patterns
function safeRenderAsync(code: string, scope = {}) {
  return new Promise((resolve, reject) => {
    renderElementAsync(
      {
        code,
        scope,
        enableTypeScript: true
      },
      (component) => {
        resolve(component);
      },
      (error) => {
        reject(new Error(`Render failed: ${error.message}`));
      }
    );
  });
}

// Usage with async/await
async function handleCodeChange(newCode: string) {
  try {
    const component = await safeRenderAsync(newCode, {
      useState: React.useState,
      useEffect: React.useEffect
    });
    setPreviewComponent(component);
  } catch (error) {
    setError(error.message);
  }
}

Transformation Pipeline

The code transformation process follows these steps:

  1. Input Validation: Verify code is a valid string
  2. TypeScript Compilation: Transform TypeScript to JavaScript (if enabled)
  3. JSX Transformation: Convert JSX syntax to React.createElement calls
  4. Import Resolution: Handle ES6 import statements
  5. Code Wrapping: Add necessary wrapper code (for inline mode)
  6. Safe Evaluation: Execute code in controlled environment with provided scope
  7. Error Boundary: Wrap result in error boundary for safe rendering

Code Execution Modes

Inline Mode (generateElement)

  • Code is automatically wrapped in a return statement
  • Suitable for simple JSX expressions
  • Direct evaluation and immediate component return
  • Example: <h1>Hello</h1> becomes return (<h1>Hello</h1>)

No-Inline Mode (renderElementAsync)

  • Code must explicitly call render() function
  • Suitable for complex component definitions
  • Allows multiple component definitions before rendering
  • Example: Code must contain render(<MyComponent />)

Error Types

// Common error scenarios handled by transformation functions:

// Syntax errors in JSX/TypeScript
SyntaxError: "Unexpected token '<'"

// Runtime errors during component execution  
ReferenceError: "CustomComponent is not defined"

// No-inline mode specific errors
SyntaxError: "No-Inline evaluations must call `render`"
SyntaxError: "`render` must be called with valid JSX"

// Transformation errors
Error: "Code failed to transform"

Advanced Usage

// Custom transformation wrapper
function createLiveTransformer(defaultScope: Record<string, unknown>) {
  return {
    transformInline: (code: string, additionalScope = {}) => {
      const scope = { ...defaultScope, ...additionalScope };
      return generateElement({ code, scope, enableTypeScript: true }, console.error);
    },
    
    transformAsync: (code: string, additionalScope = {}) => {
      const scope = { ...defaultScope, ...additionalScope };
      return new Promise((resolve, reject) => {
        renderElementAsync(
          { code, scope, enableTypeScript: true },
          resolve,
          reject
        );
      });
    }
  };
}

// Usage
const transformer = createLiveTransformer({
  React,
  useState: React.useState,
  useEffect: React.useEffect
});

const InlineComponent = transformer.transformInline('<h1>Inline</h1>');
const AsyncComponent = await transformer.transformAsync(`
  function App() { return <div>Async</div>; }
  render(<App />);
`);

Install with Tessl CLI

npx tessl i tessl/npm-react-live

docs

code-transformation.md

context-integration.md

index.md

live-components.md

standalone-editor.md

tile.json