+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorMentionMenuExample.vue b/docs/app/components/content/examples/editor/EditorMentionMenuExample.vue
new file mode 100644
index 0000000000..0f8beedbeb
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorMentionMenuExample.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorMentionMenuItemsExample.vue b/docs/app/components/content/examples/editor/EditorMentionMenuItemsExample.vue
new file mode 100644
index 0000000000..f48206f71e
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorMentionMenuItemsExample.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorSuggestionMenuExample.vue b/docs/app/components/content/examples/editor/EditorSuggestionMenuExample.vue
new file mode 100644
index 0000000000..79788f9c5e
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorSuggestionMenuExample.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorSuggestionMenuItemsExample.vue b/docs/app/components/content/examples/editor/EditorSuggestionMenuItemsExample.vue
new file mode 100644
index 0000000000..c0e81752f5
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorSuggestionMenuItemsExample.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarCustomSlotExample.vue b/docs/app/components/content/examples/editor/EditorToolbarCustomSlotExample.vue
new file mode 100644
index 0000000000..9ed9d09d1e
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarCustomSlotExample.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarExample.vue b/docs/app/components/content/examples/editor/EditorToolbarExample.vue
new file mode 100644
index 0000000000..ea3d28df7d
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarExample.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarImageExample.vue b/docs/app/components/content/examples/editor/EditorToolbarImageExample.vue
new file mode 100644
index 0000000000..c26ab68f06
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarImageExample.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarItemsExample.vue b/docs/app/components/content/examples/editor/EditorToolbarItemsExample.vue
new file mode 100644
index 0000000000..5bc26c0ca4
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarItemsExample.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
diff --git a/docs/app/components/content/examples/editor/EditorToolbarLayoutExample.vue b/docs/app/components/content/examples/editor/EditorToolbarLayoutExample.vue
new file mode 100644
index 0000000000..c7f74ca84c
--- /dev/null
+++ b/docs/app/components/content/examples/editor/EditorToolbarLayoutExample.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
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/1.getting-started/2.installation/1.nuxt.md b/docs/content/docs/1.getting-started/2.installation/1.nuxt.md
index 0f42edf350..79b2a0c673 100644
--- a/docs/content/docs/1.getting-started/2.installation/1.nuxt.md
+++ b/docs/content/docs/1.getting-started/2.installation/1.nuxt.md
@@ -363,7 +363,7 @@ export default defineNuxtConfig({
})
```
-### `theme.prefix` :badge{label="4.2+"}
+### `theme.prefix` :badge{label="4.2+" class="align-text-top"}
Use the `theme.prefix` option to configure the same prefix you set on your Tailwind CSS import. This ensures Nuxt UI components use the correct prefixed utility classes and CSS variables.
@@ -453,7 +453,7 @@ export default defineNuxtConfig({
})
```
-### `experimental.componentDetection` :badge{label="4.1+"}
+### `experimental.componentDetection` :badge{label="4.1+" class="align-text-top"}
Use the `experimental.componentDetection` option to enable automatic component detection for tree-shaking. This feature scans your source code to detect which components are actually used and only generates the necessary CSS for those components (including their dependencies).
diff --git a/docs/content/docs/1.getting-started/2.installation/2.vue.md b/docs/content/docs/1.getting-started/2.installation/2.vue.md
index 59fb562ed9..aa9d4bcc0b 100644
--- a/docs/content/docs/1.getting-started/2.installation/2.vue.md
+++ b/docs/content/docs/1.getting-started/2.installation/2.vue.md
@@ -658,7 +658,7 @@ export default defineConfig({
})
```
-### `theme.prefix` :badge{label="4.2+"}
+### `theme.prefix` :badge{label="4.2+" class="align-text-top"}
Use the `theme.prefix` option to configure the same prefix you set on your Tailwind CSS import. This ensures Nuxt UI components use the correct prefixed utility classes and CSS variables.
diff --git a/docs/content/docs/2.components/0.index.md b/docs/content/docs/2.components/0.index.md
index 759ec7f423..ac31e82c16 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,16 @@ Components for building conversational interfaces and chatbots, powered by the *
Check out the **AI Chat template** on GitHub for a real-life example.
::
+## Editor :badge{label="Soon" class="align-text-top"}
+
+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"}
+
+::note{to="https://github.com/nuxt-ui-templates/editor" target="_blank"}
+Check out the **Editor template** on GitHub for a real-life example.
+::
+
## 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/command-palette.md b/docs/content/docs/2.components/command-palette.md
index 5ca1daf68a..1457cc5ffb 100644
--- a/docs/content/docs/2.components/command-palette.md
+++ b/docs/content/docs/2.components/command-palette.md
@@ -889,7 +889,7 @@ props:
---
::
-### With virtualization :badge{label="4.1+"}
+### With virtualization :badge{label="4.1+" class="align-text-top"}
Use the `virtualize` prop to enable virtualization for large lists as a boolean or an object with options like `{ estimateSize: 32, overscan: 12 }`.
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..865181a04e
--- /dev/null
+++ b/docs/content/docs/2.components/editor-drag-handle.md
@@ -0,0 +1,136 @@
+---
+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 provides drag-and-drop functionality for reordering editor blocks using the `@tiptap/extension-drag-handle-vue-3` package.
+
+::caution
+It must be used inside an [Editor](/docs/components/editor) component's default slot to have access to the editor instance.
+::
+
+It extends the [Button](/docs/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc.
+
+::component-example
+---
+collapse: true
+elevated: true
+name: 'editor-drag-handle-example'
+class: 'p-8'
+---
+::
+
+::callout{icon="i-custom-tiptap" to="https://tiptap.dev/docs/editor/extensions/functionality/drag-handle-vue" target="_blank"}
+Learn more about the Drag Handle extension in the TipTap documentation.
+::
+
+### 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 like duplicate, delete, move up/down, or transform blocks into different types.
+
+Listen to the `@node-change` event to track the currently hovered node and its position, then use `editor.chain().setMeta('lockDragHandle', open).run()`{lang="ts-type"} to lock the handle position while the menu is open.
+
+::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](/docs/components/button) next to the drag handle to open the [EditorSuggestionMenu](/docs/components/editor-suggestion-menu).
+
+Call the `onClick` slot function to get the current node position, then use `handlers.suggestion?.execute(editor, { pos: node?.pos }).run()`{lang="ts-type"} to insert new blocks at that position.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-drag-handle-suggestion-menu-example'
+class: '!p-0'
+---
+::
+
+## 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..5ca1478220
--- /dev/null
+++ b/docs/content/docs/2.components/editor-emoji-menu.md
@@ -0,0 +1,108 @@
+---
+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 displays a menu of emoji suggestions when typing the `:` character in the editor and inserts the selected emoji. It works alongside the `@tiptap/extension-emoji` package to provide emoji support.
+
+::note
+It uses the `useEditorMenu` composable built on top of TipTap's [Suggestion](https://tiptap.dev/docs/editor/api/utilities/suggestion) utility to filter items as you type and support keyboard navigation (arrow keys, enter to select, escape to close).
+::
+
+::caution
+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-emoji-menu-example'
+class: 'p-8'
+---
+::
+
+::warning
+The `@tiptap/extension-emoji` package is not installed by default, you need to install it separately.
+::
+
+::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'
+---
+::
+
+::note
+You can also pass an array of arrays to the `items` prop to create separated groups of items.
+::
+
+### 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..1410b1f271
--- /dev/null
+++ b/docs/content/docs/2.components/editor-mention-menu.md
@@ -0,0 +1,103 @@
+---
+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 displays a menu of user suggestions when typing the `@` character in the editor and inserts the selected mention using the `@tiptap/extension-mention` package.
+
+::note
+It uses the `useEditorMenu` composable built on top of TipTap's [Suggestion](https://tiptap.dev/docs/editor/api/utilities/suggestion) utility to filter items as you type and support keyboard navigation (arrow keys, enter to select, escape to close).
+::
+
+::caution
+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-mention-menu-example'
+class: 'p-8'
+---
+::
+
+::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'
+---
+::
+
+::note
+You can also pass an array of arrays to the `items` prop to create separated groups of items.
+::
+
+### 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-suggestion-menu.md b/docs/content/docs/2.components/editor-suggestion-menu.md
new file mode 100644
index 0000000000..f011c71d19
--- /dev/null
+++ b/docs/content/docs/2.components/editor-suggestion-menu.md
@@ -0,0 +1,104 @@
+---
+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 displays a menu of formatting and action suggestions when typing a trigger character in the editor and executes the corresponding [handler](/docs/components/editor#handlers) when an item is selected.
+
+::note
+It uses the `useEditorMenu` composable built on top of TipTap's [Suggestion](https://tiptap.dev/docs/editor/api/utilities/suggestion) utility to filter items as you type and support keyboard navigation (arrow keys, enter to select, escape to close).
+::
+
+::caution
+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-suggestion-menu-example'
+class: 'p-8'
+---
+::
+
+### 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"}](/docs/components/editor#handlers)
+- `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"}
+
+::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
+
+
+
+
+
+```
+
+### 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..36762e6e38
--- /dev/null
+++ b/docs/content/docs/2.components/editor-toolbar.md
@@ -0,0 +1,197 @@
+---
+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 displays a toolbar of formatting buttons that automatically sync their active state with the editor content. It supports three layout modes using the `@tiptap/vue-3/menus` package:
+- `fixed`{lang="ts-type"} (always visible)
+- `bubble`{lang="ts-type"} (appears on text selection)
+- `floating`{lang="ts-type"} (appears on empty lines)
+
+::caution
+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'
+---
+::
+
+::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.
+::
+
+### 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"}
+- `activeColor?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"`{lang="ts-type"}
+- `variant?: "solid" | "outline" | "soft" | "ghost" | "link" | "subtle"`{lang="ts-type"}
+- `activeVariant?: "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"}](/docs/components/editor#handlers)
+- `disabled?: boolean`{lang="ts-type"}
+- `loading?: boolean`{lang="ts-type"}
+- `active?: boolean`{lang="ts-type"}
+- `tooltip?: TooltipProps`{lang="ts-type"}
+- [`slot?: string`{lang="ts-type"}](#with-link-popover)
+- `onClick?: (e: MouseEvent) => void`{lang="ts-type"}
+- `items?: EditorToolbarItem[] | EditorToolbarItem[][]`{lang="ts-type"}
+- `class?: any`{lang="ts-type"}
+
+You can pass any property from the [Button](/docs/components/button#props) component such as `color`, `variant`, `size`, etc.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-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
+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"}.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-layout-example'
+class: 'p-8'
+options:
+ - name: layout
+ label: Layout
+ default: bubble
+ items:
+ - fixed
+ - bubble
+ - floating
+---
+::
+
+### 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
+
+
+
+
+
+```
+
+### 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.
+
+```vue
+
+
+
+
+
+```
+
+## Examples
+
+### With image toolbar
+
+Use the `should-show` prop to create context-specific toolbars that appear only for certain node types. This example shows a `bubble` toolbar with download and delete actions that only appears when an image is selected.
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-image-example'
+class: 'p-8'
+---
+::
+
+### With link popover
+
+This example demonstrates how to create a custom link popover using the `slot` property on toolbar items and the [Popover](/docs/components/popover) component.
+
+1. Create a Vue component that wraps a [Popover](/docs/components/popover) with link editing functionality:
+
+::component-example
+---
+preview: false
+collapse: true
+name: 'editor-link-popover'
+---
+::
+
+2. Use the custom component in the toolbar with a named slot:
+
+::component-example
+---
+elevated: true
+collapse: true
+name: 'editor-toolbar-custom-slot-example'
+class: 'p-8'
+---
+::
+
+## API
+
+### Props
+
+:component-props
+
+### Slots
+
+:component-slots
+
+## 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..6e631bdb40
--- /dev/null
+++ b/docs/content/docs/2.components/editor.md
@@ -0,0 +1,477 @@
+---
+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](https://tiptap.dev/). 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
+
+The Editor automatically detects the content format based on `v-model` type: strings are treated as `html`{lang="ts-type"} and objects as `json`{lang="ts-type"}.
+
+You can explicitly set the format using the `content-type` prop: `json`{lang="ts-type"}, `html`{lang="ts-type"}, or `markdown`{lang="ts-type"}.
+
+::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/content/docs/2.components/input-menu.md b/docs/content/docs/2.components/input-menu.md
index df07f7d3ec..56b4f0a59f 100644
--- a/docs/content/docs/2.components/input-menu.md
+++ b/docs/content/docs/2.components/input-menu.md
@@ -782,7 +782,7 @@ name: 'input-menu-filter-fields-example'
---
::
-### With virtualization :badge{label="4.1+"}
+### With virtualization :badge{label="4.1+" class="align-text-top"}
Use the `virtualize` prop to enable virtualization for large lists as a boolean or an object with options like `{ estimateSize: 32, overscan: 12 }`.
diff --git a/docs/content/docs/2.components/modal.md b/docs/content/docs/2.components/modal.md
index 41f9056e5e..622b54fb84 100644
--- a/docs/content/docs/2.components/modal.md
+++ b/docs/content/docs/2.components/modal.md
@@ -294,7 +294,7 @@ slots:
:placeholder{class="h-48"}
::
-### Scrollable :badge{label="4.2+"}
+### Scrollable :badge{label="4.2+" class="align-text-top"}
Use the `scrollable` prop to make the Modal's content scrollable within the overlay.
diff --git a/docs/content/docs/2.components/select-menu.md b/docs/content/docs/2.components/select-menu.md
index 3dbf2c236a..379e59db21 100644
--- a/docs/content/docs/2.components/select-menu.md
+++ b/docs/content/docs/2.components/select-menu.md
@@ -821,7 +821,7 @@ name: 'select-menu-filter-fields-example'
---
::
-### With virtualization :badge{label="4.1+"}
+### With virtualization :badge{label="4.1+" class="align-text-top"}
Use the `virtualize` prop to enable virtualization for large lists as a boolean or an object with options like `{ estimateSize: 32, overscan: 12 }`.
diff --git a/docs/content/docs/2.components/table.md b/docs/content/docs/2.components/table.md
index 8fab5286b0..9305004591 100644
--- a/docs/content/docs/2.components/table.md
+++ b/docs/content/docs/2.components/table.md
@@ -596,7 +596,7 @@ class: '!p-0'
---
::
-### With virtualization :badge{label="4.1+"}
+### With virtualization :badge{label="4.1+" class="align-text-top"}
Use the `virtualize` prop to enable virtualization for large datasets as a boolean or an object with options like `{ estimateSize: 65, overscan: 12 }`. You can also pass other [TanStack Virtual options](https://tanstack.com/virtual/latest/docs/api/virtualizer#optional-options) to customize the virtualization behavior.
diff --git a/docs/content/docs/2.components/toast.md b/docs/content/docs/2.components/toast.md
index a5917f30ae..9a741f4ae3 100644
--- a/docs/content/docs/2.components/toast.md
+++ b/docs/content/docs/2.components/toast.md
@@ -260,7 +260,7 @@ name: 'toast-example'
In this example, we use the `AppConfig` to configure the `duration` prop of the `Toaster` component globally.
::
-### Change global max :badge{label="4.1+"}
+### Change global max :badge{label="4.1+" class="align-text-top"}
Change the `toaster.max` prop on the [App](/docs/components/app#props) component to change the max number of toasts displayed at once.
diff --git a/docs/content/docs/2.components/tree.md b/docs/content/docs/2.components/tree.md
index 6a53d1bdc9..3c43c63baa 100644
--- a/docs/content/docs/2.components/tree.md
+++ b/docs/content/docs/2.components/tree.md
@@ -148,7 +148,7 @@ props:
---
::
-### Nested :badge{label="4.1+"}
+### Nested :badge{label="4.1+" class="align-text-top"}
Use the `nested` prop to control whether the Tree is rendered with nested structure or as a flat list. Defaults to `true`.
@@ -501,7 +501,7 @@ props:
This lets you select a parent item without expanding or collapsing its children.
::
-### With checkbox in items :badge{label="4.1+"}
+### With checkbox in items :badge{label="4.1+" class="align-text-top"}
You can use the `item-leading` slot to add a [Checkbox](/docs/components/checkbox) to the items. Use the `multiple`, `propagate-select` and `bubble-select` props to enable multi-selection with parent-child relationship and the `select` and `toggle` events to control the selected and expanded state of the items.
@@ -518,7 +518,7 @@ props:
This example uses the `as` prop to change the items from `button` to `div` as the [`Checkbox`](/docs/components/checkbox) is also rendered as a `button`.
::
-### With drag and drop :badge{label="4.1+"}
+### With drag and drop :badge{label="4.1+" class="align-text-top"}
Use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the Tree. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.
@@ -534,7 +534,7 @@ name: 'tree-drag-and-drop-example'
This example sets the `nested` prop to `false` to have a flat list of items so that the items can be dragged and dropped.
::
-### With virtualization :badge{label="4.1+"}
+### With virtualization :badge{label="4.1+" class="align-text-top"}
Use the `virtualize` prop to enable virtualization for large lists as a boolean or an object with options like `{ estimateSize: 32, overscan: 12 }`.
diff --git a/docs/content/templates.yml b/docs/content/templates.yml
index bafaf6ff3d..0663da8658 100644
--- a/docs/content/templates.yml
+++ b/docs/content/templates.yml
@@ -5,6 +5,35 @@ hero:
description: 'Explore ready-made templates powered by our Vue components. These templates are fully responsive, accessible, and easy to customize, so you can launch your project quickly and effortlessly.'
navigation: false
items:
+ - title: 'Editor'
+ description: "A rich text editor template powered by TipTap with support for markdown, HTML, and JSON content types."
+ icon: i-lucide-file-text
+ framework: nuxt
+ features:
+ - title: Slash commands
+ icon: i-lucide-square-slash
+ - title: Drag & drop blocks
+ icon: i-lucide-move
+ - title: Real-time collaboration
+ icon: i-lucide-users
+ deploy_links:
+ - label: Vercel
+ to: https://vercel.com/new/clone?repository-name=editor&repository-url=https%3A%2F%2Fgithub.com%2Fnuxt-ui-templates%2Feditor&demo-image=https%3A%2F%2Fui.nuxt.com%2Fassets%2Ftemplates%2Fnuxt%2Feditor-dark.png&demo-url=https%3A%2F%2Feditor-template.nuxt.dev%2F&demo-title=Nuxt%20Editor%20Template&demo-description=A%20rich%20text%20editor%20template%20built%20with%20Nuxt%20UI%20and%20TipTap.&env=NUXT_PUBLIC_PARTYKIT_HOST&envDescription=PartyKit%20host%20URL%20for%20real-time%20collaboration%20(optional)&envLink=https%3A%2F%2Fdocs.partykit.io%2Fquickstart%2F
+ target: _blank
+ icon: i-simple-icons-vercel
+ - label: Netlify
+ to: https://app.netlify.com/start/deploy?repository=https://github.com/nuxt-ui-templates/editor
+ target: _blank
+ icon: i-simple-icons-netlify
+ links:
+ - label: Preview
+ to: https://editor-template.nuxt.dev
+ target: _blank
+ icon: i-lucide-link
+ - label: GitHub
+ to: https://github.com/nuxt-ui-templates/editor
+ target: _blank
+ icon: i-simple-icons-github
- title: 'Changelog'
description: "A changelog template to display your repository releases notes from GitHub powered by Nuxt MDC."
icon: i-lucide-newspaper
diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts
index 7ce0166e62..c65f766c6d 100644
--- a/docs/nuxt.config.ts
+++ b/docs/nuxt.config.ts
@@ -186,7 +186,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']
}
},
@@ -197,6 +197,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 3cc2ee2fd0..b354f36088 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -22,6 +22,8 @@
"@nuxt/ui": "workspace:*",
"@nuxtjs/mcp-toolkit": "^0.4.1",
"@nuxtjs/plausible": "^2.0.1",
+ "@tiptap/extension-emoji": "3.12.0",
+ "@tiptap/extension-text-align": "3.12.0",
"@octokit/rest": "^22.0.1",
"@regle/core": "^1.11.0",
"@regle/rules": "^1.11.0",
diff --git a/docs/public/assets/templates/nuxt/editor-dark.png b/docs/public/assets/templates/nuxt/editor-dark.png
new file mode 100644
index 0000000000..1f0a310444
Binary files /dev/null and b/docs/public/assets/templates/nuxt/editor-dark.png differ
diff --git a/docs/public/assets/templates/nuxt/editor-light.png b/docs/public/assets/templates/nuxt/editor-light.png
new file mode 100644
index 0000000000..cdf964f9df
Binary files /dev/null and b/docs/public/assets/templates/nuxt/editor-light.png differ
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 38e8213bde..8632aaa466 100644
--- a/package.json
+++ b/package.json
@@ -133,6 +133,19 @@
"@tailwindcss/vite": "^4.1.17",
"@tanstack/vue-table": "^8.21.3",
"@tanstack/vue-virtual": "^3.13.12",
+ "@tiptap/core": "3.12.0",
+ "@tiptap/extension-bubble-menu": "3.12.0",
+ "@tiptap/extension-drag-handle-vue-3": "3.12.0",
+ "@tiptap/extension-floating-menu": "3.12.0",
+ "@tiptap/extension-horizontal-rule": "3.12.0",
+ "@tiptap/extension-image": "3.12.0",
+ "@tiptap/extension-mention": "3.12.0",
+ "@tiptap/extension-placeholder": "3.12.0",
+ "@tiptap/markdown": "3.12.0",
+ "@tiptap/pm": "3.12.0",
+ "@tiptap/starter-kit": "3.12.0",
+ "@tiptap/suggestion": "3.12.0",
+ "@tiptap/vue-3": "3.12.0",
"@unhead/vue": "^2.0.19",
"@vueuse/core": "^14.1.0",
"@vueuse/integrations": "^14.1.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..17d4cc5cb6
--- /dev/null
+++ b/playgrounds/nuxt/app/components/editor/EditorLinkPopover.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
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..7f12530ff5
--- /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`] = `
+"