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

standalone-editor.mddocs/

Standalone Editor

Independent code editor component with syntax highlighting that can be used outside of the live editing context. This component provides a rich editing experience for general-purpose code editing needs without the live execution functionality.

Capabilities

Editor

Standalone code editor component with syntax highlighting, configurable themes, and customizable behavior. Built on top of Prism.js for syntax highlighting and use-editable for editing functionality.

/**
 * Standalone code editor component with syntax highlighting
 * @param props - Editor configuration and behavior options
 */
function Editor(props: {
  /** CSS class name for the editor wrapper */
  className?: string;
  /** Code content to display and edit */
  code: string;
  /** Whether editing is disabled (read-only mode) */
  disabled?: boolean;
  /** Language identifier for syntax highlighting */
  language: string;
  /** Custom Prism instance for syntax highlighting */
  prism?: typeof Prism;
  /** Custom CSS styles for the editor wrapper */
  style?: CSSProperties;
  /** Tab key behavior: "focus" moves focus, "indentation" adds spaces */
  tabMode?: "focus" | "indentation";
  /** Prism theme for syntax highlighting */
  theme?: typeof themes.nightOwl;
  /** Callback function called when code content changes */
  onChange?(value: string): void;
}): JSX.Element;

Usage Examples:

import React, { useState } from "react";
import { Editor } from "react-live";
import { themes, Prism } from "prism-react-renderer";

// Basic standalone editor
function BasicEditor() {
  const [code, setCode] = useState('console.log("Hello World");');
  
  return (
    <Editor
      code={code}
      language="javascript"
      onChange={setCode}
    />
  );
}

// TypeScript editor with custom theme
function TypeScriptEditor() {
  const [code, setCode] = useState(`
    interface User {
      name: string;
      age: number;
    }
    
    const user: User = {
      name: "Alice",
      age: 30
    };
  `);
  
  return (
    <Editor
      code={code}
      language="typescript"
      theme={themes.dracula}
      onChange={setCode}
      style={{
        fontFamily: 'Monaco, Consolas, monospace',
        fontSize: 14,
        border: '1px solid #ccc',
        borderRadius: 4
      }}
    />
  );
}

// Read-only code display
function CodeDisplay({ code, language }: { code: string; language: string }) {
  return (
    <Editor
      code={code}
      language={language}
      disabled={true}
      theme={themes.github}
      className="read-only-editor"
    />
  );
}

// Multi-language editor with language switcher
function MultiLanguageEditor() {
  const [language, setLanguage] = useState('javascript');
  const [code, setCode] = useState({
    javascript: 'console.log("Hello from JavaScript");',
    typescript: 'console.log("Hello from TypeScript" as string);',
    jsx: '<div>Hello from JSX</div>',
    css: '.hello { color: blue; }'
  });
  
  const currentCode = code[language as keyof typeof code];
  
  const handleCodeChange = (newCode: string) => {
    setCode(prev => ({
      ...prev,
      [language]: newCode
    }));
  };
  
  return (
    <div className="multi-lang-editor">
      <div className="language-selector">
        {Object.keys(code).map(lang => (
          <button
            key={lang}
            onClick={() => setLanguage(lang)}
            className={language === lang ? 'active' : ''}
          >
            {lang}
          </button>
        ))}
      </div>
      <Editor
        code={currentCode}
        language={language}
        onChange={handleCodeChange}
        tabMode="indentation"
        theme={themes.vsDark}
      />
    </div>
  );
}

// Editor with custom tab behavior
function CustomTabEditor() {
  const [code, setCode] = useState('function example() {\n  // Add your code here\n}');
  const [tabMode, setTabMode] = useState<"focus" | "indentation">("indentation");
  
  return (
    <div>
      <div className="tab-controls">
        <label>
          <input
            type="radio"
            checked={tabMode === "indentation"}
            onChange={() => setTabMode("indentation")}
          />
          Tab for indentation
        </label>
        <label>
          <input
            type="radio"
            checked={tabMode === "focus"}
            onChange={() => setTabMode("focus")}
          />
          Tab to move focus
        </label>
      </div>
      <Editor
        code={code}
        language="javascript"
        tabMode={tabMode}
        onChange={setCode}
      />
    </div>
  );
}

// Editor with external save/load functionality
function FileEditor() {
  const [code, setCode] = useState('');
  const [filename, setFilename] = useState('untitled.js');
  const [language, setLanguage] = useState('javascript');
  
  const handleSave = () => {
    const blob = new Blob([code], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    link.click();
    URL.revokeObjectURL(url);
  };
  
  const handleLoad = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (e) => {
        const content = e.target?.result as string;
        setCode(content);
        setFilename(file.name);
        
        // Auto-detect language from file extension
        const ext = file.name.split('.').pop()?.toLowerCase();
        if (ext === 'ts' || ext === 'tsx') setLanguage('typescript');
        else if (ext === 'jsx') setLanguage('jsx');
        else if (ext === 'css') setLanguage('css');
        else if (ext === 'html') setLanguage('html');
        else setLanguage('javascript');
      };
      reader.readAsText(file);
    }
  };
  
  return (
    <div className="file-editor">
      <div className="file-controls">
        <input
          type="text"
          value={filename}
          onChange={(e) => setFilename(e.target.value)}
          placeholder="Filename"
        />
        <input
          type="file"
          onChange={handleLoad}
          accept=".js,.ts,.jsx,.tsx,.css,.html"
        />
        <button onClick={handleSave} disabled={!code}>
          Save
        </button>
      </div>
      
      <Editor
        code={code}
        language={language}
        onChange={setCode}
        theme={themes.nightOwl}
        style={{ height: 400 }}
        className="file-editor-content"
      />
      
      <div className="file-info">
        <span>Language: {language}</span>
        <span>Lines: {code.split('\n').length}</span>
        <span>Characters: {code.length}</span>
      </div>
    </div>
  );
}

// Code comparison editor
function CodeComparison({ 
  originalCode, 
  modifiedCode, 
  language 
}: { 
  originalCode: string; 
  modifiedCode: string; 
  language: string; 
}) {
  return (
    <div className="code-comparison">
      <div className="comparison-pane">
        <h4>Original</h4>
        <Editor
          code={originalCode}
          language={language}
          disabled={true}
          theme={themes.github}
        />
      </div>
      <div className="comparison-pane">
        <h4>Modified</h4>
        <Editor
          code={modifiedCode}
          language={language}
          disabled={true}
          theme={themes.github}
        />
      </div>
    </div>
  );
}

Customization Options

Themes

The editor supports all themes from prism-react-renderer:

import { themes, Prism } from "prism-react-renderer";

// Available themes
const availableThemes = {
  dracula: themes.dracula,
  duotoneDark: themes.duotoneDark,
  duotoneLight: themes.duotoneLight,
  github: themes.github,
  nightOwl: themes.nightOwl,
  nightOwlLight: themes.nightOwlLight,
  oceanicNext: themes.oceanicNext,
  okaidia: themes.okaidia,
  palenight: themes.palenight,
  prism: themes.prism,
  shadesOfPurple: themes.shadesOfPurple,
  synthwave84: themes.synthwave84,
  ultramin: themes.ultramin,
  vsDark: themes.vsDark,
  vsLight: themes.vsLight
};

// Theme selector component
function ThemeSelector({ 
  currentTheme, 
  onThemeChange 
}: { 
  currentTheme: string; 
  onThemeChange: (theme: any) => void; 
}) {
  return (
    <select 
      value={currentTheme} 
      onChange={(e) => onThemeChange(availableThemes[e.target.value as keyof typeof availableThemes])}
    >
      {Object.keys(availableThemes).map(themeName => (
        <option key={themeName} value={themeName}>
          {themeName}
        </option>
      ))}
    </select>
  );
}

Supported Languages

The editor supports syntax highlighting for many languages through Prism.js:

// Common language identifiers
const supportedLanguages = [
  'javascript', 'typescript', 'jsx', 'tsx',
  'css', 'scss', 'sass', 'less',
  'html', 'xml', 'svg',
  'json', 'yaml', 'toml',
  'markdown', 'mdx',
  'python', 'java', 'csharp', 'cpp', 'c',
  'php', 'ruby', 'go', 'rust',
  'sql', 'graphql',
  'bash', 'shell', 'powershell',
  'dockerfile', 'nginx',
  'regex'
];

// Language detection utility
function detectLanguage(filename: string): string {
  const ext = filename.split('.').pop()?.toLowerCase();
  
  const languageMap: Record<string, string> = {
    'js': 'javascript',
    'mjs': 'javascript',
    'ts': 'typescript',
    'tsx': 'tsx',
    'jsx': 'jsx',
    'css': 'css',
    'scss': 'scss',
    'sass': 'sass',
    'less': 'less',
    'html': 'html',
    'htm': 'html',
    'xml': 'xml',
    'svg': 'svg',
    'json': 'json',
    'yaml': 'yaml',
    'yml': 'yaml',
    'toml': 'toml',
    'md': 'markdown',
    'mdx': 'mdx',
    'py': 'python',
    'java': 'java',
    'cs': 'csharp',
    'cpp': 'cpp',
    'cc': 'cpp',
    'cxx': 'cpp',
    'c': 'c',
    'h': 'c',
    'php': 'php',
    'rb': 'ruby',
    'go': 'go',
    'rs': 'rust',
    'sql': 'sql',
    'graphql': 'graphql',
    'gql': 'graphql',
    'sh': 'bash',
    'bash': 'bash',
    'zsh': 'bash',
    'ps1': 'powershell',
    'dockerfile': 'dockerfile'
  };
  
  return languageMap[ext || ''] || 'text';
}

Integration Patterns

With Form Libraries

// React Hook Form integration
import { useController, Control } from "react-hook-form";

function CodeFormField({ 
  name, 
  control, 
  language,
  rules 
}: {
  name: string;
  control: Control;
  language: string;
  rules?: any;
}) {
  const { field, fieldState } = useController({
    name,
    control,
    rules
  });
  
  return (
    <div className="code-form-field">
      <Editor
        code={field.value || ''}
        language={language}
        onChange={field.onChange}
        className={fieldState.error ? 'error' : ''}
      />
      {fieldState.error && (
        <span className="error-message">{fieldState.error.message}</span>
      )}
    </div>
  );
}

// Formik integration
import { Field, FieldProps } from "formik";

function FormikCodeField({ 
  name, 
  language 
}: { 
  name: string; 
  language: string; 
}) {
  return (
    <Field name={name}>
      {({ field, meta }: FieldProps) => (
        <div className="formik-code-field">
          <Editor
            code={field.value || ''}
            language={language}
            onChange={(value) => field.onChange({ target: { name, value } })}
            className={meta.touched && meta.error ? 'error' : ''}
          />
          {meta.touched && meta.error && (
            <div className="error-message">{meta.error}</div>
          )}
        </div>
      )}
    </Field>
  );
}

With State Management

// Redux integration
import { useDispatch, useSelector } from "react-redux";

function ReduxCodeEditor({ editorId }: { editorId: string }) {
  const dispatch = useDispatch();
  const code = useSelector(state => state.editors[editorId]?.code || '');
  const language = useSelector(state => state.editors[editorId]?.language || 'javascript');
  
  const handleChange = (newCode: string) => {
    dispatch({
      type: 'UPDATE_EDITOR_CODE',
      payload: { editorId, code: newCode }
    });
  };
  
  return (
    <Editor
      code={code}
      language={language}
      onChange={handleChange}
    />
  );
}

// Zustand integration  
import { create } from "zustand";

const useEditorStore = create((set) => ({
  editors: {},
  updateEditor: (id: string, updates: any) =>
    set((state) => ({
      editors: {
        ...state.editors,
        [id]: { ...state.editors[id], ...updates }
      }
    }))
}));

function ZustandCodeEditor({ editorId }: { editorId: string }) {
  const editor = useEditorStore(state => state.editors[editorId] || {});
  const updateEditor = useEditorStore(state => state.updateEditor);
  
  return (
    <Editor
      code={editor.code || ''}
      language={editor.language || 'javascript'}
      onChange={(code) => updateEditor(editorId, { code })}
    />
  );
}

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