Frameworks
Solid
npm i @matrajs/core @matrajs/solid An editor
import { starterKit } from '@matrajs/core'
import { createMatra } from '@matrajs/solid'
export function Editor() {
const { mount } = createMatra({
extensions: starterKit,
content: '<p>Hello.</p>',
})
return <div ref={mount} />
} createMatra() gives you the editor, a mount ref, and a
state accessor. It registers its own onCleanup, so disposing the
owner destroys the editor and leaves the element safe to reuse.
The editor is created immediately, not in onMount. Commands,
getJSON() and getText() all work before anything is on screen — which
is what SSR needs, and what a test needs in order not to render at all.
A toolbar that tells the truth
import { starterKit } from '@matrajs/core'
import { createMatra } from '@matrajs/solid'
export function Editor() {
const { editor, mount, state } = createMatra({ extensions: starterKit })
return (
<>
<div class="toolbar">
<button
onClick={() => editor.commands.toggleBold()}
aria-pressed={state().isActive('bold')}
>Bold</button>
<button
onClick={() => editor.commands.toggleHeading(2)}
aria-pressed={state().isActive('heading', { level: 2 })}
>H2</button>
<span>{state().getText().length} characters</span>
</div>
<div ref={mount} />
</>
)
}
Only the expressions that call state() re-run. Solid's reactivity is not a render
loop, so a document change does not re-render the component — it updates the two attributes and
the one text node that asked.
Why the accessor returns the editor
state() hands back the editor itself rather than a snapshot. A toolbar asks
isActive at render time, and cloning a document to answer that would be the expensive
way to do nothing.
What changes behind it is a version counter, because the editor is one object whose identity never changes — a signal holding it directly would never notify. That is the whole of the binding, and it is the piece worth not writing twice.
Reaching the editor from elsewhere
const EditorContext = createContext()
// in the parent
<EditorContext.Provider value={editor}>{props.children}</EditorContext.Provider>
// in any child
const editor = useContext(EditorContext) Styling
Matra ships no appearance. Styling covers what you style yourself, which extensions bring a stylesheet, and why a slash menu is state rather than a menu.