0
# Registry System
1
2
Dynamic registration system for formats, modules, themes, and blots enabling extensibility and customization. The registry system allows developers to register custom components, override existing ones, and import registered components at runtime.
3
4
## Capabilities
5
6
### Static Registration Methods
7
8
Methods on the Quill class for registering and importing components.
9
10
```typescript { .api }
11
class Quill {
12
/** Registry of imported components */
13
static imports: Record<string, unknown>;
14
15
/**
16
* Register component with path and target
17
* @param path - Registration path (e.g., 'formats/bold')
18
* @param target - Component to register
19
* @param overwrite - Whether to overwrite existing registration
20
*/
21
static register(path: string, target: any, overwrite?: boolean): void;
22
23
/**
24
* Register multiple components from object
25
* @param targets - Object with path->component mappings
26
* @param overwrite - Whether to overwrite existing registrations
27
*/
28
static register(targets: Record<string, any>, overwrite?: boolean): void;
29
30
/**
31
* Register single component (auto-detect path from blotName/attrName)
32
* @param target - Component with blotName or attrName property
33
* @param overwrite - Whether to overwrite existing registration
34
*/
35
static register(target: RegistryDefinition, overwrite?: boolean): void;
36
37
/**
38
* Import registered component
39
* @param name - Component path or name
40
* @returns Imported component or undefined
41
*/
42
static import(name: string): unknown;
43
44
/**
45
* Import core module
46
* @param name - 'core/module'
47
* @returns Module base class
48
*/
49
static import(name: 'core/module'): typeof Module;
50
51
/**
52
* Import theme
53
* @param name - Theme path (e.g., 'themes/snow')
54
* @returns Theme class
55
*/
56
static import(name: `themes/${string}`): typeof Theme;
57
58
/**
59
* Import Parchment
60
* @param name - 'parchment'
61
* @returns Parchment namespace
62
*/
63
static import(name: 'parchment'): typeof Parchment;
64
65
/**
66
* Import Delta
67
* @param name - 'delta'
68
* @returns Delta class
69
*/
70
static import(name: 'delta'): typeof Delta;
71
72
/**
73
* Find blot instance for DOM node
74
* @param node - DOM node to find blot for
75
* @param bubble - Whether to search up the DOM tree
76
* @returns Blot instance or null
77
*/
78
static find(node: Node, bubble?: boolean): Blot | null;
79
80
/**
81
* Set debug level for logging
82
* @param level - Debug level or boolean
83
*/
84
static debug(level: DebugLevel | boolean): void;
85
}
86
87
interface RegistryDefinition {
88
blotName?: string;
89
attrName?: string;
90
[key: string]: any;
91
}
92
93
type DebugLevel = 'error' | 'warn' | 'log' | 'info';
94
```
95
96
**Usage Examples:**
97
98
```typescript
99
import Quill from 'quill';
100
101
// Register single component with path
102
Quill.register('formats/highlight', HighlightBlot);
103
104
// Register multiple components
105
Quill.register({
106
'formats/mention': MentionBlot,
107
'modules/autoformat': AutoFormatModule,
108
'themes/minimal': MinimalTheme
109
});
110
111
// Register with auto-detection (uses blotName)
112
class CustomBlot extends Inline {
113
static blotName = 'custom';
114
}
115
Quill.register(CustomBlot); // Registers as 'formats/custom'
116
117
// Import registered components
118
const BoldFormat = Quill.import('formats/bold');
119
const ToolbarModule = Quill.import('modules/toolbar');
120
const SnowTheme = Quill.import('themes/snow');
121
122
// Import core components
123
const Module = Quill.import('core/module');
124
const Parchment = Quill.import('parchment');
125
const Delta = Quill.import('delta');
126
```
127
128
### Registration Paths
129
130
Standard paths for registering different types of components.
131
132
```typescript { .api }
133
// Format registration paths
134
'formats/bold' // Bold inline format
135
'formats/italic' // Italic inline format
136
'formats/header' // Header block format
137
'formats/list' // List block format
138
'formats/image' // Image embed format
139
'formats/link' // Link inline format
140
141
// Module registration paths
142
'modules/toolbar' // Toolbar module
143
'modules/keyboard' // Keyboard module
144
'modules/history' // History module
145
'modules/clipboard' // Clipboard module
146
'modules/uploader' // Uploader module
147
148
// Theme registration paths
149
'themes/snow' // Snow theme
150
'themes/bubble' // Bubble theme
151
152
// Blot registration paths
153
'blots/inline' // Inline blot
154
'blots/block' // Block blot
155
'blots/embed' // Embed blot
156
'blots/scroll' // Scroll blot
157
'blots/container' // Container blot
158
159
// Attributor registration paths
160
'attributors/attribute/direction' // Attribute-based direction
161
'attributors/class/color' // Class-based color
162
'attributors/style/font' // Style-based font
163
164
// Core component paths
165
'core/module' // Module base class
166
'core/theme' // Theme base class
167
'parchment' // Parchment system
168
'delta' // Delta class
169
170
// UI component paths
171
'ui/picker' // Picker component
172
'ui/tooltip' // Tooltip component
173
'ui/icons' // Icon definitions
174
```
175
176
**Usage Examples:**
177
178
```typescript
179
// Register formats
180
Quill.register('formats/highlight', HighlightFormat);
181
Quill.register('formats/spoiler', SpoilerFormat);
182
183
// Register modules
184
Quill.register('modules/counter', WordCountModule);
185
Quill.register('modules/autosave', AutoSaveModule);
186
187
// Register themes
188
Quill.register('themes/dark', DarkTheme);
189
Quill.register('themes/minimal', MinimalTheme);
190
191
// Register attributors
192
Quill.register('attributors/style/line-height', LineHeightAttributor);
193
194
// Use registered components
195
const quill = new Quill('#editor', {
196
theme: 'dark',
197
formats: ['bold', 'italic', 'highlight', 'spoiler'],
198
modules: {
199
counter: { container: '#word-count' },
200
autosave: { interval: 5000 }
201
}
202
});
203
```
204
205
### Custom Format Registration
206
207
Register custom text formats and blots.
208
209
```typescript { .api }
210
// Custom inline format
211
class Highlight extends Inline {
212
static blotName = 'highlight';
213
static tagName = 'MARK';
214
static className = 'ql-highlight';
215
216
static create(value) {
217
const node = super.create();
218
if (value) {
219
node.setAttribute('data-color', value);
220
node.style.backgroundColor = value;
221
}
222
return node;
223
}
224
225
static formats(domNode) {
226
return domNode.getAttribute('data-color') || true;
227
}
228
229
format(name, value) {
230
if (name !== this.statics.blotName || !value) {
231
super.format(name, value);
232
} else {
233
this.domNode.setAttribute('data-color', value);
234
this.domNode.style.backgroundColor = value;
235
}
236
}
237
}
238
239
// Custom block format
240
class Alert extends Block {
241
static blotName = 'alert';
242
static tagName = 'DIV';
243
static className = 'alert';
244
245
static create(value) {
246
const node = super.create();
247
node.classList.add(`alert-${value || 'info'}`);
248
return node;
249
}
250
251
static formats(domNode) {
252
const classList = domNode.classList;
253
for (const className of classList) {
254
if (className.startsWith('alert-')) {
255
return className.replace('alert-', '');
256
}
257
}
258
return undefined;
259
}
260
}
261
262
// Custom embed format
263
class Tweet extends BlockEmbed {
264
static blotName = 'tweet';
265
static tagName = 'DIV';
266
static className = 'tweet-embed';
267
268
static create(value) {
269
const node = super.create();
270
node.setAttribute('data-tweet-id', value);
271
node.innerHTML = `<p>Loading tweet ${value}...</p>`;
272
// Load tweet content asynchronously
273
this.loadTweet(node, value);
274
return node;
275
}
276
277
static value(domNode) {
278
return domNode.getAttribute('data-tweet-id');
279
}
280
281
static loadTweet(node, tweetId) {
282
// Implementation for loading tweet content
283
}
284
}
285
```
286
287
**Usage Examples:**
288
289
```typescript
290
// Register custom formats
291
Quill.register('formats/highlight', Highlight);
292
Quill.register('formats/alert', Alert);
293
Quill.register('formats/tweet', Tweet);
294
295
// Use custom formats
296
const quill = new Quill('#editor', {
297
formats: ['bold', 'italic', 'highlight', 'alert', 'tweet']
298
});
299
300
// Apply custom formatting
301
quill.formatText(0, 10, 'highlight', 'yellow');
302
quill.formatLine(20, 1, 'alert', 'warning');
303
quill.insertEmbed(30, 'tweet', '1234567890');
304
```
305
306
### Custom Module Registration
307
308
Register custom modules to extend editor functionality.
309
310
```typescript { .api }
311
// Custom module
312
class AutoSave extends Module {
313
static DEFAULTS = {
314
interval: 10000, // 10 seconds
315
url: '/autosave',
316
method: 'POST'
317
};
318
319
constructor(quill, options) {
320
super(quill, options);
321
this.timer = null;
322
this.lastSaved = '';
323
this.setupAutoSave();
324
}
325
326
setupAutoSave() {
327
this.quill.on('text-change', () => {
328
this.scheduleAutoSave();
329
});
330
}
331
332
scheduleAutoSave() {
333
if (this.timer) {
334
clearTimeout(this.timer);
335
}
336
337
this.timer = setTimeout(() => {
338
this.save();
339
}, this.options.interval);
340
}
341
342
save() {
343
const content = JSON.stringify(this.quill.getContents());
344
if (content !== this.lastSaved) {
345
fetch(this.options.url, {
346
method: this.options.method,
347
headers: { 'Content-Type': 'application/json' },
348
body: content
349
}).then(() => {
350
this.lastSaved = content;
351
console.log('Auto-saved');
352
});
353
}
354
}
355
}
356
357
// Mention module
358
class Mentions extends Module {
359
static DEFAULTS = {
360
source: null,
361
mentionDenotationChars: ['@'],
362
showDenotationChar: true,
363
allowedChars: /^[a-zA-Z0-9_]*$/,
364
minChars: 0,
365
maxChars: 31,
366
offsetTop: 2,
367
offsetLeft: 0
368
};
369
370
constructor(quill, options) {
371
super(quill, options);
372
this.setupMentions();
373
}
374
375
setupMentions() {
376
this.quill.keyboard.addBinding({
377
key: '@'
378
}, this.handleMentionChar.bind(this));
379
}
380
381
handleMentionChar(range, context) {
382
// Implementation for mention handling
383
this.showMentionList(range);
384
}
385
386
showMentionList(range) {
387
// Show mention dropdown
388
}
389
}
390
```
391
392
**Usage Examples:**
393
394
```typescript
395
// Register custom modules
396
Quill.register('modules/autosave', AutoSave);
397
Quill.register('modules/mentions', Mentions);
398
399
// Use custom modules
400
const quill = new Quill('#editor', {
401
modules: {
402
autosave: {
403
interval: 5000,
404
url: '/api/documents/123/autosave'
405
},
406
mentions: {
407
source: (searchTerm, renderList) => {
408
// Fetch mention suggestions
409
fetch(`/api/users/search?q=${searchTerm}`)
410
.then(response => response.json())
411
.then(users => {
412
renderList(users.map(user => ({
413
id: user.id,
414
value: user.username,
415
label: user.displayName
416
})));
417
});
418
}
419
}
420
}
421
});
422
```
423
424
### Custom Theme Registration
425
426
Register custom themes with unique UI and behavior.
427
428
```typescript { .api }
429
// Custom theme
430
class DarkTheme extends SnowTheme {
431
static DEFAULTS = {
432
...SnowTheme.DEFAULTS,
433
modules: {
434
...SnowTheme.DEFAULTS.modules,
435
toolbar: [
436
[{ 'header': [1, 2, 3, false] }],
437
['bold', 'italic', 'underline'],
438
[{ 'color': [] }, { 'background': [] }],
439
['link', 'image'],
440
['clean']
441
]
442
}
443
};
444
445
constructor(quill, options) {
446
super(quill, options);
447
this.applyDarkTheme();
448
}
449
450
applyDarkTheme() {
451
this.quill.container.classList.add('ql-dark');
452
this.addDarkStyles();
453
}
454
455
addDarkStyles() {
456
// Add dark theme CSS
457
const style = document.createElement('style');
458
style.textContent = `
459
.ql-dark .ql-editor {
460
background: #2d2d2d;
461
color: #ffffff;
462
}
463
.ql-dark .ql-toolbar {
464
background: #1e1e1e;
465
border-color: #444;
466
}
467
`;
468
document.head.appendChild(style);
469
}
470
}
471
472
// Minimal theme
473
class MinimalTheme extends Theme {
474
static DEFAULTS = {
475
modules: {}
476
};
477
478
constructor(quill, options) {
479
super(quill, options);
480
this.quill.container.classList.add('ql-minimal');
481
}
482
483
init() {
484
// Minimal initialization
485
this.quill.root.classList.add('ql-minimal-editor');
486
}
487
}
488
```
489
490
**Usage Examples:**
491
492
```typescript
493
// Register custom themes
494
Quill.register('themes/dark', DarkTheme);
495
Quill.register('themes/minimal', MinimalTheme);
496
497
// Use custom themes
498
const darkQuill = new Quill('#dark-editor', {
499
theme: 'dark'
500
});
501
502
const minimalQuill = new Quill('#minimal-editor', {
503
theme: 'minimal'
504
});
505
```
506
507
### Registry Querying and Inspection
508
509
Methods for inspecting and querying the registry.
510
511
```typescript { .api }
512
// Check if component is registered
513
const isRegistered = Quill.import('formats/custom') !== undefined;
514
515
// Get all registered formats
516
const formats = Object.keys(Quill.imports)
517
.filter(key => key.startsWith('formats/'))
518
.map(key => key.replace('formats/', ''));
519
520
// Get all registered modules
521
const modules = Object.keys(Quill.imports)
522
.filter(key => key.startsWith('modules/'))
523
.map(key => key.replace('modules/', ''));
524
525
// Get all registered themes
526
const themes = Object.keys(Quill.imports)
527
.filter(key => key.startsWith('themes/'))
528
.map(key => key.replace('themes/', ''));
529
```
530
531
**Usage Examples:**
532
533
```typescript
534
// List available components
535
console.log('Available formats:',
536
Object.keys(Quill.imports)
537
.filter(key => key.startsWith('formats/'))
538
.map(key => key.replace('formats/', ''))
539
);
540
541
console.log('Available modules:',
542
Object.keys(Quill.imports)
543
.filter(key => key.startsWith('modules/'))
544
.map(key => key.replace('modules/', ''))
545
);
546
547
// Check if specific component exists
548
function hasFormat(formatName) {
549
return Quill.import(`formats/${formatName}`) !== undefined;
550
}
551
552
function hasModule(moduleName) {
553
return Quill.import(`modules/${moduleName}`) !== undefined;
554
}
555
556
// Dynamic component loading
557
function loadComponent(type, name) {
558
const path = `${type}/${name}`;
559
const component = Quill.import(path);
560
561
if (!component) {
562
throw new Error(`Component ${path} not found`);
563
}
564
565
return component;
566
}
567
568
// Usage
569
const BoldFormat = loadComponent('formats', 'bold');
570
const ToolbarModule = loadComponent('modules', 'toolbar');
571
```
572
573
### Overriding Existing Components
574
575
Replace built-in components with custom implementations.
576
577
```typescript { .api }
578
// Override built-in bold format
579
class CustomBold extends Inline {
580
static blotName = 'bold';
581
static tagName = 'STRONG';
582
583
static create() {
584
const node = super.create();
585
node.setAttribute('data-custom', 'true');
586
return node;
587
}
588
}
589
590
// Override with overwrite flag
591
Quill.register('formats/bold', CustomBold, true);
592
593
// Override toolbar module
594
class CustomToolbar extends Toolbar {
595
constructor(quill, options) {
596
super(quill, options);
597
this.addCustomFeatures();
598
}
599
600
addCustomFeatures() {
601
// Add custom toolbar features
602
}
603
}
604
605
Quill.register('modules/toolbar', CustomToolbar, true);
606
```
607
608
**Usage Examples:**
609
610
```typescript
611
// Custom implementation of existing components
612
class EnhancedHistory extends History {
613
constructor(quill, options) {
614
super(quill, options);
615
this.addHistoryPersistence();
616
}
617
618
addHistoryPersistence() {
619
// Save history to localStorage
620
this.quill.on('text-change', () => {
621
localStorage.setItem('quill-history', JSON.stringify(this.stack));
622
});
623
624
// Restore history on load
625
const saved = localStorage.getItem('quill-history');
626
if (saved) {
627
this.stack = JSON.parse(saved);
628
}
629
}
630
}
631
632
// Override existing history module
633
Quill.register('modules/history', EnhancedHistory, true);
634
635
// Now all editors use enhanced history
636
const quill = new Quill('#editor', {
637
modules: {
638
history: true // Uses EnhancedHistory
639
}
640
});
641
```