Vue 3 components and composables for building rich text editors with tiptap
—
Vue 3 components for rendering editor content and UI elements with full integration into Vue's component system.
The main component for rendering the editor's content area where users can type and edit text.
/**
* Vue component that renders the editor content area
* Handles editor mounting and Vue context integration
*/
const EditorContent: DefineComponent<{
/** The editor instance to render content for */
editor: {
type: PropType<Editor>;
default: null;
};
}>;Key Features:
Usage Examples:
<template>
<div>
<EditorContent :editor="editor" />
</div>
</template>
<script setup>
import { useEditor, EditorContent } from "@tiptap/vue-3";
import StarterKit from "@tiptap/starter-kit";
const editor = useEditor({
content: '<p>Hello World!</p>',
extensions: [StarterKit],
});
</script>Component for rendering the content area within custom node views.
/**
* Component for rendering node view content areas
* Used inside custom node view components
*/
const NodeViewContent: DefineComponent<{
/** HTML tag to render as */
as?: {
type: PropType<string>;
default: 'div';
};
}>;Key Features:
white-space: pre-wrap stylingdata-node-view-content attributeas propUsage Examples:
<!-- In a custom node view component -->
<template>
<div class="my-custom-node">
<h3>Custom Node</h3>
<NodeViewContent as="div" />
</div>
</template>
<script setup>
import { NodeViewContent } from "@tiptap/vue-3";
</script>Wrapper component for custom node views that handles drag events and decorations.
/**
* Wrapper component for custom node views
* Handles drag events and decoration styling
*/
const NodeViewWrapper: DefineComponent<{
/** HTML tag to render as */
as?: {
type: PropType<string>;
default: 'div';
};
}>;Injected Dependencies:
onDragStart: Function - Drag start event handlerdecorationClasses: String - CSS classes for ProseMirror decorationsKey Features:
data-node-view-wrapper attributeUsage Examples:
<!-- In a custom node view component -->
<template>
<NodeViewWrapper as="article" class="my-wrapper">
<div class="node-header">
<button @click="deleteNode">Delete</button>
</div>
<NodeViewContent />
</NodeViewWrapper>
</template>
<script setup>
import { NodeViewContent, NodeViewWrapper } from "@tiptap/vue-3";
import { inject } from 'vue';
// Access injected dependencies
const onDragStart = inject('onDragStart');
const decorationClasses = inject('decorationClasses');
// Node view props are automatically provided
const props = defineProps(nodeViewProps);
const deleteNode = () => {
props.deleteNode();
};
</script>Slot Usage:
<template>
<NodeViewWrapper>
<!-- Default slot content -->
<div class="custom-content">
<NodeViewContent />
</div>
</NodeViewWrapper>
</template>Styling Integration:
<template>
<NodeViewWrapper class="prose-node" :class="additionalClasses">
<NodeViewContent class="content-area" />
</NodeViewWrapper>
</template>
<style scoped>
.prose-node {
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 1rem;
}
.content-area {
min-height: 2rem;
outline: none;
}
</style>Event Handling:
<template>
<NodeViewWrapper @click="handleClick" @keydown="handleKeydown">
<div class="toolbar">
<button @click="updateAttributes({ alignment: 'left' })">Left</button>
<button @click="updateAttributes({ alignment: 'center' })">Center</button>
</div>
<NodeViewContent />
</NodeViewWrapper>
</template>
<script setup>
import { nodeViewProps } from "@tiptap/vue-3";
const props = defineProps(nodeViewProps);
const handleClick = (event) => {
console.log('Node clicked', event);
};
const handleKeydown = (event) => {
if (event.key === 'Delete') {
props.deleteNode();
}
};
</script>interface NodeViewProps {
editor: Editor;
node: ProseMirrorNode;
decorations: DecorationWithType[];
selected: boolean;
extension: Node;
getPos: () => number | undefined;
updateAttributes: (attributes: Record<string, any>) => void;
deleteNode: () => void;
view: EditorView;
innerDecorations: DecorationSource;
HTMLAttributes: Record<string, any>;
}
const nodeViewProps: {
editor: { type: PropType<NodeViewProps['editor']>; required: true };
node: { type: PropType<NodeViewProps['node']>; required: true };
decorations: { type: PropType<NodeViewProps['decorations']>; required: true };
selected: { type: PropType<NodeViewProps['selected']>; required: true };
extension: { type: PropType<NodeViewProps['extension']>; required: true };
getPos: { type: PropType<NodeViewProps['getPos']>; required: true };
updateAttributes: { type: PropType<NodeViewProps['updateAttributes']>; required: true };
deleteNode: { type: PropType<NodeViewProps['deleteNode']>; required: true };
view: { type: PropType<NodeViewProps['view']>; required: true };
innerDecorations: { type: PropType<NodeViewProps['innerDecorations']>; required: true };
HTMLAttributes: { type: PropType<NodeViewProps['HTMLAttributes']>; required: true };
};Install with Tessl CLI
npx tessl i tessl/npm-tiptap--vue-3