{"record":{"id":"4ea6514045e9af31","repo":"slab/quill","slug":"syntax-module-requires-highlight-js-please-includ","errorCode":null,"errorMessage":"Syntax module requires highlight.js. Please include the library on the page before Quill.","messagePattern":"Syntax module requires highlight\\.js\\. Please include the library on the page before Quill\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/quill/src/modules/syntax.ts","lineNumber":218,"sourceCode":"  }\n  return lib.highlight(language, text).value;\n};\n\nclass Syntax extends Module<SyntaxOptions> {\n  static DEFAULTS: SyntaxOptions & { hljs: any };\n\n  static register() {\n    Quill.register(CodeToken, true);\n    Quill.register(SyntaxCodeBlock, true);\n    Quill.register(SyntaxCodeBlockContainer, true);\n  }\n\n  languages: Record<string, true>;\n\n  constructor(quill: Quill, options: Partial<SyntaxOptions>) {\n    super(quill, options);\n    if (this.options.hljs == null) {\n      throw new Error(\n        'Syntax module requires highlight.js. Please include the library on the page before Quill.',\n      );\n    }\n    // @ts-expect-error Fix me later\n    this.languages = this.options.languages.reduce(\n      (memo: Record<string, unknown>, { key }) => {\n        memo[key] = true;\n        return memo;\n      },\n      {},\n    );\n    this.highlightBlot = this.highlightBlot.bind(this);\n    this.initListener();\n    this.initTimer();\n  }\n\n  initListener() {\n    this.quill.on(Quill.events.SCROLL_BLOT_MOUNT, (blot: Blot) => {","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/slab/quill/blob/539cbffd0a13b18e9c65eb84dd35e6596e403158/packages/quill/src/modules/syntax.ts#L200-L236","documentation":"The Syntax module tokenizes code blocks with highlight.js. Its DEFAULTS.hljs is captured as window.hljs at module-evaluation time (packages/quill/src/modules/syntax.ts:336-338), and the constructor throws if this.options.hljs is still null when the module is constructed. Unlike formula, you can inject the library via the module options instead of relying on the global.","triggerScenarios":"new Quill(el, { modules: { syntax: true } }) when window.hljs was undefined at the time Syntax.DEFAULTS was evaluated; highlight.js loaded asynchronously after Quill initializes; bundler/SSR environment with no window.hljs and no explicit hljs option passed; hljs option omitted from module config.","commonSituations":"Forgot the highlight.js script tag; highlight.js imported as a module but never assigned to window; tree-shaking dropping the side effect; Quill initialized before highlight.js finished loading; SSR rendering where window is absent.","solutions":["Pass the highlight.js instance explicitly via module options so the global capture is bypassed: modules: { syntax: { hljs } }.","Import highlight.js and expose it globally before Quill initializes: import hljs from 'highlight.js'; (window as any).hljs = hljs;.","Include the highlight.js <script> synchronously before the Quill init script.","If code highlighting is optional, gate the syntax module behind a feature flag and only enable it when hljs is available."],"exampleFix":"// before - relies on window.hljs captured at module-eval time\nnew Quill(el, { modules: { syntax: true } }); // throws if hljs missing\n\n// after - inject hljs explicitly via options (preferred)\nimport hljs from 'highlight.js';\nnew Quill(el, {\n  modules: { syntax: { hljs } },\n});\n\n// or expose globally before construction\nimport hljs from 'highlight.js';\n(window as any).hljs = hljs;\nnew Quill(el, { modules: { syntax: true } });","handlingStrategy":"validation","validationCode":"// Ensure highlight.js is available before enabling the syntax module\nfunction resolveHljs(explicitHljs) {\n  const hljs = explicitHljs ?? (typeof window !== 'undefined' ? window.hljs : undefined);\n  if (hljs == null) {\n    throw new Error(\n      'Syntax module requires highlight.js. Pass it via modules.syntax.hljs or load window.hljs before Quill.',\n    );\n  }\n  return hljs;\n}\n\n// usage\nconst hljs = resolveHljs(myHljsInstance);\nnew Quill(el, { modules: { syntax: { hljs } } });","typeGuard":"function hasHljs(explicitHljs) {\n  const hljs = explicitHljs ?? (typeof window !== 'undefined' ? window.hljs : undefined);\n  return hljs != null && typeof hljs.highlight === 'function';\n}\n\n// usage\nif (hasHljs()) {\n  new Quill(el, { modules: { syntax: true } });\n} else {\n  // skip syntax highlighting, or import highlight.js then retry\n  new Quill(el, {});\n}","tryCatchPattern":"try {\n  new Quill(el, { modules: { syntax: { hljs } } });\n} catch (err) {\n  if (String(err?.message).includes('highlight.js')) {\n    // hljs missing - initialize Quill without the syntax module\n    console.warn('Syntax module disabled: highlight.js not available.');\n    new Quill(el, { modules: { syntax: false } });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Pass the highlight.js instance explicitly via modules.syntax.hljs rather than relying on the global captured at module-eval time.","In bundler setups, import highlight.js and assign window.hljs at app entry before Quill initializes.","Load the highlight.js script tag synchronously before the Quill init script.","Gate the syntax module behind a hasHljs() check so it is only enabled when the dependency is present."],"tags":["highlightjs","syntax","external-dependency","modules","code-block"],"backgroundTag":null,"analyzedSha":"539cbffd0a13b18e9c65eb84dd35e6596e403158","analyzedAt":"2026-08-12T17:15:45.919Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}