+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarOptionsExample.vue b/docs/app/components/content/examples/editor/EditorToolbarOptionsExample.vue
new file mode 100644
index 0000000000..a9cc4b3c11
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarOptionsExample.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarShouldShowExample.vue b/docs/app/components/content/examples/editor/EditorToolbarShouldShowExample.vue
new file mode 100644
index 0000000000..6b3bbdd551
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarShouldShowExample.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/ImageUploadNode.vue b/docs/app/components/content/examples/editor/ImageUploadNode.vue
new file mode 100644
index 0000000000..7bc80e8fd2
--- /dev/null
+++ b/docs/app/components/content/examples/editor/ImageUploadNode.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
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/app/utils/editor/image-upload.ts b/docs/app/utils/editor/image-upload.ts
new file mode 100644
index 0000000000..08261ba299
--- /dev/null
+++ b/docs/app/utils/editor/image-upload.ts
@@ -0,0 +1,38 @@
+import { Node, mergeAttributes } from '@tiptap/core'
+import { VueNodeViewRenderer } from '@tiptap/vue-3'
+import ImageUploadNode from '../../components/content/examples/editor/ImageUploadNode.vue'
+
+declare module '@tiptap/vue-3' {
+ interface Commands {
+ imageUpload: {
+ insertImageUpload: () => ReturnType
+ }
+ }
+}
+
+export const ImageUpload = Node.create({
+ name: 'imageUpload',
+ group: 'block',
+ atom: true,
+ addAttributes() {
+ return {}
+ },
+ parseHTML() {
+ return [{
+ tag: 'div[data-type="image-upload"]'
+ }]
+ },
+ renderHTML({ HTMLAttributes }) {
+ return ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'image-upload' })]
+ },
+ addNodeView() {
+ return VueNodeViewRenderer(ImageUploadNode)
+ },
+ addCommands() {
+ return {
+ insertImageUpload: () => ({ commands }) => {
+ return commands.insertContent({ type: this.name })
+ }
+ }
+ }
+})
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..0025c840d5
--- /dev/null
+++ b/docs/content/docs/2.components/editor-drag-handle.md
@@ -0,0 +1,148 @@
+---
+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 must be used inside an [Editor](/docs/components/editor) component's default slot to provide drag-and-drop functionality for editor blocks.
+
+Use it to add a draggable handle that appears on hover, allowing users to reorder blocks in the editor.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The drag handle automatically positions itself to the left of the block content and hides when not hovering.
+::
+
+### Icon
+
+Use the `icon` prop to customize the drag handle icon. Defaults to `appConfig.ui.icons.drag`.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-icon-example'
+class: 'min-h-80'
+---
+::
+
+::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.
+:::
+::
+
+### Color and variant
+
+Use the `color` and `variant` props to customize the drag handle button appearance.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-color-example'
+class: 'min-h-80'
+---
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using Floating UI options.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-options-example'
+class: 'min-h-80'
+---
+::
+
+::callout{icon="i-lucide-info" to="https://floating-ui.com/docs/computeposition" target="_blank"}
+The options are passed to Floating UI's `computePosition` function. You can configure placement, offset, flip, shift, and other positioning middleware.
+::
+
+## Examples
+
+### With dropdown menu
+
+You can use the default slot to add custom buttons or menus alongside the drag handle.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-dropdown-example'
+class: 'min-h-80'
+---
+::
+
+::tip
+This example shows how to use a dropdown menu to provide block-level actions like duplicate, move, or delete.
+::
+
+### With add button
+
+Use the `onClick` function from the slot props to handle click events and trigger actions.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-add-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The `onClick` function returns the current node and its position in the document, which you can use to insert content or perform other operations.
+::
+
+### Node change events
+
+Listen to the `@node-change` event to track which node is currently being hovered or selected.
+
+::component-example
+---
+collapse: true
+name: 'editor-drag-handle-node-change-example'
+class: 'min-h-80'
+---
+::
+
+## 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..93487b46ae
--- /dev/null
+++ b/docs/content/docs/2.components/editor-emoji-menu.md
@@ -0,0 +1,140 @@
+---
+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 must be used inside an [Editor](/docs/components/editor) component's default slot to provide emoji picker functionality.
+
+Type `:` followed by an emoji name to search and insert emojis into your content.
+
+::component-example
+---
+collapse: true
+name: 'editor-emoji-menu-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The emoji menu filters items as you type emoji names or tags and supports keyboard navigation.
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/functionality/emoji" target="_blank"}
+This component requires the Emoji extension from TipTap to be added to the Editor's extensions prop.
+::
+
+### Items
+
+Use the `items` prop to define the available emojis. Each item should include the emoji character, name, shortcodes, and optional tags.
+
+::component-example
+---
+collapse: true
+name: 'editor-emoji-menu-items-example'
+class: 'min-h-80'
+---
+::
+
+Each item should have:
+- `name`{lang="ts-type"}: The emoji name (required)
+- `emoji`{lang="ts-type"}: The emoji character to insert
+- `shortcodes`{lang="ts-type"}: Array of shortcode strings for search
+- `tags`{lang="ts-type"}: Array of tag strings for additional search terms
+- `group`{lang="ts-type"}: Optional group name
+- `fallbackImage`{lang="ts-type"}: Optional fallback image URL
+
+::tip
+You can use the `gitHubEmojis` export from `@tiptap/extension-emoji` for a complete emoji set.
+::
+
+### Trigger character
+
+Use the `char` prop to change the trigger character. Defaults to `:`.
+
+::component-example
+---
+collapse: true
+name: 'editor-emoji-menu-char-example'
+class: 'min-h-80'
+---
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using Floating UI options.
+
+::component-example
+---
+collapse: true
+name: 'editor-emoji-menu-options-example'
+class: 'min-h-80'
+---
+::
+
+::tip{to="https://floating-ui.com/docs/computeposition" target="_blank"}
+The options are passed to Floating UI's `computePosition` function. You can configure strategy, placement, offset, flip, shift, and other positioning middleware.
+::
+
+## Examples
+
+### GitHub emojis
+
+Use the GitHub emoji set from TipTap for a comprehensive emoji picker.
+
+::component-example
+---
+collapse: true
+name: 'editor-emoji-menu-github-example'
+class: 'min-h-80'
+---
+::
+
+::note
+This example uses the `gitHubEmojis` export from `@tiptap/extension-emoji` which includes over 1800 emojis.
+::
+
+### Custom emoji sets
+
+Create your own custom emoji set with specific emojis for your use case.
+
+::component-example
+---
+collapse: true
+name: 'editor-emoji-menu-custom-example'
+class: 'min-h-80'
+---
+::
+
+::tip
+Custom emoji sets are useful when you want to limit the available emojis or provide domain-specific emojis.
+::
+
+## 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-mention-menu.md b/docs/content/docs/2.components/editor-mention-menu.md
new file mode 100644
index 0000000000..9e65b81470
--- /dev/null
+++ b/docs/content/docs/2.components/editor-mention-menu.md
@@ -0,0 +1,131 @@
+---
+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 must be used inside an [Editor](/docs/components/editor) component's default slot to provide @ mention functionality.
+
+Type `@` followed by a name to search and insert mentions into your content.
+
+::component-example
+---
+collapse: true
+name: 'editor-mention-menu-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The mention menu automatically filters items as you type and supports keyboard navigation.
+::
+
+### Items
+
+Use the `items` prop to define the available mentions. Each item can include a label, avatar, icon, and description.
+
+::component-example
+---
+collapse: true
+name: 'editor-mention-menu-items-example'
+class: 'min-h-80'
+---
+::
+
+Each item should have:
+- `label`{lang="ts-type"}: The mention text (required)
+- `avatar`{lang="ts-type"}: Avatar props for displaying a user image
+- `icon`{lang="ts-type"}: Icon to display if no avatar is provided
+- `description`{lang="ts-type"}: Optional description text
+- `disabled`{lang="ts-type"}: Whether the item can be selected
+
+::tip
+Use avatars for user mentions and icons for other entities like teams or channels.
+::
+
+### Trigger character
+
+Use the `char` prop to change the trigger character. Defaults to `@`.
+
+::component-example
+---
+collapse: true
+name: 'editor-mention-menu-char-example'
+class: 'min-h-80'
+---
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using Floating UI options.
+
+::component-example
+---
+collapse: true
+name: 'editor-mention-menu-options-example'
+class: 'min-h-80'
+---
+::
+
+::tip{to="https://floating-ui.com/docs/computeposition" target="_blank"}
+The options are passed to Floating UI's `computePosition` function. You can configure strategy, placement, offset, flip, shift, and other positioning middleware.
+::
+
+## Examples
+
+### User mentions
+
+Create a mention menu for tagging users in your content.
+
+::component-example
+---
+collapse: true
+name: 'editor-mention-menu-users-example'
+class: 'min-h-80'
+---
+::
+
+### With fetched data
+
+Fetch mention suggestions from an API as users type.
+
+::component-example
+---
+collapse: true
+name: 'editor-mention-menu-fetched-example'
+class: 'min-h-80'
+---
+::
+
+::note
+This example demonstrates how to dynamically load mentions based on user input.
+::
+
+## 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-suggestion-menu.md b/docs/content/docs/2.components/editor-suggestion-menu.md
new file mode 100644
index 0000000000..8bb4d51151
--- /dev/null
+++ b/docs/content/docs/2.components/editor-suggestion-menu.md
@@ -0,0 +1,132 @@
+---
+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 must be used inside an [Editor](/docs/components/editor) component's default slot to provide slash command functionality.
+
+Type `/` to open the suggestion menu and browse available formatting commands and actions.
+
+::component-example
+---
+collapse: true
+name: 'editor-suggestion-menu-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The suggestion menu filters items as you type and supports keyboard navigation with arrow keys and enter to select.
+::
+
+### Items
+
+Use the `items` prop to define the available commands in the menu. Items can be editor commands with a `kind` property or separators and labels.
+
+::component-example
+---
+collapse: true
+name: 'editor-suggestion-menu-items-example'
+class: 'min-h-80'
+---
+::
+
+Each item can have:
+- `kind`{lang="ts-type"}: The editor command type (e.g., `heading`, `bulletList`, etc.)
+- `label`{lang="ts-type"}: Display text for the item
+- `description`{lang="ts-type"}: Optional description text
+- `icon`{lang="ts-type"}: Icon to display
+- `type: 'label'`{lang="ts-type"}: For section headers
+- `type: 'separator'`{lang="ts-type"}: For visual separators
+
+::tip
+Group related commands with labels to create organized, scannable menus.
+::
+
+### Trigger character
+
+Use the `char` prop to change the trigger character. Defaults to `/`.
+
+::component-example
+---
+collapse: true
+name: 'editor-suggestion-menu-char-example'
+class: 'min-h-80'
+---
+::
+
+### Options
+
+Use the `options` prop to customize the positioning behavior using Floating UI options.
+
+::component-example
+---
+collapse: true
+name: 'editor-suggestion-menu-options-example'
+class: 'min-h-80'
+---
+::
+
+::tip{to="https://floating-ui.com/docs/computeposition" target="_blank"}
+The options are passed to Floating UI's `computePosition` function. You can configure strategy, placement, offset, flip, shift, and other positioning middleware.
+::
+
+## Examples
+
+### With custom items
+
+Create a fully customized suggestion menu with your own commands and groupings.
+
+::component-example
+---
+collapse: true
+name: 'editor-suggestion-menu-custom-example'
+class: 'min-h-80'
+---
+::
+
+### With icons and descriptions
+
+Add icons and descriptions to make commands more discoverable.
+
+::component-example
+---
+collapse: true
+name: 'editor-suggestion-menu-icons-example'
+class: 'min-h-80'
+---
+::
+
+::tip
+Descriptions help users understand what each command does, especially for less common formatting options.
+::
+
+## 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-toolbar.md b/docs/content/docs/2.components/editor-toolbar.md
new file mode 100644
index 0000000000..8b669752fa
--- /dev/null
+++ b/docs/content/docs/2.components/editor-toolbar.md
@@ -0,0 +1,287 @@
+---
+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 must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+### Items
+
+Use the `items` prop to define the toolbar buttons and dropdowns. Each item can be an editor command (with a `kind` property) or a regular button.
+
+
+The `items` prop accepts an array of toolbar items or an array of arrays for grouping items with separators.
+
+Each item can be:
+
+- An **editor item** with a `kind`{lang="ts-type"} property (e.g., `{ kind: 'bold', icon: 'i-lucide-bold' }`{lang="ts-type"})
+- A **button item** with standard button props (e.g., `{ icon: 'i-lucide-save', onClick: () => {} }`{lang="ts-type"})
+- A **dropdown item** with an `items`{lang="ts-type"} array for submenus
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-items-example'
+class: 'min-h-80'
+---
+::
+
+::tip
+Editor items automatically sync their active and disabled states with the editor. The toolbar uses handlers from the Editor component to execute commands.
+::
+
+### Layout
+
+Use the `layout` prop to change how the toolbar is displayed. Defaults to `fixed`.
+
+Available layouts:
+- `fixed`{lang="ts-type"} (default): always visible toolbar
+- `bubble`{lang="ts-type"}: contextual menu that appears on text selection
+- `floating`{lang="ts-type"}: menu that appears on empty blocks
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-layout-example'
+class: 'min-h-80'
+---
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/functionality/bubble-menu" target="_blank"}
+The bubble and floating layouts use TipTap's BubbleMenu and FloatingMenu extensions. Check the TipTap documentation for advanced positioning options.
+::
+
+### Should show
+
+When using `bubble` or `floating` layouts, use the `should-show` prop to control when the toolbar appears.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-should-show-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The `should-show` function receives the editor, view, state, and other useful properties to determine visibility.
+::
+
+### Options
+
+When using `bubble` or `floating` layouts, use the `options` prop to customize the positioning behavior using Floating UI options.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-options-example'
+class: 'min-h-80'
+---
+::
+
+::tip{to="https://floating-ui.com/docs/computeposition" target="_blank"}
+The options are passed to Floating UI's `computePosition` function. You can configure strategy, placement, offset, flip, shift, and other positioning middleware.
+::
+
+### Color and variant
+
+Use the `color` and `variant` props to customize the toolbar button styles.
+
+::component-code
+---
+prettier: true
+collapse: true
+ignore:
+ - editor
+ - items
+ - class
+external:
+ - editor
+ - items
+class: 'min-h-80'
+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
+---
+prettier: true
+collapse: true
+ignore:
+ - editor
+ - items
+ - class
+external:
+ - editor
+ - items
+class: 'min-h-80'
+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
+---
+prettier: true
+collapse: true
+ignore:
+ - editor
+ - items
+ - class
+external:
+ - editor
+ - items
+class: 'min-h-80'
+items:
+ size:
+ - xs
+ - sm
+ - md
+ - lg
+props:
+ size: 'md'
+ editor: {}
+ items: [[{ kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' }]]
+---
+::
+
+## Examples
+
+### Fixed toolbar in navbar
+
+You can place a fixed toolbar in a navbar or header for a consistent interface.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-navbar-example'
+class: 'min-h-96'
+---
+::
+
+### Bubble menu on text selection
+
+Use `layout="bubble"` to create a contextual toolbar that appears when text is selected.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-bubble-example'
+class: 'min-h-80'
+---
+::
+
+::note
+The bubble menu automatically positions itself near the selection and includes flip and shift middleware for optimal positioning.
+::
+
+### Floating menu for empty blocks
+
+Use `layout="floating"` to create a menu that appears on empty lines.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-floating-example'
+class: 'min-h-80'
+---
+::
+
+::tip
+Combine with the `should-show` prop to control when the floating menu appears, such as only on empty paragraphs.
+::
+
+### With custom button slots
+
+You can use slots to customize specific toolbar items.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-custom-slot-example'
+class: 'min-h-80'
+---
+::
+
+::tip{to="#slots"}
+Use the `#item` slot to customize all items, or `#{{ item.slot }}` to customize a specific item.
+::
+
+### With dropdown menus
+
+Create dropdown menus by adding an `items` property to toolbar items.
+
+::component-example
+---
+collapse: true
+name: 'editor-toolbar-dropdown-example'
+class: 'min-h-80'
+---
+::
+
+::note
+Dropdowns automatically show the active child item's icon when an option is selected.
+::
+
+### Image-specific toolbar
+
+Create context-specific toolbars that appear only for certain node types.
+
+::component-example
+---
+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..1f9eb11440
--- /dev/null
+++ b/docs/content/docs/2.components/editor.md
@@ -0,0 +1,520 @@
+---
+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'
+---
+::
+
+::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 setup. 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
+externalTypes:
+ - EditorContent
+class: 'min-h-40 sm:py-9'
+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'
+---
+::
+
+### 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: 'min-h-40 sm:py-9'
+props:
+ modelValue: |
+
Hello World
+
This is a rich text editor.
+ contentType: 'html'
+ class: 'w-full'
+---
+::
+
+### 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-custom-extensions"}
+See the Examples section for how to add custom extensions like emoji picker, text alignment, and file uploads with custom UI.
+::
+
+### 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: 'min-h-40 sm:py-9'
+props:
+ modelValue: |
+
Hello World
+
+ placeholder: 'Start writing...'
+ class: 'w-full'
+---
+::
+
+::note{to="https://tiptap.dev/docs/editor/extensions/functionality/placeholder#settings" target="_blank"}
+Learn more about placeholder options in the TipTap documentation.
+::
+
+### Starter Kit
+
+Use the `starter-kit` prop to configure the built-in TipTap StarterKit extension which includes common editor features.
+
+```vue
+
+
+
+
+
+```
+
+::note{to="https://tiptap.dev/docs/editor/extensions/functionality/starterkit#included-extensions" target="_blank"}
+The StarterKit includes extensions for bold, italic, strike, code, headings, lists, blockquotes, code blocks, horizontal rules, and more. Check the TipTap documentation for all available options.
+::
+
+### Handlers
+
+Use the `handlers` prop to override or extend the default command handlers that are used by [EditorToolbar](/docs/components/editor-toolbar) and [EditorSuggestionMenu](/docs/components/editor-suggestion-menu) items through the `kind` field. Handlers define how editor commands are executed, checked for active state, and validated.
+
+Each handler implements the `EditorHandler`{lang="ts-type"} interface:
+
+```ts
+interface EditorHandler {
+ canExecute?: (editor: Editor, item?: any) => boolean
+ execute: (editor: Editor, item?: any) => any
+ isActive?: (editor: Editor, item?: any) => boolean
+ isDisabled?: (editor: Editor, item?: any) => boolean
+}
+```
+
+The Editor component provides the following default handlers:
+
+- `mark`{lang="ts-type"} - Toggle text marks (bold, italic, strike, code, underline)
+- `textAlign`{lang="ts-type"} - Set text alignment (left, center, right, justify)
+- `heading`{lang="ts-type"} - Toggle heading levels (1-6)
+- `link`{lang="ts-type"} - Add, edit, or remove links
+- `image`{lang="ts-type"} - Insert images
+- `blockquote`{lang="ts-type"} - Toggle blockquotes
+- `bulletList`{lang="ts-type"} - Toggle bullet lists
+- `orderedList`{lang="ts-type"} - Toggle ordered lists
+- `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 from selection
+- `duplicate`{lang="ts-type"} - Duplicate a node
+- `delete`{lang="ts-type"} - Delete a node
+- `moveUp`{lang="ts-type"} - Move a node up
+- `moveDown`{lang="ts-type"} - Move a node down
+- `suggestion`{lang="ts-type"} - Trigger suggestion menu (/)
+- `mention`{lang="ts-type"} - Trigger mention menu (@)
+- `emoji`{lang="ts-type"} - Trigger emoji picker (:)
+
+::tip{to="#with-custom-handlers"}
+See the Examples section for more practical use cases like adding notifications, custom validation, or integrating with external services.
+::
+
+## 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
+name: 'editor-toolbar-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+### 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
+name: 'editor-drag-handle-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+### 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
+name: 'editor-suggestion-menu-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+### 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
+name: 'editor-mention-menu-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+### 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
+name: 'editor-emoji-menu-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+### With custom extensions
+
+You can add custom TipTap extensions to enhance the editor functionality.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-custom-extensions-example'
+class: 'min-h-96'
+---
+::
+
+::tip
+This example includes the Emoji extension for emoji picker support and the TextAlign extension for text alignment buttons.
+::
+
+### With custom handlers
+
+You can provide custom handlers to override or extend the default command behavior.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-custom-handlers-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+::note
+Custom handlers allow you to customize how editor commands are executed, checked for active state, and validated.
+::
+
+### Custom toolbar item slots
+
+You can override specific toolbar items using slots to add custom functionality. This is useful for complex interactions like link editing.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-custom-toolbar-slot-example'
+class: 'min-h-40 sm:py-9'
+---
+::
+
+In the example above, we use the `slot` property on a toolbar item and provide a matching template slot with custom UI:
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+::tip
+Use custom slots when you need more control over toolbar item behavior, such as popovers, modals, or complex forms.
+::
+
+### Custom handlers with extensions
+
+You can add custom TipTap extensions and provide matching handlers to integrate them with the editor's toolbar and menus.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-custom-extension-handler-example'
+class: 'min-h-96'
+---
+::
+
+The example above shows a complete custom ImageUpload extension with handlers. Here's how it works:
+
+**1. Create a TipTap extension (`utils/editor/image-upload.ts`):**
+
+```ts
+import { Node, mergeAttributes } from '@tiptap/core'
+import { VueNodeViewRenderer } from '@tiptap/vue-3'
+import ImageUploadNode from './ImageUploadNode.vue'
+
+export const ImageUpload = Node.create({
+ name: 'imageUpload',
+ group: 'block',
+ atom: true,
+
+ addNodeView() {
+ return VueNodeViewRenderer(ImageUploadNode)
+ },
+
+ addCommands() {
+ return {
+ insertImageUpload: () => ({ commands }) => {
+ return commands.insertContent({ type: this.name })
+ }
+ }
+ }
+})
+```
+
+**2. Create custom handlers to connect the extension to your toolbar:**
+
+```ts
+import type { EditorCustomHandlers } from '@nuxt/ui'
+import type { Editor } from '@tiptap/vue-3'
+
+const customHandlers = {
+ imageUpload: {
+ canExecute: (editor: Editor) => editor.can().insertContent({ type: 'imageUpload' }),
+ execute: (editor: Editor) => editor.chain().focus().insertImageUpload().run(),
+ isActive: (editor: Editor) => editor.isActive('imageUpload'),
+ isDisabled: undefined
+ }
+} satisfies EditorCustomHandlers
+```
+
+**3. Create the Vue component for the custom node (`ImageUploadNode.vue`):**
+
+```vue
+
+
+
+
+
+
+
+```
+
+::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.
+::
+
+::tip
+Custom extensions with handlers unlock powerful functionality like file uploads, custom blocks, AI completions, and more. The ImageUpload example shows a complete implementation with a custom Vue component for the node view.
+::
+
+## 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..176c2f18a6 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', '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,13 @@ export default defineNuxtConfig({
return { component, code }
}],
+ overrides: {
+ 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' } } }
+ },
exclude: [
'@nuxt/content',
'@nuxt/icon',
diff --git a/docs/package.json b/docs/package.json
index d65722cb40..a8a042dc19 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -35,7 +35,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/package.json b/package.json
index 58bdacfb77..79c9ab482b 100644
--- a/package.json
+++ b/package.json
@@ -132,6 +132,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.0",
+ "@tiptap/extension-image": "^3.11.0",
+ "@tiptap/extension-mention": "^3.11.0",
+ "@tiptap/extension-placeholder": "^3.11.0",
+ "@tiptap/markdown": "^3.11.0",
+ "@tiptap/starter-kit": "^3.11.0",
+ "@tiptap/suggestion": "^3.11.0",
+ "@tiptap/vue-3": "^3.11.0",
"@unhead/vue": "^2.0.19",
"@vueuse/core": "^13.9.0",
"@vueuse/integrations": "^13.9.0",
diff --git a/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue b/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue
new file mode 100644
index 0000000000..4debbf4640
--- /dev/null
+++ b/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
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..85a4571b43
--- /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`] = `
+"