> ## Documentation Index
> Fetch the complete documentation index at: https://react.email/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# useEditor

> Hook for creating and managing an editor instance.

## Import

```tsx theme={"theme":{"light":"github-light","dark":"vesper"}}
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`](https://tiptap.dev/docs/editor/api/editor#settings)
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

> [!NOTE]
>
> 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:

```tsx theme={"theme":{"light":"github-light","dark":"vesper"}}
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>
  );
}
```

> [!TIP]
>
> 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.