Frameworks
React
npm i @matrajs/react The engine comes with it · there is no second package, and nothing third-party behind it.
An editor
import { starterKit } from '@matrajs/core'
import { EditorContent, useEditor } from '@matrajs/react'
export function Editor() {
const editor = useEditor({
extensions: starterKit,
content: '<p>Hello.</p>',
})
return <EditorContent editor={editor} />
} The editor is created once and destroyed on unmount. Options are read once · changing them later does not recreate it, because tearing down a live document on a prop change loses whatever the person was writing. Use commands instead.
A toolbar that stays honest
Commands mutate the document; React does not hear about that on its own, so a naive
toolbar's active states go stale the moment the caret moves.
useEditorState subscribes to both changes and selection changes.
import { useEditorState } from '@matrajs/react'
function BoldButton({ editor }) {
const active = useEditorState(editor, (e) => e.isActive('bold'))
return (
<button
aria-pressed={active}
onMouseDown={(event) => {
event.preventDefault() // keep the caret in the editor
editor.commands.toggleBold()
}}
>
Bold
</button>
)
} onMouseDown with preventDefault, not
onClick. A click moves focus to the button first, which collapses the selection
· so the command runs against a caret rather than the words you highlighted.
Buttons that know when they cannot
can is the same command asking rather than doing. A button reads it to be
disabled instead of looking enabled and doing nothing when pressed — the caret is in
a code block, or the selection cannot hold that mark, and the answer is available before the user
finds out by pressing.
const canBold = useEditorState(editor, (e) => e.can.toggleBold())
<button disabled={!canBold} …>Bold</button> Several changes, one undo
editor.batch((c) => {
c.toggleBold()
c.toggleItalic()
})
One history step, and it rolls back entirely if any command in it returns false.
Saving
useEffect(() => editor.on('change', () => save(editor.getJSON())), [editor]) on returns its own unsubscribe function, so it works as an effect cleanup directly.
Focus
const focused = useEditorFocus(editor) True while the editor has DOM focus · useful for showing a toolbar only while the caret is actually in the document.