Installation
Install the core package
Install the core package and any extensions you need:
npm install @tiptap/core @tiptap/starter-kit
Core Concepts
Editor Class
TheEditor class is the core of Tiptap. It manages the editor state, extensions, and view.
Type Signature
class Editor {
constructor(options: EditorOptions)
// Properties
view: EditorView
state: EditorState
schema: Schema
isDestroyed: boolean
isFocused: boolean
// Methods
destroy(): void
setOptions(options: Partial<EditorOptions>): void
getHTML(): string
getJSON(): JSONContent
getText(): string
setContent(content: Content): void
commands: Commands
chain(): ChainedCommands
can(): CanCommands
isActive(name: string, attributes?: {}): boolean
// ... and many more
}
interface EditorOptions {
element: Element
extensions: Extension[]
content?: Content
editable?: boolean
autofocus?: boolean | 'start' | 'end' | number
injectCSS?: boolean
onCreate?: (props: { editor: Editor }) => void
onUpdate?: (props: { editor: Editor, transaction: Transaction }) => void
onSelectionUpdate?: (props: { editor: Editor, transaction: Transaction }) => void
onTransaction?: (props: { editor: Editor, transaction: Transaction }) => void
onFocus?: (props: { editor: Editor, event: FocusEvent }) => void
onBlur?: (props: { editor: Editor, event: FocusEvent }) => void
onDestroy?: () => void
// ... and more
}
Basic Usage
Here’s a minimal example of creating an editor:import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
// Create the editor
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
HTML Structure
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tiptap Editor</title>
<style>
/* Basic editor styles */
.tiptap {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
min-height: 200px;
}
.tiptap:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
</style>
</head>
<body>
<div id="editor"></div>
<script type="module" src="/main.js"></script>
</body>
</html>
Complete Working Examples
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
// Create editor
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: `
<h2>Hi there,</h2>
<p>this is a <em>basic</em> example of <strong>Tiptap</strong>.</p>
`,
})
Working with Content
Getting Content
// Get HTML
const html = editor.getHTML()
console.log(html)
// Output: '<p>Hello <strong>World</strong>!</p>'
// Get JSON
const json = editor.getJSON()
console.log(json)
// Output: { type: 'doc', content: [...] }
// Get plain text
const text = editor.getText()
console.log(text)
// Output: 'Hello World!'
// Get text with custom separator
const textWithBreaks = editor.getText({ blockSeparator: '\n\n' })
Setting Content
// Set HTML content
editor.commands.setContent('<p>New <strong>content</strong>!</p>')
// Set JSON content
editor.commands.setContent({
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{ type: 'text', text: 'New content!' },
],
},
],
})
// Clear content
editor.commands.clearContent()
// Insert content at current position
editor.commands.insertContent('<p>Inserted content</p>')
Commands
Tiptap provides a powerful command system:// Single command
editor.commands.toggleBold()
// Chain multiple commands
editor
.chain()
.focus()
.toggleBold()
.toggleItalic()
.run()
// Check if a command can be executed
if (editor.can().toggleBold()) {
console.log('Can toggle bold')
}
// Common commands
editor.commands.setHeading({ level: 1 })
editor.commands.toggleBulletList()
editor.commands.toggleOrderedList()
editor.commands.setHorizontalRule()
editor.commands.undo()
editor.commands.redo()
Check Active States
// Check if mark/node is active
if (editor.isActive('bold')) {
console.log('Bold is active')
}
if (editor.isActive('heading', { level: 1 })) {
console.log('H1 is active')
}
if (editor.isActive('link')) {
console.log('Link is active')
}
Event Handling
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
// Listen to events
editor.on('update', ({ editor }) => {
console.log('Content updated:', editor.getHTML())
})
editor.on('selectionUpdate', ({ editor }) => {
console.log('Selection changed')
})
editor.on('focus', ({ editor, event }) => {
console.log('Editor focused')
})
editor.on('blur', ({ editor, event }) => {
console.log('Editor blurred')
})
// Remove event listener
const handler = ({ editor }) => console.log('Updated')
editor.on('update', handler)
editor.off('update', handler)
Toolbar Example
Here’s a complete example with a toolbar:<!DOCTYPE html>
<html>
<head>
<style>
.toolbar {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
background: #f9f9f9;
}
.toolbar button {
padding: 0.5rem 1rem;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
cursor: pointer;
}
.toolbar button:hover {
background: #f0f0f0;
}
.toolbar button.is-active {
background: #333;
color: white;
}
.tiptap {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
min-height: 200px;
}
</style>
</head>
<body>
<div id="toolbar" class="toolbar"></div>
<div id="editor"></div>
<script type="module">
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
// Helper to create toolbar buttons
function createButton(label, command, checkActive) {
const button = document.createElement('button')
button.textContent = label
button.addEventListener('click', command)
if (checkActive) {
editor.on('selectionUpdate', () => {
if (checkActive()) {
button.classList.add('is-active')
} else {
button.classList.remove('is-active')
}
})
}
return button
}
const toolbar = document.querySelector('#toolbar')
// Add buttons
toolbar.appendChild(createButton(
'Bold',
() => editor.chain().focus().toggleBold().run(),
() => editor.isActive('bold')
))
toolbar.appendChild(createButton(
'Italic',
() => editor.chain().focus().toggleItalic().run(),
() => editor.isActive('italic')
))
toolbar.appendChild(createButton(
'H1',
() => editor.chain().focus().toggleHeading({ level: 1 }).run(),
() => editor.isActive('heading', { level: 1 })
))
toolbar.appendChild(createButton(
'Bullet List',
() => editor.chain().focus().toggleBulletList().run(),
() => editor.isActive('bulletList')
))
</script>
</body>
</html>
Advanced: Custom Extensions
Create custom extensions in vanilla JavaScript:import { Extension } from '@tiptap/core'
const CustomExtension = Extension.create({
name: 'customExtension',
addOptions() {
return {
myOption: 'default value',
}
},
addCommands() {
return {
customCommand: () => ({ commands }) => {
console.log('Custom command executed')
return true
},
}
},
addKeyboardShortcuts() {
return {
'Mod-k': () => {
console.log('Mod-k pressed')
return true
},
}
},
})
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [
StarterKit,
CustomExtension.configure({
myOption: 'custom value',
}),
],
})
// Use custom command
editor.commands.customCommand()
Cleanup
Always destroy the editor when it’s no longer needed:const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
// Later, when you're done:
editor.destroy()
Using with Build Tools
- Vite
- Webpack
- CDN (Browser)
// main.js
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import './style.css'
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
// index.js
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
document.addEventListener('DOMContentLoaded', () => {
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
})
<!DOCTYPE html>
<html>
<head>
<script type="module">
import { Editor } from 'https://esm.sh/@tiptap/core'
import StarterKit from 'https://esm.sh/@tiptap/starter-kit'
window.addEventListener('DOMContentLoaded', () => {
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [StarterKit],
content: '<p>Hello World!</p>',
})
})
</script>
</head>
<body>
<div id="editor"></div>
</body>
</html>
Next Steps
Extensions
Explore available extensions to enhance your editor
Commands
Learn about all available commands
Editor API
Full reference for the Editor class
Custom Extensions
Build your own extensions