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.

Edit this page on GitHub