React wrapper component for CodeMirror 6 editor with TypeScript support and extensible theming system
—
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.
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>
);
}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>
);
}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>
);
}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