React
The editor inside a React component: mounted in useEffect, destroyed on unmount, its text held in React state so the rest of the app can read and replace it. It uses the plain API; there is no React wrapper to install.
import { useEffect, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import { createEditorState, mountEditor, type MarkdownEditor } from "@openmarkdown-dev/editor";
const INITIAL = "# Notes\n\nThis editor lives in a React component.\nType here and watch the counter below.\n";
/** Mounts once, destroys on unmount; `value` from outside replaces the document. */
function Editor({ value, onChange }: { value: string; onChange: (doc: string) => void }) {
const el = useRef<HTMLDivElement>(null);
const editor = useRef<MarkdownEditor | null>(null);
const latest = useRef(onChange);
latest.current = onChange;
useEffect(() => {
const ed = mountEditor(el.current!, createEditorState({ doc: value, host: { onChange: (doc) => latest.current(doc) } }));
editor.current = ed;
return () => {
ed.destroy();
editor.current = null;
};
}, []); // mount once; later values arrive through the effect below
useEffect(() => {
const ed = editor.current;
if (ed && ed.getDoc() !== value) ed.setDoc(value);
}, [value]);
return <div ref={el} className="editor" />;
}
function App() {
const [doc, setDoc] = useState(INITIAL);
return (
<>
<Editor value={doc} onChange={setDoc} />
<div className="bar">
<button type="button" onClick={() => setDoc(INITIAL)}>Reset</button>
<output id="count">{doc.length} characters</output>
</div>
</>
);
}
createRoot(document.getElementById("root")!).render(<App />);<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenMarkdown editor: React</title>
<style>
#root .editor { height: 340px; border: 1px solid #d9d6cf; border-radius: 8px; overflow: hidden; }
#root .editor .cm-editor { height: 100%; }
#root .bar { display: flex; gap: 12px; align-items: center; margin-top: 10px; font: 14px system-ui, sans-serif; }
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>{
"name": "openmarkdown-editor-example-react",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@openmarkdown-dev/editor": "0.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"typescript": "~5.8.3",
"vite": "^7.0.4",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6"
}
}