+
+
+
+
diff --git a/docs/app/composables/useNavigation.ts b/docs/app/composables/useNavigation.ts
index 47662b6418..817ee6c13b 100644
--- a/docs/app/composables/useNavigation.ts
+++ b/docs/app/composables/useNavigation.ts
@@ -29,7 +29,10 @@ const categories = {
title: 'Dashboard'
}, {
id: 'chat',
- title: 'Chat'
+ title: 'AI Chat'
+ }, {
+ id: 'editor',
+ title: 'Editor'
}, {
id: 'content',
title: 'Content',
diff --git a/docs/app/layouts/docs.vue b/docs/app/layouts/docs.vue
index 93b0ee2493..518f79f496 100644
--- a/docs/app/layouts/docs.vue
+++ b/docs/app/layouts/docs.vue
@@ -32,7 +32,7 @@ watch(() => route.path, () => {
defineShortcuts({
'/': {
- usingInput: true,
+ usingInput: false,
handler: () => {
input.value?.inputRef?.focus()
}
diff --git a/docs/content.config.ts b/docs/content.config.ts
index 31619ce168..6491d6ca49 100644
--- a/docs/content.config.ts
+++ b/docs/content.config.ts
@@ -63,7 +63,7 @@ export const collections = {
include: 'docs/**/*'
}],
schema: z.object({
- category: z.enum(['layout', 'form', 'element', 'navigation', 'data', 'overlay', 'dashboard', 'page', 'ai', 'color-mode', 'i18n']).optional(),
+ category: z.enum(['layout', 'form', 'element', 'navigation', 'data', 'overlay', 'dashboard', 'page', 'chat', 'editor', 'color-mode', 'i18n']).optional(),
framework: z.enum(['nuxt', 'vue']).optional(),
navigation: z.object({
title: z.string().optional()
diff --git a/docs/content/docs/2.components/0.index.md b/docs/content/docs/2.components/0.index.md
index 759ec7f423..f0010d01f6 100644
--- a/docs/content/docs/2.components/0.index.md
+++ b/docs/content/docs/2.components/0.index.md
@@ -62,7 +62,7 @@ Specialized components for building dynamic dashboards with resizable panels, co
Check out the **Dashboard template** on GitHub for a real-life example.
::
-## Chat
+## AI Chat
Components for building conversational interfaces and chatbots, powered by the **[Vercel AI SDK](https://sdk.vercel.ai)**.
@@ -72,6 +72,12 @@ Components for building conversational interfaces and chatbots, powered by the *
Check out the **AI Chat template** on GitHub for a real-life example.
::
+## Editor
+
+Components for building a rich text editor with support for markdown, HTML, and JSON content types, powered by **[TipTap](https://tiptap.dev)**.
+
+:components-list{category="editor"}
+
## Content
Components that integrate with [Content](/docs/getting-started/integrations/content) for documentation sites, including table of contents, search, navigation trees, and surrounding page links.
diff --git a/docs/content/docs/2.components/editor-drag-handle.md b/docs/content/docs/2.components/editor-drag-handle.md
new file mode 100644
index 0000000000..9fedf9cd3d
--- /dev/null
+++ b/docs/content/docs/2.components/editor-drag-handle.md
@@ -0,0 +1,121 @@
+---
+title: EditorDragHandle
+description: A draggable handle for reordering and selecting blocks in the editor.
+category: editor
+links:
+ - label: GitHub
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/EditorDragHandle.vue
+navigation.badge: Soon
+---
+
+## Usage
+
+The EditorDragHandle component wraps TipTap's [Drag Handle extension](https://tiptap.dev/docs/editor/extensions/functionality/drag-handle) to provide drag-and-drop functionality for editor blocks. It must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+
+::component-example
+---
+elevated: true
+name: 'editor-drag-handle-example'
+class: 'p-8'
+---
+::
+
+::note
+The EditorDragHandle component extends the [Button](/docs/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc.
+::
+
+### Icon
+
+Use the `icon` prop to customize the drag handle icon.
+
+```vue
+
+
+
+```
+
+::framework-only
+#nuxt
+:::tip{to="/docs/getting-started/integrations/icons/nuxt#theme"}
+You can customize this icon globally in your `app.config.ts` under `ui.icons.drag` key.
+:::
+
+#vue
+:::tip{to="/docs/getting-started/integrations/icons/vue#theme"}
+You can customize this icon globally in your `vite.config.ts` under `ui.icons.drag` key.
+:::
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using [Floating UI options](https://floating-ui.com/docs/computeposition#options).
+
+::note
+The offset is automatically calculated to center the handle for small blocks and align it to the top for taller blocks.
+::
+
+```vue
+
+
+
+```
+
+## Examples
+
+### With dropdown menu
+
+Use the default slot to add a [DropdownMenu](/docs/components/dropdown-menu) with block-level actions and listen to the `@node-change` event to track the currently hovered node.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-drag-handle-dropdown-menu-example'
+class: 'p-8'
+---
+::
+
+::note
+This example uses the `mapEditorItems` utility from `@nuxt/ui/utils/editor` to automatically map handler kinds (like `duplicate`, `delete`, `moveUp`, etc.) to their corresponding editor commands with proper state management.
+::
+
+### With suggestion menu
+
+Use the default slot to add a button that triggers the `suggestion` handler and open the [EditorSuggestionMenu](/docs/components/editor-suggestion-menu) component.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-drag-handle-suggestion-menu-example'
+class: 'p-8'
+---
+::
+
+## API
+
+### Props
+
+:component-props
+
+### Slots
+
+:component-slots
+
+### Emits
+
+:component-emits
+
+## Theme
+
+:component-theme
+
+## Changelog
+
+:component-changelog
diff --git a/docs/content/docs/2.components/editor-emoji-menu.md b/docs/content/docs/2.components/editor-emoji-menu.md
new file mode 100644
index 0000000000..a521aa07cf
--- /dev/null
+++ b/docs/content/docs/2.components/editor-emoji-menu.md
@@ -0,0 +1,98 @@
+---
+title: EditorEmojiMenu
+description: "An emoji picker menu that displays emoji suggestions when typing the : character in the editor."
+category: editor
+links:
+ - label: GitHub
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/EditorEmojiMenu.vue
+navigation.badge: Soon
+---
+
+## Usage
+
+The EditorEmojiMenu component is used to display a menu of emoji suggestions when typing the `:` character in the editor. Emojis are inserted as text characters or custom nodes depending on the extension configuration. It must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+
+Type `:` followed by an emoji name to search and insert emojis.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-emoji-menu-example'
+class: 'p-8'
+---
+::
+
+::note
+The menu filters items as you type and supports keyboard navigation (arrow keys, enter to select, escape to close).
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/nodes/emoji" target="_blank"}
+Learn more about the Emoji extension in the TipTap documentation.
+::
+
+### Items
+
+Use the `items` prop as an array of objects with the following properties:
+
+- `name: string`{lang="ts-type"}
+- `emoji: string`{lang="ts-type"}
+- `shortcodes?: string[]`{lang="ts-type"}
+- `tags?: string[]`{lang="ts-type"}
+- `group?: string`{lang="ts-type"}
+- `fallbackImage?: string`{lang="ts-type"}
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-emoji-menu-items-example'
+class: 'p-8'
+---
+::
+
+::tip
+Use the `gitHubEmojis` export from `@tiptap/extension-emoji` for a comprehensive emoji set with over 1800 emojis.
+::
+
+### Char
+
+Use the `char` prop to change the trigger character. Defaults to `:`{lang="ts-type"}.
+
+```vue
+
+
+
+```
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using [Floating UI options](https://floating-ui.com/docs/computeposition#options).
+
+```vue
+
+
+
+```
+
+## API
+
+### Props
+
+:component-props
+
+## Theme
+
+:component-theme
+
+## Changelog
+
+:component-changelog
diff --git a/docs/content/docs/2.components/editor-mention-menu.md b/docs/content/docs/2.components/editor-mention-menu.md
new file mode 100644
index 0000000000..f59302791a
--- /dev/null
+++ b/docs/content/docs/2.components/editor-mention-menu.md
@@ -0,0 +1,101 @@
+---
+title: EditorMentionMenu
+description: A mention menu that displays user suggestions when typing the @ character in the editor.
+category: editor
+links:
+ - label: GitHub
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/EditorMentionMenu.vue
+navigation.badge: Soon
+---
+
+## Usage
+
+The EditorMentionMenu component is used to display a menu of user suggestions when typing the `@` character in the editor. Mentions are inserted as inline elements that can be styled and linked. It must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+
+Type `@` followed by a name to search and insert mentions.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-mention-menu-example'
+class: 'p-8'
+---
+::
+
+::note
+The menu filters items as you type and supports keyboard navigation (arrow keys, enter to select, escape to close).
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/nodes/mention" target="_blank"}
+Learn more about the Mention extension in the TipTap documentation.
+::
+
+### Items
+
+Use the `items` prop as an array of objects with the following properties:
+
+- `label: string`{lang="ts-type"}
+- `avatar?: AvatarProps`{lang="ts-type"}
+- `icon?: string`{lang="ts-type"}
+- `description?: string`{lang="ts-type"}
+- `disabled?: boolean`{lang="ts-type"}
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-mention-menu-items-example'
+class: 'p-8'
+---
+::
+
+::tip
+Use avatars for user mentions and icons for entities like teams, channels, or tags.
+::
+
+### Char
+
+Use the `char` prop to change the trigger character. Defaults to `@`{lang="ts-type"}.
+
+```vue
+
+
+
+```
+
+::tip
+Use `#` for channels or tags, `+` for adding team members, etc.
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using [Floating UI options](https://floating-ui.com/docs/computeposition#options).
+
+```vue
+
+
+
+```
+
+## API
+
+### Props
+
+:component-props
+
+## Theme
+
+:component-theme
+
+## Changelog
+
+:component-changelog
diff --git a/docs/content/docs/2.components/editor-suggestion-menu.md b/docs/content/docs/2.components/editor-suggestion-menu.md
new file mode 100644
index 0000000000..fb6bc8cb23
--- /dev/null
+++ b/docs/content/docs/2.components/editor-suggestion-menu.md
@@ -0,0 +1,115 @@
+---
+title: EditorSuggestionMenu
+description: A command menu that displays formatting and action suggestions when typing the / character in the editor.
+category: editor
+links:
+ - label: GitHub
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/EditorSuggestionMenu.vue
+navigation.badge: Soon
+---
+
+## Usage
+
+The EditorSuggestionMenu component is used to display a menu of formatting and action suggestions when typing a trigger character in the editor. It must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+
+Type `/` in the editor to open the suggestion menu.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-suggestion-menu-example'
+class: 'p-8'
+---
+::
+
+::note
+The menu supports keyboard navigation (arrow keys, enter to select, escape to close) and filters items as you type.
+::
+
+### Items
+
+Use the `items` prop as an array of objects with the following properties:
+
+- `kind?: "textAlign" | "heading" | "link" | "image" | "blockquote" | "bulletList" | "orderedList" | "codeBlock" | "horizontalRule" | "paragraph" | "clearFormatting" | "duplicate" | "delete" | "moveUp" | "moveDown" | "suggestion" | "mention" | "emoji"`{lang="ts-type"}
+- `label?: string`{lang="ts-type"}
+- `description?: string`{lang="ts-type"}
+- `icon?: string`{lang="ts-type"}
+- `type?: "label" | "separator"`{lang="ts-type"}
+- `disabled?: boolean`{lang="ts-type"}
+
+::note{to="/docs/components/editor#handlers"}
+The `kind` property references a handler defined in the [Editor](/docs/components/editor) component. Handlers wrap TipTap commands and manage their state (active, disabled, etc.). The Editor provides default handlers for common actions (`heading`, `blockquote`, `bulletList`, etc.), but you can add custom handlers using the `handlers` prop on the Editor component.
+::
+
+::tip
+When using the `kind` property for editor-specific actions, additional properties may be required:
+
+- For `kind: "textAlign"`{lang="ts-type"}: `align: "left" | "center" | "right" | "justify"`{lang="ts-type"}
+- For `kind: "heading"`{lang="ts-type"}: `level: 1 | 2 | 3 | 4 | 5 | 6`{lang="ts-type"}
+- For `kind: "link"`{lang="ts-type"}: `href?: string`{lang="ts-type"}
+- For `kind: "image"`{lang="ts-type"}: `src?: string`{lang="ts-type"}
+::
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-suggestion-menu-items-example'
+class: 'p-8'
+---
+::
+
+::note
+You can also pass an array of arrays to the `items` prop to create separated groups of items.
+::
+
+::tip
+Use `type: 'label'` for section headers and `type: 'separator'` for visual dividers to organize commands into logical groups for better discoverability.
+::
+
+### Char
+
+Use the `char` prop to change the trigger character. Defaults to `/`{lang="ts-type"}.
+
+```vue
+
+
+
+```
+
+::tip
+Common alternatives include `>` for block commands or `+` for insertions.
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using [Floating UI options](https://floating-ui.com/docs/computeposition#options).
+
+```vue
+
+
+
+```
+
+## API
+
+### Props
+
+:component-props
+
+## Theme
+
+:component-theme
+
+## Changelog
+
+:component-changelog
diff --git a/docs/content/docs/2.components/editor-toolbar.md b/docs/content/docs/2.components/editor-toolbar.md
new file mode 100644
index 0000000000..015fd5083d
--- /dev/null
+++ b/docs/content/docs/2.components/editor-toolbar.md
@@ -0,0 +1,270 @@
+---
+title: EditorToolbar
+description: A customizable toolbar for editor actions that can be displayed as fixed, bubble, or floating menu.
+category: editor
+links:
+ - label: GitHub
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/EditorToolbar.vue
+navigation.badge: Soon
+---
+
+## Usage
+
+The EditorToolbar component is used to display a toolbar of formatting buttons that automatically sync their active state with the editor content. It must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-example'
+class: 'p-8'
+---
+::
+
+### Items
+
+Use the `items` prop as an array of objects with the following properties:
+
+- `label?: string`{lang="ts-type"}
+- `icon?: string`{lang="ts-type"}
+- `color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"`{lang="ts-type"}
+- `variant?: "solid" | "outline" | "soft" | "ghost" | "link" | "subtle"`{lang="ts-type"}
+- `size?: "xs" | "sm" | "md" | "lg" | "xl"`{lang="ts-type"}
+- `kind?: "mark" | "textAlign" | "heading" | "link" | "image" | "blockquote" | "bulletList" | "orderedList" | "codeBlock" | "horizontalRule" | "paragraph" | "undo" | "redo" | "clearFormatting" | "duplicate" | "delete" | "moveUp" | "moveDown" | "suggestion" | "mention" | "emoji"`{lang="ts-type"}
+- `disabled?: boolean`{lang="ts-type"}
+- `loading?: boolean`{lang="ts-type"}
+- `active?: boolean`{lang="ts-type"}
+- [`slot?: string`{lang="ts-type"}](#with-custom-button-slots)
+- `onClick?: (e: MouseEvent) => void`{lang="ts-type"}
+- [`items?: EditorToolbarItem[] | EditorToolbarItem[][]`{lang="ts-type"}](#with-dropdown-menus)
+- `class?: any`{lang="ts-type"}
+
+::note{to="/docs/components/editor#handlers"}
+The `kind` property references a handler defined in the [Editor](/docs/components/editor) component. Handlers wrap TipTap commands and manage their state (active, disabled, etc.). The Editor provides default handlers for common actions (`mark`, `heading`, `link`, etc.), but you can add custom handlers using the `handlers` prop on the Editor component.
+::
+
+::tip
+When using the `kind` property for editor-specific actions, additional properties may be required:
+
+- For `kind: "mark"`{lang="ts-type"}: `mark: "bold" | "italic" | "strike" | "code" | "underline"`{lang="ts-type"}
+- For `kind: "textAlign"`{lang="ts-type"}: `align: "left" | "center" | "right" | "justify"`{lang="ts-type"}
+- For `kind: "heading"`{lang="ts-type"}: `level: 1 | 2 | 3 | 4 | 5 | 6`{lang="ts-type"}
+- For `kind: "link"`{lang="ts-type"}: `href?: string`{lang="ts-type"}
+- For `kind: "image"`{lang="ts-type"}: `src?: string`{lang="ts-type"}
+- For `kind: "duplicate" | "delete" | "moveUp" | "moveDown"`{lang="ts-type"}: `pos: number`{lang="ts-type"}
+- For `kind: "clearFormatting" | "suggestion"`{lang="ts-type"}: `pos?: number`{lang="ts-type"}
+::
+
+You can pass any property from the [Button](/docs/components/button#props) component such as `color`, `variant`, `size`, etc. but also `active-color` and `active-variant` as items with a `kind` property automatically sync their active state with the editor.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-items-example'
+class: 'p-8 h-176 overflow-y-auto'
+---
+::
+
+::note
+You can also pass an array of arrays to the `items` prop to create separated groups of items.
+::
+
+::tip
+Each item can take an `items` array of objects with the same properties as the `items` prop to create a [DropdownMenu](/docs/components/dropdown-menu).
+::
+
+### Layout
+
+Use the `layout` prop to change how the toolbar is displayed. Defaults to `fixed`{lang="ts-type"}.
+
+| Layout | Description |
+| ------ | ----------- |
+| `fixed`{lang="ts-type"} | Always visible toolbar, typically placed above the editor |
+| `bubble`{lang="ts-type"} | Contextual menu that appears when text is selected |
+| `floating`{lang="ts-type"} | Menu that appears on empty lines or blocks |
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-layout-example'
+class: 'p-8'
+---
+::
+
+::callout{icon="i-custom-tiptap"}
+The bubble and floating layouts use TipTap's [BubbleMenu](https://tiptap.dev/docs/editor/extensions/functionality/bubble-menu) and [FloatingMenu](https://tiptap.dev/docs/editor/extensions/functionality/floating-menu) extensions. Check the TipTap documentation for advanced positioning options.
+::
+
+### Should show
+
+When using `bubble`{lang="ts-type"} or `floating`{lang="ts-type"} layouts, use the `should-show` prop to control when the toolbar appears. This function receives context about the editor state and returns a boolean.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-should-show-example'
+class: 'p-8'
+---
+::
+
+### Options
+
+When using `bubble`{lang="ts-type"} or `floating`{lang="ts-type"} layouts, use the `options` prop to customize the positioning behavior using [Floating UI options](https://floating-ui.com/docs/computeposition#options).
+
+```vue
+
+
+
+```
+
+### Color and variant
+
+Use the `color` and `variant` props to customize the toolbar button styles.
+
+::component-code
+---
+elevated: true
+prettier: true
+collapse: true
+ignore:
+ - editor
+ - items
+ - class
+external:
+ - editor
+ - items
+class: 'p-8'
+props:
+ color: 'primary'
+ variant: 'soft'
+ editor: {}
+ items: [[{ kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' }]]
+---
+::
+
+Use the `active-color` and `active-variant` props to customize the active state styling.
+
+::component-code
+---
+elevated: true
+prettier: true
+collapse: true
+ignore:
+ - editor
+ - items
+ - class
+external:
+ - editor
+ - items
+class: 'p-8'
+props:
+ activeColor: 'success'
+ activeVariant: 'solid'
+ editor: {}
+ items: [[{ kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' }]]
+---
+::
+
+### Size
+
+Use the `size` prop to change the size of toolbar buttons. Defaults to `sm`.
+
+::component-code
+---
+elevated: true
+prettier: true
+collapse: true
+ignore:
+ - editor
+ - items
+ - class
+external:
+ - editor
+ - items
+class: 'p-8'
+items:
+ size:
+ - xs
+ - sm
+ - md
+ - lg
+props:
+ size: 'md'
+ editor: {}
+ items: [[{ kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' }]]
+---
+::
+
+## Examples
+
+### With custom button slots
+
+You can use slots to customize specific toolbar items.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-custom-slot-example'
+class: 'p-8'
+---
+::
+
+Use the `slot` property on an item to specify a named slot, then provide a matching template:
+
+```vue
+
+
+
+
+
+```
+
+### Image-specific toolbar
+
+Create context-specific toolbars that appear only for certain node types.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-image-example'
+class: 'min-h-96'
+---
+::
+
+## API
+
+### Props
+
+:component-props
+
+### Slots
+
+:component-slots
+
+### Emits
+
+:component-emits
+
+## Theme
+
+:component-theme
+
+## Changelog
+
+:component-changelog
diff --git a/docs/content/docs/2.components/editor.md b/docs/content/docs/2.components/editor.md
new file mode 100644
index 0000000000..9becb29b55
--- /dev/null
+++ b/docs/content/docs/2.components/editor.md
@@ -0,0 +1,475 @@
+---
+title: Editor
+description: A rich text editor component based on TipTap with support for markdown, HTML, and JSON content types.
+category: editor
+links:
+ - label: TipTap
+ icon: i-custom-tiptap
+ to: https://tiptap.dev/
+ - label: GitHub
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/Editor.vue
+navigation.badge: Soon
+---
+
+## Usage
+
+The Editor component provides a powerful rich text editing experience built on TipTap. It supports multiple content formats (JSON, HTML, Markdown), customizable toolbars, drag-and-drop block reordering, slash commands, mentions, emoji picker, and extensible architecture for adding custom functionality.
+
+::component-example
+---
+source: false
+elevated: true
+name: 'editor-example'
+class: 'relative h-176 overflow-y-auto !p-0 rounded-b-md'
+---
+::
+
+::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/blob/v4/docs/app/components/content/examples/editor/EditorExample.vue" aria-label="View source code"}
+This example demonstrates a production-ready Editor component. Check out the source code on GitHub.
+::
+
+### Content
+
+Use the `v-model` directive to control the value of the Editor.
+
+::component-code
+---
+elevated: true
+prettier: true
+collapse: true
+ignore:
+ - modelValue.type
+ - modelValue.content
+ - class
+external:
+ - modelValue
+class: 'p-8'
+props:
+ modelValue:
+ type: 'doc'
+ content:
+ - type: 'heading'
+ attrs:
+ level: 1
+ content:
+ - type: 'text'
+ text: 'Hello World'
+ - type: 'paragraph'
+ content:
+ - type: 'text'
+ text: 'This is a '
+ - type: 'text'
+ marks:
+ - type: 'bold'
+ text: 'rich text'
+ - type: 'text'
+ text: ' editor.'
+ class: 'w-full min-h-21'
+---
+::
+
+### Content Type
+
+Use the `content-type` prop to set the format: `json`{lang="ts-type"} (default), `html`{lang="ts-type"}, or `markdown`{lang="ts-type"}. If not specified, strings are treated as HTML and objects as JSON.
+
+::component-code
+---
+elevated: true
+prettier: true
+ignore:
+ - modelValue
+ - contentType
+ - class
+external:
+ - modelValue
+class: 'p-8'
+props:
+ modelValue: |
+
Hello World
+
This is a rich text editor.
+ contentType: 'html'
+ class: 'w-full min-h-21'
+---
+::
+
+### Extensions
+
+The Editor includes the following extensions by default:
+
+- [**StarterKit**](#starter-kit) - Core editing features (bold, italic, headings, lists, etc.)
+- [**Placeholder**](#placeholder) - Show placeholder text (when placeholder prop is provided)
+- **Image** - Insert and display images
+- **Mention** - Add @ mentions support
+- **Markdown** - Parse and serialize markdown (when content type is markdown)
+
+::note
+Each built-in extension can be configured using its corresponding prop (`starter-kit`, `placeholder`, `image`, `mention`, `markdown`) to customize its behavior with TipTap options.
+::
+
+You can use the `extensions` prop to add additional TipTap extensions to enhance the Editor's capabilities:
+
+```vue
+
+
+
+
+
+```
+
+::tip{to="#with-image-upload"}
+Check out the image upload example for creating custom TipTap extensions.
+::
+
+### Placeholder
+
+Use the `placeholder` prop to set a placeholder text that shows in empty paragraphs.
+
+::component-code
+---
+elevated: true
+prettier: true
+ignore:
+ - modelValue
+ - contentType
+ - placeholder
+ - class
+external:
+ - modelValue
+class: 'p-8'
+props:
+ modelValue: |
+
Hello World
+
+ placeholder: 'Start writing...'
+ class: 'w-full min-h-21'
+---
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/functionality/placeholder" target="_blank"}
+Learn more about Placeholder extension in the TipTap documentation.
+::
+
+### Starter Kit
+
+Use the `starter-kit` prop to configure the built-in TipTap StarterKit extension which includes common editor features like bold, italic, headings, lists, blockquotes, code blocks, and more.
+
+```vue
+
+
+
+
+
+```
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/functionality/starterkit" target="_blank"}
+Learn more about StarterKit extension in the TipTap documentation.
+::
+
+### Handlers
+
+Handlers wrap TipTap's built-in commands to provide a unified interface for editor actions. When you add a `kind` property to a [EditorToolbar](/docs/components/editor-toolbar) or [EditorSuggestionMenu](/docs/components/editor-suggestion-menu) item, the corresponding handler executes the TipTap command and manages its state (active, disabled, etc.).
+
+#### Default handlers
+
+The Editor component provides these default handlers, which you can reference in toolbar or suggestion menu items using the `kind` property:
+
+| Handler | Description | Usage |
+|---------|-------------|-------|
+| `mark`{lang="ts-type"} | Toggle text marks (bold, italic, strike, code, underline) | Requires `mark` property in item |
+| `textAlign`{lang="ts-type"} | Set text alignment (left, center, right, justify) | Requires `align` property in item |
+| `heading`{lang="ts-type"} | Toggle heading levels (1-6) | Requires `level` property in item |
+| `link`{lang="ts-type"} | Add, edit, or remove links | Prompts for URL if not provided |
+| `image`{lang="ts-type"} | Insert images | Prompts for URL if not provided |
+| `blockquote`{lang="ts-type"} | Toggle blockquotes | |
+| `bulletList`{lang="ts-type"} | Toggle bullet lists | Handles list conversions |
+| `orderedList`{lang="ts-type"} | Toggle ordered lists | Handles list conversions |
+| `codeBlock`{lang="ts-type"} | Toggle code blocks | |
+| `horizontalRule`{lang="ts-type"} | Insert horizontal rules | |
+| `paragraph`{lang="ts-type"} | Set paragraph format | |
+| `undo`{lang="ts-type"} | Undo last change | |
+| `redo`{lang="ts-type"} | Redo last undone change | |
+| `clearFormatting`{lang="ts-type"} | Remove all formatting | Works with selection or position |
+| `duplicate`{lang="ts-type"} | Duplicate a node | Requires `pos` property in item |
+| `delete`{lang="ts-type"} | Delete a node | Requires `pos` property in item |
+| `moveUp`{lang="ts-type"} | Move a node up | Requires `pos` property in item |
+| `moveDown`{lang="ts-type"} | Move a node down | Requires `pos` property in item |
+| `suggestion`{lang="ts-type"} | Trigger suggestion menu | Inserts `/` character |
+| `mention`{lang="ts-type"} | Trigger mention menu | Inserts `@` character |
+| `emoji`{lang="ts-type"} | Trigger emoji picker | Inserts `:` character |
+
+Here's how to use default handlers in toolbar or suggestion menu items:
+
+```vue
+
+
+
+
+
+
+
+```
+
+#### Custom handlers
+
+Use the `handlers` prop to extend or override the default handlers. Custom handlers are merged with the default handlers, allowing you to add new actions or modify existing behavior.
+
+Each handler implements the `EditorHandler`{lang="ts-type"} interface:
+
+```ts
+interface EditorHandler {
+ /* Checks if the command can be executed in the current editor state */
+ canExecute: (editor: Editor, item?: any) => boolean
+ /* Executes the command and returns a Tiptap chain */
+ execute: (editor: Editor, item?: any) => any
+ /* Determines if the item should appear active (used for toggle states) */
+ isActive: (editor: Editor, item?: any) => boolean
+ /* Optional additional check to disable the item (combined with `canExecute`) */
+ isDisabled?: (editor: Editor, item?: any) => boolean
+}
+```
+
+Here's an example of creating custom handlers:
+
+```vue
+
+
+
+
+
+
+
+```
+
+::tip{to="#with-image-upload"}
+Check out the image upload example for a complete implementation with custom handlers.
+::
+
+## Examples
+
+### With toolbar
+
+You can use the [EditorToolbar](/docs/components/editor-toolbar) component to add a fixed, bubble, or floating toolbar to the Editor with common formatting actions.
+
+::component-example
+---
+elevated: true
+collapse: true
+prettier: true
+name: 'editor-toolbar-example'
+class: 'p-8'
+---
+::
+
+### With drag handle
+
+You can use the [EditorDragHandle](/docs/components/editor-drag-handle) component to add a draggable handle for reordering blocks.
+
+::component-example
+---
+elevated: true
+collapse: true
+prettier: true
+name: 'editor-drag-handle-example'
+class: 'p-8'
+---
+::
+
+### With suggestion menu
+
+You can use the [EditorSuggestionMenu](/docs/components/editor-suggestion-menu) component to add slash commands for quick formatting and insertions.
+
+::component-example
+---
+elevated: true
+collapse: true
+prettier: true
+name: 'editor-suggestion-menu-example'
+class: 'p-8'
+---
+::
+
+### With mention menu
+
+You can use the [EditorMentionMenu](/docs/components/editor-mention-menu) component to add @ mentions for tagging users or entities.
+
+::component-example
+---
+elevated: true
+collapse: true
+prettier: true
+name: 'editor-mention-menu-example'
+class: 'p-8'
+---
+::
+
+### With emoji menu
+
+You can use the [EditorEmojiMenu](/docs/components/editor-emoji-menu) component to add emoji picker support.
+
+::component-example
+---
+elevated: true
+collapse: true
+prettier: true
+name: 'editor-emoji-menu-example'
+class: 'p-8'
+---
+::
+
+### With image upload
+
+This example demonstrates how to create an image upload feature using the `extensions` prop to register a custom TipTap node and the `handlers` prop to define how the toolbar button triggers the upload flow.
+
+1. Create a Vue component that uses the [FileUpload](/docs/components/file-upload) component:
+
+::component-example
+---
+preview: false
+collapse: true
+name: 'editor-image-upload-node'
+---
+::
+
+2. Create a custom TipTap extension to register the node:
+
+::component-example
+---
+preview: false
+collapse: true
+lang: 'ts'
+name: 'editor-image-upload'
+---
+::
+
+::warning
+If you encounter a `Adding different instances of a keyed plugin` error when creating a custom extension, you may need to add `prosemirror-state` to the vite `optimizeDeps` include list in your `nuxt.config.ts` file.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ vite: {
+ optimizeDeps: {
+ include: ['prosemirror-state']
+ }
+ }
+})
+```
+::
+
+3. Use the custom extension in the Editor:
+
+::component-example
+---
+elevated: true
+collapse: true
+prettier: true
+name: 'editor-image-upload-example'
+class: '!p-0'
+---
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/custom-extensions" target="_blank"}
+Learn more about creating custom extensions in the TipTap documentation.
+::
+
+## API
+
+### Props
+
+:component-props
+
+### Slots
+
+:component-slots
+
+### Emits
+
+:component-emits
+
+### Expose
+
+When accessing the component via a template ref, you can use the following:
+
+| Name | Type |
+| ---- | ---- |
+| `editor`{lang="ts-type"} | `Ref`{lang="ts-type"} |
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/api/editor" target="_blank"}
+The exposed editor instance is the TipTap Editor API. Check the TipTap documentation for all available methods and properties.
+::
+
+## Theme
+
+:component-theme
+
+## Changelog
+
+:component-changelog
diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts
index f31bd365ef..2427358b53 100644
--- a/docs/nuxt.config.ts
+++ b/docs/nuxt.config.ts
@@ -182,7 +182,7 @@ export default defineNuxtConfig({
vite: {
optimizeDeps: {
// prevents reloading page when navigating between components
- include: ['@ai-sdk/vue', '@internationalized/date', '@nuxt/content/utils', '@tanstack/vue-table', '@vercel/analytics/nuxt', '@vercel/speed-insights/nuxt', '@vue/devtools-core', '@vue/devtools-kit', '@vueuse/integrations/useFuse', '@vueuse/shared', 'ai', 'colortranslator', 'embla-carousel-auto-height', 'embla-carousel-auto-scroll', 'embla-carousel-autoplay', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-vue', 'embla-carousel-wheel-gestures', 'json5', 'motion-v', 'ohash', 'ohash/utils', 'prettier', 'reka-ui', 'reka-ui/namespaced', 'scule', 'shiki', 'shiki-stream/vue', 'shiki-transformer-color-highlight', 'shiki/engine-javascript.mjs', 'tailwind-variants', 'tailwindcss/colors', 'ufo', 'vaul-vue', 'zod']
+ include: ['@ai-sdk/vue', '@internationalized/date', '@nuxt/content/utils', '@tanstack/vue-table', '@tiptap/extension-emoji', '@tiptap/extension-text-align', '@tiptap/core', '@tiptap/extension-horizontal-rule', '@tiptap/extension-image', '@tiptap/extension-mention', '@tiptap/extension-placeholder', '@tiptap/markdown', '@tiptap/starter-kit', '@tiptap/vue-3', '@floating-ui/dom', '@tiptap/extension-drag-handle-vue-3', '@tiptap/vue-3/menus', '@tiptap/suggestion', '@tiptap/pm/state', '@vercel/analytics/nuxt', '@vercel/speed-insights/nuxt', '@vue/devtools-core', '@vue/devtools-kit', '@vueuse/integrations/useFuse', '@vueuse/shared', 'ai', 'colortranslator', 'embla-carousel-auto-height', 'embla-carousel-auto-scroll', 'embla-carousel-autoplay', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-vue', 'embla-carousel-wheel-gestures', 'json5', 'motion-v', 'ohash', 'ohash/utils', 'prettier', 'prosemirror-state', 'reka-ui', 'reka-ui/namespaced', 'scule', 'shiki', 'shiki-stream/vue', 'shiki-transformer-color-highlight', 'shiki/engine-javascript.mjs', 'tailwind-variants', 'tailwindcss/colors', 'ufo', 'vaul-vue', 'zod']
}
},
@@ -193,6 +193,49 @@ export default defineNuxtConfig({
return { component, code }
}],
+ overrides: {
+ UEditor: {
+ props: {
+ modelValue: { name: 'modelValue', type: 'null | string | JSONContent | JSONContent[]' },
+ parseOptions: { name: 'parseOptions', type: 'ParseOptions' }
+ }
+ },
+ UEditorDragHandle: { props: { editor: { name: 'editor', type: 'Editor' } } },
+ UEditorToolbar: { props: { editor: { name: 'editor', type: 'Editor' } } },
+ UEditorSuggestionMenu: { props: { editor: { name: 'editor', type: 'Editor' } } },
+ UEditorMentionMenu: { props: { editor: { name: 'editor', type: 'Editor' } } },
+ UEditorEmojiMenu: { props: { editor: { name: 'editor', type: 'Editor' } } },
+ UCalendar: {
+ props: {
+ defaultValue: { name: 'defaultValue', type: 'CalendarDate | CalendarDateTime | ZonedDateTime | DateRange | DateValue[]' },
+ modelValue: { name: 'modelValue', type: 'null | CalendarDate | CalendarDateTime | ZonedDateTime | DateRange | DateValue[]' },
+ defaultPlaceholder: { name: 'defaultPlaceholder', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' },
+ placeholder: { name: 'placeholder', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' },
+ maxValue: { name: 'maxValue', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' },
+ minValue: { name: 'minValue', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' }
+ }
+ },
+ UInputDate: {
+ props: {
+ defaultValue: { name: 'defaultValue', type: 'CalendarDate | CalendarDateTime | ZonedDateTime | DateRange' },
+ modelValue: { name: 'modelValue', type: 'null | CalendarDate | CalendarDateTime | ZonedDateTime | DateRange' },
+ defaultPlaceholder: { name: 'defaultPlaceholder', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' },
+ placeholder: { name: 'placeholder', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' },
+ maxValue: { name: 'maxValue', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' },
+ minValue: { name: 'minValue', type: 'CalendarDate | CalendarDateTime | ZonedDateTime' }
+ }
+ },
+ UInputTime: {
+ props: {
+ defaultValue: { name: 'defaultValue', type: 'Time | CalendarDateTime | ZonedDateTime' },
+ modelValue: { name: 'modelValue', type: 'null | Time | CalendarDateTime | ZonedDateTime' },
+ defaultPlaceholder: { name: 'defaultPlaceholder', type: 'Time | CalendarDateTime | ZonedDateTime' },
+ placeholder: { name: 'placeholder', type: 'Time | CalendarDateTime | ZonedDateTime' },
+ maxValue: { name: 'maxValue', type: 'Time | CalendarDateTime | ZonedDateTime' },
+ minValue: { name: 'minValue', type: 'Time | CalendarDateTime | ZonedDateTime' }
+ }
+ }
+ },
exclude: [
'@nuxt/content',
'@nuxt/icon',
diff --git a/docs/package.json b/docs/package.json
index d65722cb40..58f6e2a77e 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -21,6 +21,8 @@
"@nuxt/image": "^2.0.0",
"@nuxt/ui": "workspace:*",
"@nuxtjs/plausible": "^2.0.1",
+ "@tiptap/extension-emoji": "^3.11.1",
+ "@tiptap/extension-text-align": "^3.11.1",
"@octokit/rest": "^22.0.1",
"@regle/core": "^1.10.2",
"@regle/rules": "^1.10.2",
@@ -35,7 +37,7 @@
"maska": "^3.2.0",
"motion-v": "^1.7.3",
"nuxt": "^4.2.1",
- "nuxt-component-meta": "^0.14.2",
+ "nuxt-component-meta": "https://pkg.pr.new/nuxt-component-meta@29e1040",
"nuxt-llms": "^0.1.3",
"nuxt-og-image": "^5.1.12",
"prettier": "^3.6.2",
diff --git a/docs/public/components/dark/editor-drag-handle.png b/docs/public/components/dark/editor-drag-handle.png
new file mode 100644
index 0000000000..4b7ab79253
Binary files /dev/null and b/docs/public/components/dark/editor-drag-handle.png differ
diff --git a/docs/public/components/dark/editor-emoji-menu.png b/docs/public/components/dark/editor-emoji-menu.png
new file mode 100644
index 0000000000..5337ee1b4a
Binary files /dev/null and b/docs/public/components/dark/editor-emoji-menu.png differ
diff --git a/docs/public/components/dark/editor-mention-menu.png b/docs/public/components/dark/editor-mention-menu.png
new file mode 100644
index 0000000000..7897a498fc
Binary files /dev/null and b/docs/public/components/dark/editor-mention-menu.png differ
diff --git a/docs/public/components/dark/editor-suggestion-menu.png b/docs/public/components/dark/editor-suggestion-menu.png
new file mode 100644
index 0000000000..a9e7ed05d1
Binary files /dev/null and b/docs/public/components/dark/editor-suggestion-menu.png differ
diff --git a/docs/public/components/dark/editor-toolbar.png b/docs/public/components/dark/editor-toolbar.png
new file mode 100644
index 0000000000..3824ef8887
Binary files /dev/null and b/docs/public/components/dark/editor-toolbar.png differ
diff --git a/docs/public/components/dark/editor.png b/docs/public/components/dark/editor.png
new file mode 100644
index 0000000000..d14ca54e4e
Binary files /dev/null and b/docs/public/components/dark/editor.png differ
diff --git a/docs/public/components/light/editor-drag-handle.png b/docs/public/components/light/editor-drag-handle.png
new file mode 100644
index 0000000000..4662206558
Binary files /dev/null and b/docs/public/components/light/editor-drag-handle.png differ
diff --git a/docs/public/components/light/editor-emoji-menu.png b/docs/public/components/light/editor-emoji-menu.png
new file mode 100644
index 0000000000..8a1cb8fde6
Binary files /dev/null and b/docs/public/components/light/editor-emoji-menu.png differ
diff --git a/docs/public/components/light/editor-mention-menu.png b/docs/public/components/light/editor-mention-menu.png
new file mode 100644
index 0000000000..3ff1dec58d
Binary files /dev/null and b/docs/public/components/light/editor-mention-menu.png differ
diff --git a/docs/public/components/light/editor-suggestion-menu.png b/docs/public/components/light/editor-suggestion-menu.png
new file mode 100644
index 0000000000..f150c111fa
Binary files /dev/null and b/docs/public/components/light/editor-suggestion-menu.png differ
diff --git a/docs/public/components/light/editor-toolbar.png b/docs/public/components/light/editor-toolbar.png
new file mode 100644
index 0000000000..5a7b02d6a3
Binary files /dev/null and b/docs/public/components/light/editor-toolbar.png differ
diff --git a/docs/public/components/light/editor.png b/docs/public/components/light/editor.png
new file mode 100644
index 0000000000..b6b1f76d0d
Binary files /dev/null and b/docs/public/components/light/editor.png differ
diff --git a/docs/public/placeholder.jpeg b/docs/public/placeholder.jpeg
new file mode 100644
index 0000000000..b86d40b552
Binary files /dev/null and b/docs/public/placeholder.jpeg differ
diff --git a/package.json b/package.json
index e5b1077fde..0a72f7d9c2 100644
--- a/package.json
+++ b/package.json
@@ -133,6 +133,14 @@
"@tailwindcss/vite": "^4.1.17",
"@tanstack/vue-table": "^8.21.3",
"@tanstack/vue-virtual": "^3.13.12",
+ "@tiptap/extension-drag-handle-vue-3": "^3.11.1",
+ "@tiptap/extension-image": "^3.11.1",
+ "@tiptap/extension-mention": "^3.11.1",
+ "@tiptap/extension-placeholder": "^3.11.1",
+ "@tiptap/markdown": "^3.11.1",
+ "@tiptap/starter-kit": "^3.11.1",
+ "@tiptap/suggestion": "^3.11.1",
+ "@tiptap/vue-3": "^3.11.1",
"@unhead/vue": "^2.0.19",
"@vueuse/core": "^13.9.0",
"@vueuse/integrations": "^13.9.0",
diff --git a/playgrounds/nuxt/app/components/Navbar.vue b/playgrounds/nuxt/app/components/Navbar.vue
index 6c5c8134ee..14b8882d71 100644
--- a/playgrounds/nuxt/app/components/Navbar.vue
+++ b/playgrounds/nuxt/app/components/Navbar.vue
@@ -27,7 +27,14 @@ defineShortcuts({
-
+
diff --git a/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue b/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue
new file mode 100644
index 0000000000..0b44525009
--- /dev/null
+++ b/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.
@@ -539,7 +539,7 @@ exports[`Accordion > renders with leading slot correctly 1`] = `
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.
diff --git a/test/components/__snapshots__/Editor-vue.spec.ts.snap b/test/components/__snapshots__/Editor-vue.spec.ts.snap
new file mode 100644
index 0000000000..60220859c8
--- /dev/null
+++ b/test/components/__snapshots__/Editor-vue.spec.ts.snap
@@ -0,0 +1,40 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Editor > renders with as correctly 1`] = `
+"
+
+
+
+
+
+"
+`;
+
+exports[`Editor > renders with class correctly 1`] = `
+"