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

# Editor composables

> Composables for creating an editor and using it from your components.

`EmailEditor` and `EditorProvider` create the editor for you. These composables give your components
the editor they're rendered in, select a piece of its state, and let you share an editor you create
yourself with `useEditor` from `@tiptap/vue-3`.

## Import

```ts theme={null}
import {
  isDocumentVisuallyEmpty,
  provideCurrentEditor,
  useCurrentEditor,
  useEditorState,
} from '@vuemaildev/editor/core';
```

## Parameters

### useCurrentEditor

`useCurrentEditor()` takes no parameters. It gives your components the editor of the `EmailEditor` or
`EditorProvider` they are rendered in, or the one provided with `provideCurrentEditor`.

### useEditorState

**editor** `MaybeRefOrGetter\<Editor | null | undefined\>` *required*

The editor to select from, like the `editor` of `useCurrentEditor()`.

**selector** `(snapshot: \{ editor: Editor | null; transactionNumber: number \}) =\> T` *required*

Selects the state your component needs. It runs again after every change to the editor, and
reactive values it reads, like props, are tracked too.

**equalityFn** `(a: T, b: T | null) =\> boolean`

Decides whether a new selection is the same as the previous one, which keeps what depends on it from
updating. Deep equality by default.

### provideCurrentEditor

**editor** `ShallowRef\<Editor | undefined\>` *required*

The editor to make available to `useCurrentEditor()`, and so to the bubble menus, slash commands and
inspector, in the components rendered inside the one that calls it, like the editor that `useEditor`
from `@tiptap/vue-3` returns.

### isDocumentVisuallyEmpty

**doc** `Node` *required*

The document to check, like `editor.state.doc`.

## Return value

### useCurrentEditor

**editor** `ShallowRef\<Editor | undefined\>`

The TipTap editor instance. `undefined` until the editor is created, which happens once the
component that creates it is mounted, in the browser.

### useEditorState

A `ComputedRef<T>` with the latest selection, like `useEditorState` from `@tiptap/react`. The editor's
state isn't reactive, so a `computed` that reads it directly would never update.

### provideCurrentEditor

**editor** `ShallowRef\<Editor | undefined\>`

The context it provides, as `{ editor }`.

### isDocumentVisuallyEmpty

**isDocumentVisuallyEmpty** `boolean`

Whether the document is visually empty (only contains an empty paragraph).
Excludes `globalContent` nodes from the calculation.

## Collaboration support

> [!NOTE]
>
> `EditorProvider` and `useEditor` from `@tiptap/vue-3` don't detect collaboration extensions, like
> TipTap's `Collaboration` extension for Y.js. When you add one:
>
> * Leave `content` out, since the collaboration provider manages the content
> * Turn off the `UndoRedo` extension with `StarterKit.configure({ UndoRedo: false })`, since
>   collaboration extensions handle their own history

## Example

### Using the editor from your components

`useCurrentEditor()` works in any component rendered inside `EmailEditor` or `EditorProvider`:

```vue toolbar.vue theme={null}
<script setup lang="ts">
import { useCurrentEditor } from '@vuemaildev/editor/core';

const { editor } = useCurrentEditor();
</script>

<template>
  <div v-if="editor">
    <button type="button" @click="editor.chain().focus().toggleBold().run()">
      Bold
    </button>
  </div>
</template>
```

Select the state your component renders with `useEditorState`, so that it updates as the editor changes:

```vue toolbar.vue theme={null}
<script setup lang="ts">
import { useCurrentEditor, useEditorState } from '@vuemaildev/editor/core';

const { editor } = useCurrentEditor();

const state = useEditorState({
  editor,
  selector: ({ editor }) => ({
    isBold: editor?.isActive('bold') ?? false,
    isItalic: editor?.isActive('italic') ?? false,
  }),
});
</script>

<template>
  <div v-if="editor">
    <button
      type="button"
      :aria-pressed="state.isBold"
      @click="editor.chain().focus().toggleBold().run()"
    >
      Bold
    </button>
    <button
      type="button"
      :aria-pressed="state.isItalic"
      @click="editor.chain().focus().toggleItalic().run()"
    >
      Italic
    </button>
  </div>
</template>
```

### Creating the editor yourself

Use `useEditor` from `@tiptap/vue-3` directly when you need more control than `EditorProvider` offers,
like the editor instance in the same component that creates it, and make it available to the UI
components of `@vuemaildev/editor` with `provideCurrentEditor`:

```vue theme={null}
<script setup lang="ts">
import {
  isDocumentVisuallyEmpty,
  provideCurrentEditor,
  useEditorState,
} from '@vuemaildev/editor/core';
import { StarterKit } from '@vuemaildev/editor/extensions';
import { BubbleMenu } from '@vuemaildev/editor/ui';
import { EditorContent, useEditor } from '@tiptap/vue-3';

const editor = useEditor({
  extensions: [StarterKit],
  content: { type: 'doc', content: [] },
  onUpdate: ({ editor }) => {
    console.log('Content changed:', editor.getJSON());
  },
});

provideCurrentEditor(editor);

const isEditorEmpty = useEditorState({
  editor,
  selector: ({ editor }) => !editor || isDocumentVisuallyEmpty(editor.state.doc),
});
</script>

<template>
  <EditorContent :editor="editor" />
  <p v-if="isEditorEmpty">Start typing...</p>
  <BubbleMenu />
</template>
```

`useEditor` creates the editor when the component mounts and destroys it when it unmounts. It takes
any of [TipTap's editor options](https://tiptap.dev/docs/editor/api/editor#settings), and unlike
`EmailEditor`, it doesn't add `StarterKit` for you.

> [!TIP]
>
> For most use cases, [`EditorProvider`](https://vuemail.dev/docs/editor/getting-started#lower-level-setup) is simpler: it
> creates the editor, renders its content and provides it in one component.


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