Getting started
Your first editor
Turn a textarea into an editor, mount one into any element, or build it from Editor.create and createUI, then read its HTML.
There are three ways to start an editor: upgrade a textarea in a form, mount an editor into any element with mountEditor, or build one yourself from Editor.create and createUI. This page shows each, then how to read the content, listen for changes, and set the label and the language.
Upgrade a textarea
This is the shortest way, and it works with the CDN script or with npm.
- Load
viveeditor.cssandviveeditor.classic.iife.jswith your exact version, as shown on Installation. - Add
data-editorto the textarea. Keep itsname, and a<label for>that points at it. - On the server, read the field as usual and sanitize the HTML before you store it; see Server sanitizing.
<form method="post">
<label for="body">Article</label>
<textarea id="body" name="body" data-editor required></textarea>
<button>Publish</button>
</form>
The textarea stays in the form, hidden, and always holds the latest HTML ('' when the document is empty). It fires input on every change. With required, an empty editor cancels the submit, marks itself aria-invalid="true", fires a vive:invalid event on the textarea and takes the focus. While images are still uploading, the submit waits and goes out once they are done. Add data-files="false" to a textarea to hide its File menu.
From your own code
With npm, call upgradeAll() to upgrade every textarea[data-editor], or upgradeTextarea() for one:
import { upgradeTextarea } from 'viveeditor'
import 'viveeditor/style.css'
const field = await upgradeTextarea(document.querySelector('#body'), {
label: 'Article body',
ui: { locale: 'en' },
})
console.log(field.editor.getHTML())
upgradeAll(root, options) limits the search to root. Calling upgradeTextarea twice at the same time returns the same promise. field.destroy() brings the textarea back and leaves the final HTML in it. Pass ui: false to leave out the toolbar.
Mount into any element
mountEditor puts the full editor, with every extension and the toolbar, into any element. The framework components are built on it.
import { mountEditor } from 'viveeditor'
import 'viveeditor/style.css'
const mount = mountEditor(document.querySelector('#editor'), {
value: '<p>Hello</p>',
locale: 'en',
label: 'Article body',
onChange: (html) => console.log(html),
})
const editor = await mount.ready
- The value is HTML, and an empty document is
''. getValue(),setValue(html),setEditable(editable)anddestroy()control it.setValuewith a new value does not callonChangeand does not go into the undo history.extensionsreplaces the default set;ui: falsedrops the toolbar.- If you call
destroy()before the editor is ready,readyrejects withMountDestroyedError. - No File menu converters are loaded by default; pass them in
ui.files.
Build it yourself
Editor.create gives you the engine with only the extensions you list. createUI adds the toolbar, the selection bubble, the / menu and the panels.
import { Editor } from '@viveeditor/core'
import '@viveeditor/core/style.css'
import { basicExtensions } from '@viveeditor/ext-basic'
import { listExtensions } from '@viveeditor/ext-list'
import { createUI } from '@viveeditor/ui'
import '@viveeditor/ui/style.css'
const editor = await Editor.create(document.querySelector('#editor'), {
extensions: [...basicExtensions, ...listExtensions],
content: '<p>Hello <strong>world</strong></p>',
label: 'Article body',
})
const ui = createUI(editor, { locale: 'en', theme: 'auto' })
createUI mounts next to the editing area and is removed when the editor is destroyed. It shows only the tools whose commands are registered, so a tool appears once you add its extension. ui.destroy() removes it earlier. The options are covered on Toolbar and shortcuts.
Read the content
editor.getHTML()returns the document as HTML.editor.getJSON()returns ProseMirror JSON, the canonical storage format.editor.getText()returns plain text, one line per block.editor.isEmptytells you whether the document is empty.editor.setContent(content)loads HTML or JSON. It does not go into the undo history.
Store either HTML or JSON. Both stay readable by later 0.x versions; see Versioning.
Listen for changes
const stop = editor.on('change', ({ editor }) => {
console.log(editor.getHTML())
})
editor.on('error', ({ error }) => console.error(error))
changefires when the document changes.updatefires on any state change, such as a new selection.focus,blur,destroyanderrorare also available.onreturns a function that removes the listener;offdoes the same.
An error thrown inside one listener does not stop the others. It is reported through the error event.
Label and language
Give every editor a label, such as label: 'Article body'. Screen readers announce it as the name of the editing area. With a textarea, keep a visible <label for>; the editor takes its name from it when you pass no label.
The interface speaks Turkish and English. Pass locale to choose. Without it, the editor follows <html lang="...">, then the browser language: anything starting with tr gives Turkish, everything else English. To change single strings, pass messages to createUI, for example createUI(editor, { messages: { toolbar: 'Formatting' } }).
Next steps
- Frameworks: the same editor in React, Vue, Svelte and Angular.
- Images and uploads: connect your upload endpoint.