slab/quill · critical · Error

Invalid theme ${options.theme}. Did you register it?

Error message

Invalid theme ${options.theme}. Did you register it?

What it means

In expandConfig, when options.theme differs from Quill.DEFAULTS.theme (the built-in 'snow'), Quill resolves the theme class via Quill.import(`themes/${options.theme}`). Quill.import returns undefined for an unregistered path and logs a debug error, and expandConfig then throws because the theme class is required to construct the editor. Quill v2 only registers the default theme; every other theme (including the built-in 'bubble') must be explicitly imported/registered.

Source

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

  );
}

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 &&
    userModuleOptions.toolbar &&
    userModuleOptions.toolbar.constructor !== Object
  ) {
    userModuleOptions = {
      ...userModuleOptions,
      toolbar: { container: userModuleOptions.toolbar },
    };
  }

View on GitHub (pinned to 539cbffd0a)

Solutions

  1. Import and register the built-in bubble theme before construction: import BubbleTheme from 'quill/themes/bubble'; Quill.register('themes/bubble', BubbleTheme);.
  2. For a custom theme class, register it: Quill.register('themes/my-theme', MyTheme) (or rely on its side-effect import).
  3. Double-check the theme string spelling matches a registered path exactly (themes/<name>).
  4. If you do not need a custom theme, omit the theme option to fall back to the always-registered default 'snow' theme.

Example fix

// before - bubble theme never registered
new Quill(el, { theme: 'bubble' }); // throws: Invalid theme bubble

// after - register the theme first
import { Quill } from 'quill';
import BubbleTheme from 'quill/themes/bubble';
Quill.register('themes/bubble', BubbleTheme);
new Quill(el, { theme: 'bubble' });
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the theme is registered before constructing Quill
import { Quill } from 'quill';

function assertThemeRegistered(themeName) {
  const isDefault = !themeName || themeName === Quill.DEFAULTS.theme;
  if (isDefault) return; // 'snow' is always registered
  const theme = Quill.import(`themes/${themeName}`);
  if (!theme) {
    throw new Error(
      `Theme "${themeName}" is not registered. Import it and call Quill.register('themes/${themeName}', ThemeClass).`,
    );
  }
}

// usage
assertThemeRegistered(options.theme);
new Quill(el, options);

Type guard

function isThemeRegistered(themeName) {
  if (!themeName || themeName === Quill.DEFAULTS.theme) return true;
  return Quill.import(`themes/${themeName}`) != null;
}

// usage
if (isThemeRegistered('bubble')) {
  new Quill(el, { theme: 'bubble' });
}

Try / catch

try {
  new Quill(el, { theme });
} catch (err) {
  if (String(err?.message).startsWith('Invalid theme')) {
    // theme not registered - fall back to default or register then retry once
    Quill.register(`themes/${theme}`, FallbackThemeClass);
    new Quill(el, { theme });
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: new Quill(el, { theme: 'bubble' }) without importing/registering the bubble theme; referencing a custom theme name that was never passed to Quill.register; a typo in the theme string; tree-shaking removing the theme module's registration side effect.

Common situations: Quill v1->v2 migration where bubble was bundled by default; using ESM tree-shaking builds that drop the theme import; loading only packages/quill/src/core and expecting all themes present; custom theme class authored but not registered.

Related errors


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