Text Editor
A full-featured rich text editor with Word-like capabilities: font styling, color pickers, checklists, tables, image/video embeds, emoji, find & replace, footnotes, print, fullscreen, and more. Built on contenteditable with no external dependencies.
<script setup>
import { ref } from 'vue'
const content = ref('')
</script>
<template>
<VxTextEditor v-model="content" placeholder="Write something amazing..." />
</template>With Initial Content
<template>
<VxTextEditor v-model="content" />
</template>Image Upload with beforeUpload
The beforeUpload prop receives the selected File object. Your function should upload it to your server and return the image URL. The editor inserts the image once the URL is returned.
<script setup>
import { ref } from 'vue'
const content = ref('')
async function handleUpload(file) {
// Upload file to your server
const formData = new FormData()
formData.append('image', file)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const data = await response.json()
// Return the URL — the editor inserts <img src="url">
return data.url
}
</script>
<template>
<VxTextEditor
v-model="content"
:before-upload="handleUpload"
:max-file-size="5 * 1024 * 1024"
@upload-error="(err, file) => console.error(err, file)"
/>
</template>How it works:
- User clicks image toolbar button → "Upload" tab → selects file from disk
beforeUpload(file)is called with the rawFileobject- Your function uploads to server and returns the URL string
- Editor inserts
<img src="returned-url">at cursor position
Also supports:
- Drag & drop — drop images directly onto the editor
- Paste — paste images from clipboard (screenshots, copied images)
- URL tab — manually enter an image URL as fallback
Resizable Images
Click any image in the editor to select it. Drag the corner or side handles to resize. Press Delete or Backspace to remove a selected image.
<template>
<VxTextEditor v-model="content" :before-upload="handleUpload" />
<!-- Click any inserted image to select it, then drag handles to resize -->
</template>Table
Click the table button in the toolbar to open a grid picker. Hover to select size and click to insert.
<template>
<VxTextEditor v-model="content" />
</template>Right-click context menu on any cell:
- Insert row above / below
- Insert column left / right
- Merge cell right — merges with the cell to the right (increases colspan)
- Merge cell down — merges with the cell below (increases rowspan)
- Split cell — appears when colspan > 1 or rowspan > 1, splits the merged cell back
- Colspan / Rowspan controls — +/- buttons to set exact span values
- Delete row / column / table
Column resize — hover near a column border to see the resize cursor, then drag to resize.
Font Family & Size
Use the font family and font size dropdowns to change typography. Select text first, then pick a font or size.
<template>
<VxTextEditor
v-model="content"
:toolbar="['fontFamily', 'fontSize', '|', 'bold', 'italic', '|', 'undo', 'redo']"
/>
</template>Text & Highlight Color
Apply text color or background highlight color to selected text. Choose from 80 preset colors.
<template>
<VxTextEditor
v-model="content"
:toolbar="['bold', 'italic', '|', 'textColor', 'highlightColor', '|', 'undo', 'redo']"
/>
</template>Checklist
Create interactive checklists. Click the checkbox area to toggle items.
<template>
<VxTextEditor
v-model="content"
:toolbar="['checklist', '|', 'bulletList', 'orderedList', '|', 'bold', 'italic', '|', 'undo', 'redo']"
/>
</template>Video Embed
Embed YouTube or Vimeo videos. Click the video button, paste a URL, and the editor inserts a responsive iframe.
<template>
<VxTextEditor
v-model="content"
:toolbar="['video', 'image', 'link', '|', 'bold', 'italic', '|', 'undo', 'redo']"
/>
</template>Import & Export
Import Word documents (.docx/.doc), export to Word, or export to PDF. The toolbar buttons use Vexel Icons.
<script setup>
import { ref } from 'vue'
const content = ref('')
</script>
<template>
<VxTextEditor
v-model="content"
:toolbar="[
'bold', 'italic', 'underline', '|',
'heading1', 'heading2', '|',
'bulletList', 'orderedList', '|',
'importWord', 'exportWord', 'exportPdf',
]"
/>
</template>Features:
- Import Word — Click the import icon to select a
.docxor.docfile. Content is parsed and inserted into the editor. - Export Word — Downloads the editor content as a Word-compatible
.docfile with proper formatting, tables, and styles. - Export PDF — Opens the browser print dialog for saving as PDF with a clean print stylesheet.
Full-Featured
All Word-like toolbar items enabled — font styling, colors, lists, media, emoji, special characters, find & replace, footnotes, page breaks, print, and fullscreen.
<template>
<VxTextEditor v-model="content" :before-upload="handleUpload" show-word-count show-character-count />
</template>Custom Toolbar
Pass a custom array of toolbar items. Use '|' for separators.
<template>
<VxTextEditor
v-model="content"
:toolbar="['bold', 'italic', 'underline', '|', 'bulletList', 'orderedList', '|', 'image', 'link', '|', 'undo', 'redo']"
:before-upload="handleUpload"
/>
</template>No Toolbar
Set :toolbar="false" to hide the toolbar entirely. Keyboard shortcuts still work.
<template>
<VxTextEditor v-model="content" :toolbar="false" />
</template>Word & Character Count
<template>
<VxTextEditor v-model="content" show-word-count show-character-count />
</template>Min / Max Height
<template>
<VxTextEditor v-model="content" min-height="100px" max-height="300px" />
</template>Read Only
<template>
<VxTextEditor v-model="content" readonly />
</template>Disabled
<template>
<VxTextEditor model-value="<p>Disabled content</p>" disabled />
</template>HTML Output
<script setup>
import { ref } from 'vue'
const content = ref('')
</script>
<template>
<VxTextEditor v-model="content" />
<pre>{{ content }}</pre>
</template>Toolbar Items
All available toolbar items:
| Item | Description |
|---|---|
fontFamily | Font family dropdown (Arial, Times New Roman, Georgia, etc.) |
fontSize | Font size dropdown (8px – 72px) |
bold | Bold text |
italic | Italic text |
underline | Underline text |
strikethrough | Strikethrough text |
heading1 – heading6 | Heading levels 1–6 |
textColor | Text color picker (80 preset colors) |
highlightColor | Background highlight color picker |
bulletList | Unordered list |
orderedList | Ordered list |
checklist | Interactive checklist with checkboxes |
blockquote | Blockquote |
code | Inline code |
codeBlock | Code block |
link | Insert/edit link |
image | Upload image or insert by URL |
video | Embed YouTube or Vimeo video |
table | Insert table with grid picker; right-click cells for row/column/merge actions |
emoji | Emoji picker (80 emojis) |
specialChars | Special characters picker (60 characters) |
alignLeft | Align left |
alignCenter | Align center |
alignRight | Align right |
alignJustify | Justify |
indent | Increase indent |
outdent | Decrease indent |
lineHeight | Line height picker (1.0 – 3.0) |
horizontalRule | Horizontal rule |
pageBreak | Insert page break |
footnote | Insert numbered footnote |
subscript | Subscript |
superscript | Superscript |
findReplace | Find & replace bar with next/prev navigation |
undo | Undo |
redo | Redo |
clear | Clear formatting |
print | Print editor content |
fullscreen | Toggle fullscreen editing mode |
| | Separator |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | '' | HTML content (v-model) |
placeholder | string | 'Start writing...' | Placeholder text |
disabled | boolean | false | Disable the editor |
readonly | boolean | false | Read-only mode |
toolbar | TextEditorToolbarItem[] | false | Default toolbar | Toolbar configuration |
minHeight | string | '200px' | Minimum editor height |
maxHeight | string | undefined | Maximum editor height (enables scroll) |
showCharacterCount | boolean | false | Show character count |
showWordCount | boolean | false | Show word count |
autofocus | boolean | false | Auto-focus on mount |
beforeUpload | (file: File) => Promise<string> | string | undefined | Upload handler — receives file, must return image URL |
accept | string | 'image/*' | Accepted file types for the file input |
maxFileSize | number | undefined | Maximum file size in bytes |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | Emitted when content changes |
focus | FocusEvent | Emitted when editor receives focus |
blur | FocusEvent | Emitted when editor loses focus |
change | string | Emitted when content changes |
uploadError | (error: Error, file: File) | Emitted when image upload fails or exceeds size limit |
Exposed Methods
| Method | Description |
|---|---|
focus() | Focus the editor |
blur() | Blur the editor |
getHTML() | Get current HTML content |
getText() | Get current plain text content |
exec(command, value?) | Execute a document command |