CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-uiw--react-codemirror

React wrapper component for CodeMirror 6 editor with TypeScript support and extensible theming system

Pending
Overview
Eval results
Files

hook-api.mddocs/

Hook API

Hook-based interface for advanced integration scenarios, custom containers, and direct CodeMirror state management. The useCodeMirror hook provides lower-level access to CodeMirror functionality for complex use cases.

Capabilities

useCodeMirror Hook

Main hook for integrating CodeMirror with custom React components and containers.

/**
 * Hook for integrating CodeMirror with custom React components
 * @param props - Configuration options extending ReactCodeMirrorProps
 * @returns Object containing state, view, container, and their setters
 */
function useCodeMirror(props: UseCodeMirror): {
  state: EditorState | undefined;
  setState: React.Dispatch<React.SetStateAction<EditorState | undefined>>;
  view: EditorView | undefined;
  setView: React.Dispatch<React.SetStateAction<EditorView | undefined>>;
  container: HTMLDivElement | null | undefined;
  setContainer: React.Dispatch<React.SetStateAction<HTMLDivElement | null | undefined>>;
};

interface UseCodeMirror extends ReactCodeMirrorProps {
  /** Custom container element for the editor */
  container?: HTMLDivElement | null;
}

Usage Examples:

import React, { useEffect, useRef } from "react";
import { useCodeMirror } from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";

// Basic hook usage
function CustomEditor() {
  const editor = useRef<HTMLDivElement>(null);
  const { setContainer, view, state } = useCodeMirror({
    container: editor.current,
    value: "console.log('Hello World');",
    extensions: [javascript()],
    onChange: (value, viewUpdate) => {
      console.log('Value changed:', value);
    },
  });

  useEffect(() => {
    if (editor.current) {
      setContainer(editor.current);
    }
  }, [setContainer]);

  return <div ref={editor} />;
}

// Advanced hook usage with multiple containers
function MultiEditorContainer() {
  const editor1 = useRef<HTMLDivElement>(null);
  const editor2 = useRef<HTMLDivElement>(null);
  
  const { setContainer: setContainer1, view: view1 } = useCodeMirror({
    container: editor1.current,
    value: "// Editor 1",
    theme: "light",
  });
  
  const { setContainer: setContainer2, view: view2 } = useCodeMirror({
    container: editor2.current,
    value: "// Editor 2",
    theme: "dark",
  });

  useEffect(() => {
    if (editor1.current) setContainer1(editor1.current);
    if (editor2.current) setContainer2(editor2.current);
  }, [setContainer1, setContainer2]);

  const syncEditors = () => {
    if (view1 && view2) {
      const content = view1.state.doc.toString();
      view2.dispatch({
        changes: { from: 0, to: view2.state.doc.length, insert: content }
      });
    }
  };

  return (
    <div>
      <button onClick={syncEditors}>Sync Editors</button>
      <div style={{ display: 'flex' }}>
        <div ref={editor1} style={{ flex: 1 }} />
        <div ref={editor2} style={{ flex: 1 }} />
      </div>
    </div>
  );
}

Hook State Management

The hook returns state management objects for direct manipulation of the editor.

// Returned state objects
interface UseCodeMirrorReturn {
  /** Current editor state - undefined before initialization */
  state: EditorState | undefined;
  /** State setter for programmatic state updates */
  setState: React.Dispatch<React.SetStateAction<EditorState | undefined>>;
  /** Current editor view - undefined before initialization */
  view: EditorView | undefined;
  /** View setter for programmatic view updates */  
  setView: React.Dispatch<React.SetStateAction<EditorView | undefined>>;
  /** Current container element */
  container: HTMLDivElement | null | undefined;
  /** Container setter for attaching to DOM elements */
  setContainer: React.Dispatch<React.SetStateAction<HTMLDivElement | null | undefined>>;
}

Usage Example:

import React, { useEffect, useRef } from "react";
import { useCodeMirror } from "@uiw/react-codemirror";
import { EditorState } from "@codemirror/state";
import { javascript } from "@codemirror/lang-javascript";

function StateManagementExample() {
  const containerRef = useRef<HTMLDivElement>(null);
  const { 
    state, 
    setState, 
    view, 
    container, 
    setContainer 
  } = useCodeMirror({
    value: "// Initial code",
    extensions: [javascript()],
  });

  useEffect(() => {
    if (containerRef.current) {
      setContainer(containerRef.current);
    }
  }, [setContainer]);

  const resetEditor = () => {
    if (view) {
      const newState = EditorState.create({
        doc: "// Reset code",
        extensions: [javascript()],
      });
      setState(newState);
    }
  };

  const logState = () => {
    if (state) {
      console.log('Current state:', {
        content: state.doc.toString(),
        selection: state.selection.main,
        lineCount: state.doc.lines,
      });
    }
  };

  return (
    <div>
      <button onClick={resetEditor} disabled={!view}>
        Reset Editor
      </button>
      <button onClick={logState} disabled={!state}>
        Log State
      </button>
      <div ref={containerRef} />
    </div>
  );
}

External Change Tracking

The hook includes support for tracking external changes to prevent echo effects.

/**
 * Annotation for marking transactions as external changes
 * Used internally to prevent onChange callbacks for programmatic updates
 */
const ExternalChange: import('@codemirror/state').AnnotationType<boolean>;

Usage Example:

import React, { useRef } from "react";
import { useCodeMirror, ExternalChange } from "@uiw/react-codemirror";

function ExternalChangeExample() {
  const containerRef = useRef<HTMLDivElement>(null);
  const { view, setContainer } = useCodeMirror({
    container: containerRef.current,
    value: "// Original content",
    onChange: (value, viewUpdate) => {
      // This won't trigger for external changes
      console.log('User changed content to:', value);
    },
  });

  React.useEffect(() => {
    if (containerRef.current) {
      setContainer(containerRef.current);
    }
  }, [setContainer]);

  const updateExternally = () => {
    if (view) {
      // This update won't trigger onChange
      view.dispatch({
        changes: { 
          from: 0, 
          to: view.state.doc.length, 
          insert: "// Updated externally" 
        },
        annotations: [ExternalChange.of(true)],
      });
    }
  };

  return (
    <div>
      <button onClick={updateExternally}>Update Externally</button>
      <div ref={containerRef} />
    </div>
  );
}

Performance Optimization

The hook includes built-in performance optimizations for handling rapid updates.

Usage Example:

import React, { useRef, useMemo } from "react";
import { useCodeMirror } from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";

function OptimizedEditor() {
  const containerRef = useRef<HTMLDivElement>(null);
  
  // Memoize extensions to prevent unnecessary re-renders
  const extensions = useMemo(() => [javascript()], []);
  
  const { setContainer } = useCodeMirror({
    container: containerRef.current,
    value: "// Optimized editor",
    extensions, // Stable reference prevents re-creation
    onChange: React.useCallback((value) => {
      // Debounced internally by the hook
      console.log('Content updated:', value);
    }, []),
  });

  React.useEffect(() => {
    if (containerRef.current) {
      setContainer(containerRef.current);
    }
  }, [setContainer]);

  return <div ref={containerRef} />;
}

Install with Tessl CLI

npx tessl i tessl/npm-uiw--react-codemirror

docs

extensions.md

hook-api.md

index.md

main-component.md

themes.md

utilities.md

tile.json