slab/quill · critical · Error

Invalid Quill container

Error message

Invalid Quill container

What it means

expandConfig (packages/quill/src/core/quill.ts:793) calls resolveSelector on the container argument; resolveSelector runs document.querySelector for a string and returns the element directly otherwise. If the result is falsy it throws 'Invalid Quill container'. Quill needs a real, mounted DOM node to attach to before any editor wiring can run.

Source

Thrown at packages/quill/src/core/quill.ts:799

      [key]: value === true ? {} : value,
    }),
    {} as Record<string, unknown>,
  );
}

function omitUndefinedValuesFromOptions(obj: QuillOptions) {
  return Object.fromEntries(
    Object.entries(obj).filter((entry) => entry[1] !== undefined),
  );
}

function expandConfig(
  containerOrSelector: HTMLElement | string,
  options: QuillOptions,
): ExpandedQuillOptions {
  const container = resolveSelector(containerOrSelector);
  if (!container) {
    throw new Error('Invalid Quill container');
  }

  const shouldUseDefaultTheme =
    !options.theme || options.theme === Quill.DEFAULTS.theme;
  const theme = shouldUseDefaultTheme
    ? Theme
    : Quill.import(`themes/${options.theme}`);
  if (!theme) {
    throw new Error(`Invalid theme ${options.theme}. Did you register it?`);
  }

  const { modules: quillModuleDefaults, ...quillDefaults } = Quill.DEFAULTS;
  const { modules: themeModuleDefaults, ...themeDefaults } = theme.DEFAULTS;

  let userModuleOptions = expandModuleConfig(options.modules);
  // Special case toolbar shorthand
  if (
    userModuleOptions != null &&

View on GitHub (pinned to 539cbffd0a)

Solutions

  1. Defer initialization until the element exists: run new Quill inside DOMContentLoaded, or in the framework's onMounted/afterViewInit/componentDidMount lifecycle hook.
  2. Pass the element reference directly instead of a selector string: new Quill(document.getElementById('editor')) to remove querySelector ambiguity.
  3. Verify the selector resolves before constructing: if (!document.querySelector(sel)) return;.
  4. In an SPA, ensure the container is rendered (v-if resolved) before the Quill initialization effect runs.

Example fix

// before - runs before #editor is in the DOM
new Quill('#editor'); // throws: Invalid Quill container

// after - resolve and guard, then pass the element
const el = document.querySelector('#editor');
if (!el) throw new Error('#editor not found');
new Quill(el, { theme: 'snow' });

// or, in Vue/React, inside onMounted / useEffect where the ref is set
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the container yourself before constructing Quill
function resolveQuillContainer(target) {
  const el =
    typeof target === 'string'
      ? document.querySelector(target)
      : target;
  if (!el || !(el instanceof HTMLElement)) {
    throw new Error(
      `Invalid Quill container: ${typeof target === 'string' ? target : 'provided element'} is not a mounted HTMLElement`,
    );
  }
  return el;
}

// usage
const el = resolveQuillContainer('#editor');
new Quill(el, { theme: 'snow' });

Type guard

function isMountedHTMLElement(value) {
  return value instanceof HTMLElement && document.body.contains(value);
}

// usage
const el = document.querySelector('#editor');
if (isMountedHTMLElement(el)) {
  new Quill(el);
}

Prevention

When it happens

Trigger: Passing a CSS selector string that matches no element; calling new Quill('#editor') before the element is in the DOM (script in head without defer, or running before DOMContentLoaded); passing a detached/removed HTMLElement; running where document is absent (SSR) so querySelector returns null.

Common situations: Script tag in <head> executing before the body is parsed; SPA frameworks instantiating Quill in created() instead of mounted()/onMounted(); selector typo or renamed element id; hydration races where the container is conditionally rendered.

Related errors


AI-assisted analysis of slab/quill@539cbffd0a (2026-08-12). Data as JSON: /api/errors/66a987de24408a08. Report an issue: GitHub.