slab/quill · critical · Error

Cannot initialize Quill without

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.

Solutions

  1. 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.
  2. 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.
  3. Ensure the full Quill build side effects ran (import Quill from 'quill' rather than cherry-picking submodules) so parchment's global registry is populated.
  4. 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

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.

Appendix: 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)