React CodeMirror 2 Wrapper
react-codemirror2 is a React component wrapper for CodeMirror 5, designed to embed a customizable code editor into React applications. It provides both `Controlled` and `UnControlled` components, catering to different state management paradigms within React. The current stable version is 9.0.1. Its release cadence is primarily tied to major React version updates for peer dependency compatibility and critical bug fixes, rather than a fixed schedule. A key differentiator is its explicit commitment to CodeMirror 5, meaning it does not support the fundamentally rewritten CodeMirror 6 API. Developers must manually import CodeMirror modes and themes. The library ships with TypeScript types and is widely used for creating syntax-highlighting text areas.
Common errors
-
Syntax highlighting missing or 'Mode not found for ...'
cause CodeMirror base CSS or specific language mode/theme files are not imported.fixEnsure `import 'codemirror/lib/codemirror.css';` is present, and `require('codemirror/mode/<your-mode>/<your-mode>.js');` is called for each mode used. Also check `options.mode`. -
Component does not update when 'value' prop changes in Controlled component.
cause The `onBeforeChange` callback is not correctly updating the component's state, preventing the editor from reflecting new `value` props.fixFor controlled components, ensure `onBeforeChange={(editor, data, value) => this.setState({ value })}` (or equivalent for functional components) is implemented to update the parent component's state. -
TypeError: Cannot read properties of undefined (reading 'mode') or similar CJS/ESM interop errors in modern bundlers.
cause An ESM-first bundler (e.g., Vite) is struggling to process the CommonJS output of `react-codemirror2` or its dependencies (like `codemirror`).fixFor Vite, configure `vite.config.js` with `optimizeDeps: { include: ['react-codemirror2', 'codemirror'] }` and consider using `@rollup/plugin-commonjs` if issues persist during build.
Warnings
- breaking Version 9.0.0 bumps the React peer dependency to `v19.x`. Ensure your project uses a compatible React version.
- breaking Version 8.0.0 bumps the React peer dependency to `v18.x`. Ensure your project uses a compatible React version.
- breaking Version 7.0.0 removed the `event` parameter from `copy`, `cut`, and `paste` event callbacks to align with `@types/codemirror`.
- gotcha This library is exclusively for CodeMirror 5 and is not compatible with CodeMirror 6. CodeMirror 6 is a complete rewrite with a different API.
- gotcha The package build process is CommonJS-based. Modern ESM-centric bundlers (like Vite) might require specific configuration to handle these modules correctly.
- gotcha CodeMirror modes, themes, and base CSS must be explicitly imported by the user. Failure to do so will result in missing features or styling.
Install
-
npm install react-codemirror2 -
yarn add react-codemirror2 -
pnpm add react-codemirror2
Imports
- UnControlled
import CodeMirror from 'react-codemirror2'
import { UnControlled as CodeMirror } from 'react-codemirror2' - Controlled
const CodeMirror = require('react-codemirror2').Controlledimport { Controlled as CodeMirror } from 'react-codemirror2' - CodeMirror assets (CSS, modes, themes)
import { xml } from 'codemirror/mode/xml/xml'import 'codemirror/lib/codemirror.css'; require('codemirror/mode/xml/xml');
Quickstart
import React from 'react';
import { Controlled as CodeMirror } from 'react-codemirror2';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/javascript/javascript';
interface MyEditorState {
value: string;
}
class MyControlledEditor extends React.Component<{}, MyEditorState> {
constructor(props: {}) {
super(props);
this.state = {
value: '<h1>Hello, controlled CodeMirror!</h1>\n<script>\n console.log("This is JavaScript");\n</script>',
};
}
render() {
const options = {
mode: 'xml',
theme: 'material',
lineNumbers: true,
indentUnit: 2,
tabSize: 2,
};
return (
<div>
<p>Edit the code below:</p>
<CodeMirror
value={this.state.value}
options={options}
onBeforeChange={(editor, data, value) => {
this.setState({ value });
}}
onChange={(editor, data, value) => {
console.log('Editor value changed:', value);
}}
/>
</div>
);
}
}
export default MyControlledEditor;