Skip to main content

Import

import { useEditor } from '@react-email/editor/core';

Parameters

content
Content
required
Initial editor content. Accepts TipTap JSON or an HTML string. Ignored when collaboration extensions are detected (the collab provider manages content).
extensions
Extensions[]
default:"[]"
Additional extensions to load. StarterKit is always included automatically, so you only need to pass extra extensions (e.g., EmailTheming, custom nodes).
onUpdate
(editor: Editor, transaction: { getMeta }) => void
Called on every content change. The transaction object provides metadata about the change.
onPaste
(payload: string | File, view: EditorView) => boolean
Custom paste handler. Return true to prevent default paste behavior.
onUploadImage
(file: File, view: EditorView, pos: number) => void
Called when an image is pasted or dropped. Use this to upload the image and insert the resulting URL.
onReady
(editor: Editor | null) => void
Called once when the editor has finished initializing.
editable
boolean
default:"true"
Toggle read-only mode. When false, the editor content cannot be modified.
Any additional TipTap UseEditorOptions are also accepted and forwarded to the underlying TipTap editor.

Return value

editor
Editor | null
The TipTap editor instance. null until the editor is mounted.
isEditorEmpty
boolean
Whether the document is visually empty (only contains an empty paragraph). Excludes globalContent nodes from the calculation.
extensions
Extensions[]
The effective extensions array, including StarterKit and UndoRedo (unless collaborative).
contentError
Error | null
Content validation error, if the provided content is invalid for the current schema. When set, the editor is automatically made read-only.
isCollaborative
boolean
Whether collaboration extensions (Liveblocks, Y.js) were detected in the extensions array.

Collaboration support

When collaboration extensions are detected (liveblocksExtension or collaboration), the hook automatically:
  • Ignores the content parameter (content is managed by the collab provider)
  • Excludes the UndoRedo extension (collab extensions handle their own history)

Example

Use useEditor directly when you need more control than EditorProvider offers:
import { useEditor } from '@react-email/editor/core';

export function MyEditor() {
  const { editor, isEditorEmpty, contentError } = useEditor({
    content: { type: 'doc', content: [] },
    onUpdate: (editor) => {
      console.log('Content changed:', editor.getJSON());
    },
    onReady: (editor) => {
      console.log('Editor ready');
    },
  });

  if (contentError) {
    return <div>Error: {contentError.message}</div>;
  }

  if (!editor) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <div ref={(el) => el && editor.setOptions({ element: el })} />
      {isEditorEmpty && <p>Start typing...</p>}
    </div>
  );
}
For most use cases, EditorProvider from @tiptap/react is simpler. Use useEditor when you need direct access to the editor instance before rendering, or when integrating with complex state management.