Typix LogoTypix
Core

useTypixEditor & TypixEditor

The hook that creates the editor, the instance it returns, and how to use both

useTypixEditor is the v2 entry point. It creates a TypixEditor instance — a thin, typed wrapper around Lexical — and tears it down on unmount.

import { useTypixEditor, defaultTheme } from "@typix-editor/react";
import { StarterKit } from "@typix-editor/extension-starter-kit";

const extensions = [StarterKit()];

function MyComponent() {
  const editor = useTypixEditor({
    extensions,
    theme: defaultTheme,
    namespace: "my-editor",
  });

  // editor is a TypixEditor (or null if immediatelyRender: false on first render)
}

Options

extensions (required)

The list of extensions to register. Each item is either:

  • A bare extension constructor: StarterKit(), DragDropPasteExtension()
  • A configured tuple from configExtension:
    configExtension(MentionExtension, { trigger: "@" })

Each extension augments the global TypixCommands<R> type, so editor.chain() is fully typed for the extensions you've installed.

theme

CSS-class theme passed to Lexical. Use defaultTheme unless you have a reason to override.

import { defaultTheme } from "@typix-editor/react";

theme: defaultTheme,

namespace

Unique string identifying this editor instance. Required when multiple editors render on the same page.

namespace: "comment-editor-42",

content

Initial content as serialized state (the shape editor.getJSON() returns).

content: {
  root: {
    children: [/* … */],
    direction: null,
    format: "",
    indent: 0,
    type: "root",
    version: 1,
  },
},

editable

true (default) or false. Read-only when false.

autofocus

"start", "end", true, or false. Defaults to false.

immediatelyRender

When false, defers editor creation until after the first render. Use this for SSR safety. Add a null guard:

const editor = useTypixEditor({
  extensions,
  immediatelyRender: false,
});

if (!editor) return null;

Lifecycle callbacks

All optional. Each receives an object with the editor instance.

CallbackFires when
onBeforeCreate({ options })Before the editor is constructed; mutate options here.
onCreate({ editor })Editor is ready to use.
onUpdate({ editor })Content changed.
onSelectionUpdate({ editor })Selection moved.
onFocus({ editor })Editor got focus.
onBlur({ editor })Editor lost focus.
onDestroy()Editor torn down.
onError(error)Lexical or extension threw.

Each fires synchronously during Lexical's reconciliation. Heavy work (network, expensive serialization) should be debounced.

The TypixEditor instance

Fluent commands

editor.chain().toggleBold().toggleItalic().run();

editor.chain()
  .focus()
  .insertText("Hello")
  .toggleBold()
  .run();

Each method returns the chain for further calls. .run() executes the batched commands inside a single Lexical update — atomic.

can() checks

if (editor.can().toggleBold()) {
  editor.chain().toggleBold().run();
}

Same fluent API; returns booleans instead of executing.

Direct Lexical access

editor.lexical.dispatchCommand(SOME_LEXICAL_COMMAND, payload);

editor.lexical.update(() => {
  const root = $getRoot();
  // ...standard Lexical $-API
});

editor.lexical is the underlying LexicalEditor. Use it for raw Lexical APIs not yet exposed through editor.chain().

Serialization

const json = editor.getJSON();   // SerializedContent (load with setContent)
const html = editor.getHTML();   // string
const text = editor.getText();   // string

editor.setContent(json);         // load from JSON

State queries

editor.isEmpty();        // boolean
editor.isEditable();     // boolean
editor.setEditable(v);   // toggle
editor.isDestroyed();    // true after unmount

Events

const off = editor.emitter.on("contentChange", () => {
  console.log("content changed");
});

// Later:
off();

Available events: contentChange, selectionChange, focus, blur, create, destroy, transaction.

Identity

editor.id;          // auto-generated unique id
editor.namespace;   // the namespace you passed

Sharing across components

Wrap children in TypixEditorContext.Provider so they can access the editor via useCurrentTypixEditor():

import { TypixEditorContext } from "@typix-editor/react";

return (
  <TypixEditorContext.Provider value={{ editor }}>
    <EditorToolbar />
    <EditorContent editor={editor} placeholder="…" />
    <FloatingLinkUI />
  </TypixEditorContext.Provider>
);

In any descendant:

import { useCurrentTypixEditor } from "@typix-editor/react";

function CustomButton() {
  const editor = useCurrentTypixEditor();
  return (
    <button onClick={() => editor.chain().toggleBold().run()}>Bold</button>
  );
}

Reactive state in components

Use useEditorState to subscribe to a Lexical state value. Re-renders only when the selector's value changes (deep equality by default).

import { useEditorState } from "@typix-editor/react";

function WordCount() {
  const count = useEditorState((state) => state.read(() => $getRoot().getTextContent().split(/\s+/).length));
  return <span>{count} words</span>;
}

Next

On this page