A modern, powerful rich text editor built for compatibility and extensibility
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Delta-based document representation and operational transform system for tracking and applying changes to rich text content. Delta is Quill's native format for describing document content and changes, providing a JSON-based structure that supports operational transformation for real-time collaboration.
Creates a new Delta instance for representing document content or changes.
/**
* Creates a new Delta instance
* @param ops - Array of operations or another Delta to copy
*/
constructor(ops?: Op[] | Delta);
interface Op {
/** Insert text or embed object */
insert?: string | object;
/** Delete specified number of characters */
delete?: number;
/** Retain specified number of characters, optionally with attributes */
retain?: number;
/** Formatting attributes to apply */
attributes?: AttributeMap;
}
interface AttributeMap {
[key: string]: unknown;
}Usage Examples:
import { Delta } from 'quill';
// Create empty Delta
const delta = new Delta();
// Create Delta from operations
const delta = new Delta([
{ insert: 'Hello ' },
{ insert: 'World', attributes: { bold: true } },
{ insert: '\n' }
]);
// Copy existing Delta
const copy = new Delta(existingDelta);Methods for inserting text or embed content with optional formatting.
/**
* Insert text at current position
* @param text - Text to insert
* @param attributes - Optional formatting attributes
* @returns This Delta instance for chaining
*/
insert(text: string, attributes?: AttributeMap): Delta;
/**
* Insert embed object at current position
* @param embed - Embed object (image, video, etc.)
* @param attributes - Optional formatting attributes
* @returns This Delta instance for chaining
*/
insert(embed: object, attributes?: AttributeMap): Delta;Usage Examples:
// Insert plain text
const delta = new Delta()
.insert('Hello World');
// Insert formatted text
const delta = new Delta()
.insert('Bold Text', { bold: true })
.insert('Red Text', { color: '#ff0000' });
// Insert embeds
const delta = new Delta()
.insert({ image: 'https://example.com/image.jpg' })
.insert({ video: 'https://youtube.com/watch?v=abc123' });
// Chain multiple inserts
const delta = new Delta()
.insert('Normal text ')
.insert('bold text ', { bold: true })
.insert('italic text', { italic: true })
.insert('\n');Methods for deleting characters from the document.
/**
* Delete specified number of characters
* @param length - Number of characters to delete
* @returns This Delta instance for chaining
*/
delete(length: number): Delta;Usage Examples:
// Delete 5 characters
const delta = new Delta()
.delete(5);
// Delete and insert (replacement)
const delta = new Delta()
.retain(10) // Skip first 10 characters
.delete(5) // Delete next 5 characters
.insert('new text'); // Insert replacement textMethods for retaining (keeping) characters, optionally applying formatting changes.
/**
* Retain specified number of characters
* @param length - Number of characters to retain
* @param attributes - Optional attributes to apply/change
* @returns This Delta instance for chaining
*/
retain(length: number, attributes?: AttributeMap): Delta;Usage Examples:
// Retain characters without changes
const delta = new Delta()
.retain(10);
// Retain and apply formatting
const delta = new Delta()
.retain(5, { bold: true }); // Make first 5 characters bold
// Complex formatting change
const delta = new Delta()
.retain(10) // Skip first 10 characters
.retain(5, { italic: true }) // Make next 5 characters italic
.retain(3, { bold: null }); // Remove bold from next 3 charactersMethods for combining Deltas using operational transform principles.
/**
* Compose this Delta with another Delta
* @param other - Delta to compose with
* @returns New Delta representing the composition
*/
compose(other: Delta): Delta;
/**
* Transform this Delta against another Delta
* @param other - Delta to transform against
* @param priority - Whether this Delta has priority in conflicts
* @returns New transformed Delta
*/
transform(other: Delta, priority?: boolean): Delta;
/**
* Transform a cursor position against this Delta
* @param index - Cursor position to transform
* @param priority - Whether cursor has priority
* @returns New cursor position
*/
transformPosition(index: number, priority?: boolean): number;
/**
* Calculate difference between this Delta and another
* @param other - Delta to compare against
* @param cursor - Optional cursor position for optimization
* @returns Delta representing the difference
*/
diff(other: Delta, cursor?: number): Delta;Usage Examples:
// Compose two changes
const delta1 = new Delta().insert('Hello');
const delta2 = new Delta().retain(5).insert(' World');
const composed = delta1.compose(delta2);
// Result: Delta with "Hello World"
// Transform concurrent changes
const delta1 = new Delta().insert('A');
const delta2 = new Delta().insert('B');
const transformed = delta1.transform(delta2, true);
// Result: Delta accounting for concurrent insertion
// Transform cursor position
const delta = new Delta().insert('Hello ');
const newPosition = delta.transformPosition(0); // Position after "Hello "
// Calculate difference between documents
const oldDoc = new Delta().insert('Hello World');
const newDoc = new Delta().insert('Hello Beautiful World');
const diff = oldDoc.diff(newDoc);
// Result: Delta showing what changedMethods for analyzing and manipulating Delta content.
/**
* Get total length of Delta content
* @returns Total number of characters
*/
length(): number;
/**
* Extract slice of Delta
* @param start - Starting index (default: 0)
* @param end - Ending index (default: end of Delta)
* @returns New Delta with sliced content
*/
slice(start?: number, end?: number): Delta;
/**
* Partition Delta based on predicate
* @param predicate - Function to test each operation
* @returns Tuple of [matching ops, non-matching ops] as Deltas
*/
partition(predicate: (op: Op) => boolean): [Delta, Delta];
/**
* Filter operations based on predicate
* @param predicate - Function to test each operation
* @returns Array of operations that match predicate
*/
filter(predicate: (op: Op, index: number) => boolean): Op[];
/**
* Execute function for each operation
* @param predicate - Function to execute for each operation
*/
forEach(predicate: (op: Op, index: number) => void): void;
/**
* Map operations to new values
* @param predicate - Function to transform each operation
* @returns Array of transformed values
*/
map<T>(predicate: (op: Op, index: number) => T): T[];
/**
* Reduce operations to single value
* @param predicate - Function to combine operations
* @param initialValue - Initial value for reduction
* @returns Final reduced value
*/
reduce<T>(predicate: (acc: T, curr: Op, index: number) => T, initialValue: T): T;Usage Examples:
const delta = new Delta()
.insert('Hello ')
.insert('World', { bold: true })
.insert('\n');
// Get length
const length = delta.length(); // 12 characters
// Slice content
const slice = delta.slice(0, 5); // Delta with "Hello"
const end = delta.slice(6); // Delta with "World\n" (bold)
// Partition by attributes
const [formatted, plain] = delta.partition(op => op.attributes && Object.keys(op.attributes).length > 0);
// Filter insert operations
const inserts = delta.filter(op => op.insert !== undefined);
// Process each operation
delta.forEach((op, index) => {
console.log(`Operation ${index}:`, op);
});
// Extract text content
const text = delta
.map(op => typeof op.insert === 'string' ? op.insert : '')
.join('');
// Count characters
const charCount = delta.reduce((count, op) => {
return count + (typeof op.insert === 'string' ? op.insert.length : 1);
}, 0);Iterator class for processing Delta operations sequentially.
/**
* Iterator for processing Delta operations
* @param ops - Array of operations to iterate over
*/
class OpIterator {
constructor(ops: Op[]);
/**
* Check if there are more operations
* @returns True if more operations available
*/
hasNext(): boolean;
/**
* Get next operation, optionally consuming specific length
* @param length - Length to consume from current operation
* @returns Next operation or portion of operation
*/
next(length?: number): Op;
/**
* Peek at next operation without consuming it
* @returns Next operation
*/
peek(): Op;
/**
* Get length of next operation
* @returns Length of next operation
*/
peekLength(): number;
/**
* Get type of next operation
* @returns Type of next operation
*/
peekType(): 'insert' | 'delete' | 'retain';
/**
* Get remaining operations
* @returns Array of remaining operations
*/
rest(): Op[];
}Usage Examples:
import { OpIterator } from 'quill';
const delta = new Delta()
.insert('Hello')
.retain(5)
.delete(3);
const iter = new OpIterator(delta.ops);
while (iter.hasNext()) {
const op = iter.next();
console.log('Operation:', op);
// Process based on type
if (op.insert) {
console.log('Insert:', op.insert);
} else if (op.delete) {
console.log('Delete:', op.delete);
} else if (op.retain) {
console.log('Retain:', op.retain);
}
}
// Peek without consuming
const iter2 = new OpIterator(delta.ops);
console.log('Next type:', iter2.peekType());
console.log('Next length:', iter2.peekLength());
console.log('Next op:', iter2.peek());
// Get remaining operations
const remaining = iter2.rest();Delta objects are JSON-serializable for storage and transmission.
// Delta structure for JSON serialization
interface DeltaJSON {
ops: Op[];
}
interface Op {
insert?: string | object;
delete?: number;
retain?: number;
attributes?: AttributeMap;
}Usage Examples:
// Create Delta
const delta = new Delta()
.insert('Hello ')
.insert('World', { bold: true })
.insert('\n');
// Serialize to JSON
const json = JSON.stringify(delta);
console.log('JSON:', json);
// Output: {"ops":[{"insert":"Hello "},{"insert":"World","attributes":{"bold":true}},{"insert":"\n"}]}
// Deserialize from JSON
const parsed = JSON.parse(json);
const restored = new Delta(parsed.ops);
// Delta can be used directly as JSON
const delta2 = new Delta(parsed);Frequently used Delta operation patterns for content manipulation.
Usage Examples:
// Document creation
const document = new Delta()
.insert('Heading', { header: 1 })
.insert('\n')
.insert('This is a paragraph with ')
.insert('bold text', { bold: true })
.insert(' and ')
.insert('italic text', { italic: true })
.insert('.\n');
// Text replacement
const replacement = new Delta()
.retain(startIndex)
.delete(lengthToDelete)
.insert(newText, formatting);
// Format application
const formatting = new Delta()
.retain(startIndex)
.retain(length, { bold: true, color: '#ff0000' });
// Format removal
const formatRemoval = new Delta()
.retain(startIndex)
.retain(length, { bold: null, italic: null });
// List creation
const list = new Delta()
.insert('Item 1', { list: 'bullet' })
.insert('\n')
.insert('Item 2', { list: 'bullet' })
.insert('\n');
// Link insertion
const link = new Delta()
.retain(textStart)
.retain(textLength, { link: 'https://example.com' });
// Image insertion
const image = new Delta()
.retain(insertPosition)
.insert({ image: 'https://example.com/image.jpg' });Install with Tessl CLI
npx tessl i tessl/npm-quill