name: quill description: Use when working with the Quill rich text editor. Covers initialization, configuration, API methods, Delta format, formats, modules, themes, registries, and customization.
Quill — Rich Text Editor
Quill is a modern rich text editor built for compatibility and extensibility. It uses a custom document model (Parchment) and represents content/changes as Deltas.
Installation
CDN (jsDelivr)
Full build (includes themes, common formats, modules):
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet">
Core build (minimal, no theme or formats):
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.core.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.core.js"></script>
npm
npm install quill
import Quill from 'quill';
// Or core only:
// import Quill from 'quill/core';
const quill = new Quill('#editor');
Import styles:
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
Component Access
// Via Quill.import() (works in both CDN and npm)
const Delta = Quill.import('delta');
const Parchment = Quill.import('parchment');
const Link = Quill.import('formats/link');
// Or direct ES6 imports in npm
import { Delta } from 'quill';
Quickstart
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet">
<div id="editor">
<p>Hello World!</p>
<p>Some initial <strong>bold</strong> text</p>
<p><br /></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<script>
const quill = new Quill('#editor', {
theme: 'snow'
});
</script>
Configuration
Pass options as the second argument to new Quill():
const quill = new Quill('#editor', {
debug: 'info',
modules: {
toolbar: true,
},
placeholder: 'Compose an epic...',
theme: 'snow'
});
Options Reference
| Option | Default | Description |
|---|---|---|
bounds | document.body | DOM element or CSS selector to confine UI elements (tooltips, etc.) |
debug | 'warn' | Log level: 'error', 'warn', 'log', 'info', true (='log'), false |
formats | null | Array of allowed format names. null allows all. Ignored if registry is set |
placeholder | None | Placeholder text when editor is empty |
readOnly | false | Instantiate in read-only mode |
registry | null | Custom Parchment.Registry for isolated format sets |
theme | 'snow' | Theme name: 'snow', 'bubble', or falsy for minimal |
Restricting Formats
const quill = new Quill('#editor', {
formats: ['bold', 'italic', 'underline']
});
Core API
Content Methods
All content methods accept source: 'user', 'api', or 'silent'. User-source calls are ignored when the editor is disabled.
| Method | Signature | Description |
|---|---|---|
deleteText | deleteText(index, length, source='api'): Delta | Delete text at index |
getContents | getContents(index=0, length=remaining): Delta | Get editor contents as Delta |
getLength | getLength(): number | Get content length (empty editor = 1) |
getText | getText(index=0, length=remaining): string | Get plain text content |
getSemanticHTML | getSemanticHTML(index=0, length=remaining): string | Get HTML representation |
insertEmbed | insertEmbed(index, type, value, source='api'): Delta | Insert embed (image, video, etc.) |
insertText | insertText(index, text, formats?, source='api'): Delta | Insert text with optional formatting |
setContents | setContents(delta, source='api'): Delta | Overwrite editor with Delta |
setText | setText(text, source='api'): Delta | Set plain text content |
updateContents | updateContents(delta, source='api'): Delta | Apply Delta changes to editor |
Formatting Methods
| Method | Signature | Description |
|---|---|---|
format | format(name, value, source='api'): Delta | Format at current selection. Sets active format if selection is collapsed |
formatLine | formatLine(index, length, format?, value?, source='api'): Delta | Format lines in range |
formatText | formatText(index, length, format?, value?, source='api'): Delta | Format text in range |
getFormat | getFormat(range?): Record<string, unknown> | Get active formats at range or selection |
removeFormat | removeFormat(index, length, source='api'): Delta | Remove all formatting in range |
Selection Methods
| Method | Signature | Description |
|---|---|---|
getBounds | getBounds(index, length=0): {left, top, height, width} | Pixel position of text at index |
getSelection | getSelection(focus=false): {index, length} | null | Get current selection range |
setSelection | setSelection(index, length=0, source='api') | Set selection range |
scrollSelectionIntoView | scrollSelectionIntoView() | Scroll selection into visible area |
Editor State
| Method | Signature | Description |
|---|---|---|
blur | blur() | Remove focus |
disable | disable() | Disable user input |
enable | enable(enabled=true) | Enable/disable user input |
focus | focus({preventScroll?}={}) | Focus editor and restore last range |
hasFocus | hasFocus(): boolean | Check if editor has focus |
update | update(source='user') | Synchronously check for updates and fire events |
Events
quill.on('text-change', (delta, oldContents, source) => {
// Content changed
});
quill.on('selection-change', (range, oldRange, source) => {
// Selection changed. null range = focus lost
});
quill.on('editor-change', (eventName, ...args) => {
// Fires for both text-change and selection-change, even with 'silent' source
});
Event methods: on(name, handler), once(name, handler), off(name, handler).
Delta Format
Deltas describe Quill documents and changes. They are a strict JSON subset.
Document Representation
{
ops: [
{ insert: 'Gandalf', attributes: { bold: true } },
{ insert: ' the ' },
{ insert: 'Grey', attributes: { color: '#cccccc' } }
]
}
Key Rules
- All Quill documents must end with a
\nnewline - Embeds use object insert:
{ insert: { image: 'url' } } - Line formatting applies to the newline character:
{ insert: '\n', attributes: { header: 1 } } - Use chainable methods:
new Delta().insert('text').retain(5).delete(3)
Change Operations
| Operation | Description |
|---|---|
insert | Insert text or embed |
delete | Delete next N characters |
retain | Keep next N characters (optionally apply attributes) |
Example Change
// Starting from "Gandalf the Grey"
new Delta()
.retain(7, { bold: null, italic: true }) // Unbold and italicize "Gandalf"
.retain(5) // Keep " the "
.insert('White', { color: '#fff' }) // Insert "White"
.delete(4); // Delete "Grey"
Formats
Inline
background— Background colorbold— Boldcolor— Text colorfont— Font familycode— Inline codeitalic— Italiclink— Hyperlinksize— Font sizestrike— Strikethroughscript— Superscript/subscript ('super'/'sub')underline— Underline
Block
blockquote— Blockquoteheader— Header (1–6)indent— Indentationlist— List ('ordered','bullet','check')align— Text alignment ('left','center','right','justify')direction— Text direction ('rtl')code-block— Code block
Embeds
formula— Formula (requires KaTeX)image— Imagevideo— Video
Modules
Enable modules in configuration:
const quill = new Quill('#editor', {
modules: {
toolbar: true,
history: { delay: 2500, userOnly: true },
syntax: true
}
});
Toolbar
// Simple array
const toolbarOptions = ['bold', 'italic', 'underline', 'strike'];
// Grouped
const toolbarOptions = [
['bold', 'italic'],
['link', 'image'],
[{ header: [1, 2, 3, false] }],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ color: [] }, { background: [] }],
['clean']
];
// Custom HTML container
const quill = new Quill('#editor', {
modules: {
toolbar: '#toolbar-id'
}
});
// With custom handlers
const quill = new Quill('#editor', {
modules: {
toolbar: {
container: '#toolbar',
handlers: {
link: function(value) {
if (value) {
const href = prompt('Enter URL');
this.quill.format('link', href);
} else {
this.quill.format('link', false);
}
}
}
}
}
});
History
const quill = new Quill('#editor', {
modules: {
history: {
delay: 1000, // Merge changes within N ms
maxStack: 100, // Max undo/redo stack size
userOnly: false // Only track user changes
}
}
});
quill.history.undo();
quill.history.redo();
quill.history.clear();
quill.history.cutoff(); // Prevent merging with previous change
Clipboard
// Add custom matcher
quill.clipboard.addMatcher('B', (node, delta) => {
return delta.compose(new Delta().retain(delta.length(), { bold: true }));
});
quill.clipboard.addMatcher(Node.TEXT_NODE, (node, delta) => {
return new Delta().insert(node.data);
});
// Paste HTML (dangerous — sanitize first!)
quill.clipboard.dangerouslyPasteHTML(5, ' <b>World</b>');
Configuration:
const quill = new Quill('#editor', {
modules: {
clipboard: {
matchers: [
['B', customMatcherA],
[Node.TEXT_NODE, customMatcherB]
]
}
}
});
Keyboard
const quill = new Quill('#editor', {
modules: {
keyboard: {
bindings: {
tab: {
key: 9,
handler: function() { /* ... */ }
}
}
}
}
});
// Or after initialization
quill.keyboard.addBinding({ key: 'b', shortKey: true }, (range, context) => {
this.quill.formatText(range, 'bold', true);
});
Context options for bindings: collapsed, empty, format, offset, prefix, suffix.
Syntax Highlighting
// CDN approach (highlight.js must be loaded globally)
const quill = new Quill('#editor', {
modules: {
syntax: true,
toolbar: [['code-block']]
}
});
// npm approach
import Quill from 'quill';
import hljs from 'highlight.js';
const quill = new Quill('#editor', {
modules: {
syntax: { hljs }
}
});
Themes
Snow
Clean, flat toolbar theme:
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet">
Bubble
Tooltip-based theme:
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.bubble.css" rel="stylesheet">
Custom / No Theme
For full UI customization, omit theme or set to falsy. Include minimal core CSS:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.core.css" />
Customization
Parchment Basics
Content and formats are represented as Blots (DOM-like nodes) or Attributors (attributes).
Using Class vs Inline Styles
const ColorClass = Quill.import('attributors/class/color');
const SizeStyle = Quill.import('attributors/style/size');
Quill.register(ColorClass, true);
Quill.register(SizeStyle, true);
Customizing Attributors
const FontAttributor = Quill.import('attributors/class/font');
FontAttributor.whitelist = ['sofia', 'slabo', 'roboto', 'inconsolata', 'ubuntu'];
Quill.register(FontAttributor, true);
Customizing Blots
const Bold = Quill.import('formats/bold');
Bold.tagName = 'B'; // Default is <strong>
Quill.register(Bold, true);
Extending Blots
const ListItem = Quill.import('formats/list/item');
class PlainListItem extends ListItem {
formatAt(index, length, name, value) {
if (name === 'list') {
super.formatAt(index, length, name, value);
}
// Otherwise ignore
}
}
Quill.register(PlainListItem, true);
Custom Modules
const Module = Quill.import('core/module');
class CustomModule extends Module {
constructor(quill, options) {
super(quill, options);
// Module initialization
}
}
Quill.register('modules/custom-module', CustomModule);
Registries
Use custom registries when multiple editors with different formats must coexist.
const Parchment = Quill.import('parchment');
// Essential formats
const Block = Quill.import('blots/block');
const Break = Quill.import('blots/break');
const Container = Quill.import('blots/container');
const Cursor = Quill.import('blots/cursor');
const Inline = Quill.import('blots/inline');
const Scroll = Quill.import('blots/scroll');
const Text = Quill.import('blots/text');
const registry = new Parchment.Registry();
registry.register(Scroll, Block, Break, Container, Cursor, Inline, Text);
// Add additional formats
const Bold = Quill.import('formats/bold');
registry.register(Bold);
const quill = new Quill('#editor', {
registry,
theme: 'snow'
});
Extension API
Static Methods
| Method | Signature | Description |
|---|---|---|
Quill.find | Quill.find(domNode, bubble=false): Blot | Quill | Get Quill/Blot instance for DOM node |
Quill.debug | Quill.debug(level: string | boolean) | Enable logging |
Quill.import | Quill.import(path): any | Import Quill library/format/module/theme |
Quill.register | Quill.register(path, def, warn?) | Register module/theme/format |
Instance Methods
| Method | Signature | Description |
|---|---|---|
getIndex | getIndex(blot): number | Document index of blot |
getLeaf | getLeaf(index): [LeafBlot | null, number] | Leaf blot at index |
getLine | getLine(index): [Block | null, number] | Line blot at index |
getLines | getLines(index?, length?): Block[] | Lines in range |
addContainer | addContainer(className, refNode?): Element | Add container element |
getModule | getModule(name): any | Get module instance |
Upgrading to 2.0
Key changes from Quill 1.x:
- TypeScript: Official TypeScript definitions (remove
@types/quill) - SVG icons: Inlined in source (no SVG loader needed)
- Removed:
strictoption,scrollingContaineroption - Added:
registryoption for multiple editor format isolation - Clipboard:
convertAPI changed;matchVisualandpasteHTMLremoved - Keyboard:
keyis case-sensitive; supports array of keys; native event passed to handlers - Parchment: Lists use
<ol>only; code blocks use<div>;Attributor.Class→ClassAttributor; exports use full names (ScrollBlot, notScroll) - Delta: Deprecated format support removed
- Browser: IE support dropped