slab/quill · critical · Error
Cannot initialize Quill without "${scrollBlotName}" blot
Error message
Cannot initialize Quill without "${scrollBlotName}" blot What it means
Quill builds its editor tree on top of Parchment's root "scroll" blot (blotName 'scroll', defined at packages/quill/src/blots/scroll.ts:31). The constructor queries the configured registry for Parchment.ScrollBlot.blotName and throws if the lookup returns nothing or a value without a blotName property. This means the registry in options.registry does not contain Quill's core scroll blot, so the editor cannot be rooted.
Source
Thrown at packages/quill/src/core/quill.ts:218
this.container = this.options.container;
if (this.container == null) {
debug.error('Invalid Quill container', container);
return;
}
if (this.options.debug) {
Quill.debug(this.options.debug);
}
const html = this.container.innerHTML.trim();
this.container.classList.add('ql-container');
this.container.innerHTML = '';
instances.set(this.container, this);
this.root = this.addContainer('ql-editor');
this.root.classList.add('ql-blank');
this.emitter = new Emitter();
const scrollBlotName = Parchment.ScrollBlot.blotName;
const ScrollBlot = this.options.registry.query(scrollBlotName);
if (!ScrollBlot || !('blotName' in ScrollBlot)) {
throw new Error(
`Cannot initialize Quill without "${scrollBlotName}" blot`,
);
}
this.scroll = new ScrollBlot(this.options.registry, this.root, {
emitter: this.emitter,
}) as Scroll;
this.editor = new Editor(this.scroll);
this.selection = new Selection(this.scroll, this.emitter);
this.composition = new Composition(this.scroll, this.emitter);
this.theme = new this.options.theme(this, this.options); // eslint-disable-line new-cap
this.keyboard = this.theme.addModule('keyboard');
this.clipboard = this.theme.addModule('clipboard');
this.history = this.theme.addModule('history');
this.uploader = this.theme.addModule('uploader');
this.theme.addModule('input');
this.theme.addModule('uiNode');
this.theme.init();
this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => {View on GitHub (pinned to 539cbffd0a)
Solutions
- Drop the custom registry and use the formats option instead: createRegistryWithFormats (packages/quill/src/core/utils/createRegistryWithFormats.ts:4) always seeds CORE_FORMATS ['block','break','cursor','inline','scroll','text'], so scroll is guaranteed present.
- If you must keep a custom registry, register the scroll blot on it: const Scroll = Quill.import('blots/scroll'); myRegistry.register(Scroll); before passing it as options.registry.
- Ensure the full Quill build side effects ran (import Quill from 'quill' rather than cherry-picking submodules) so parchment's global registry is populated.
- In SSR, guard construction behind a typeof window check and only instantiate Quill in the browser where registration side effects have executed.
Example fix
// before
const registry = new Registry();
const quill = new Quill(el, { registry }); // throws: scroll blot missing
// after - use formats so core blots are auto-included
const quill = new Quill(el, { formats: ['bold', 'italic', 'list'] });
// or, register scroll on a custom registry
import { Quill } from 'quill';
import { Registry } from 'parchment';
const registry = new Registry();
registry.register(Quill.import('blots/scroll'));
const quill = new Quill(el, { registry }); Defensive patterns
Strategy: validation
Validate before calling
// Run before constructing Quill with a custom registry
import { Quill } from 'quill';
function assertScrollBlotPresent(registry) {
const scrollBlotName = registry.constructor.name && 'scroll'; // 'scroll' per packages/quill/src/blots/scroll.ts:32
const ScrollBlot = registry.query(scrollBlotName);
if (!ScrollBlot || !(typeof ScrollBlot === 'function' && 'blotName' in ScrollBlot)) {
throw new Error(
`options.registry is missing the "${scrollBlotName}" blot; use the formats option or register Quill.import('blots/scroll').`,
);
}
}
// usage
const registry = options.registry ?? Quill.import('core').globalRegistry;
assertScrollBlotPresent(registry);
const quill = new Quill(el, { ...options, registry }); Type guard
// Narrows a registry to one known to contain the scroll blot
function hasScrollBlot(registry) {
const blot = registry.query('scroll');
return blot != null && typeof blot === 'function' && 'blotName' in blot;
}
// usage
if (hasScrollBlot(myRegistry)) {
const quill = new Quill(el, { registry: myRegistry });
} Try / catch
try {
const quill = new Quill(el, options);
} catch (err) {
if (String(err?.message).includes('Cannot initialize Quill without')) {
// registry is missing core blots - fall back to default registry / formats option
console.error('Quill init failed: custom registry lacks the scroll blot.', err);
}
throw err;
} Prevention
- Prefer the formats option over a custom registry; createRegistryWithFormats always seeds CORE_FORMATS including 'scroll'.
- If you build a Registry by hand, register Quill.import('blots/scroll') and the other core blots (block, break, cursor, inline, text) before use.
- Import the full Quill build (from 'quill') rather than deep submodule paths so registration side effects run.
- In SSR, only construct Quill in the browser after registration side effects have executed.
When it happens
Trigger: Passing a hand-built options.registry (new Registry()) that was never seeded with the scroll blot; running in an SSR/isomorphic context where the parchment global registry side-effects never executed; a custom registry whose query('scroll') was overridden/removed; tree-shaking that dropped quill's core blot registration imports.
Common situations: Switching from the formats option to a custom registry to sandbox formats but forgetting core blots; Quill v1->v2 migration where the global registry seeding moved behind an explicit import; bundler misconfiguration stripping side-effectful imports from packages/quill/src/quill.ts.
Related errors
AI-assisted analysis of slab/quill@539cbffd0a (2026-08-12).
Data as JSON: /api/errors/fb3a0d98c68ad510.
Report an issue: GitHub.