How to Handle Custom HTML Tags in html-to-pdfmake: A Complete Guide

Use the customTag callback option to intercept unknown HTML elements and transform them into valid PDFMake nodes before they reach the parser's default handler.

The html-to-pdfmake library converts standard HTML into PDFMake document definitions, but it cannot natively recognize every custom element or specialized tag you might use. When the parser encounters an unsupported tag, it triggers the customTag callback defined in your options object, allowing you to inject custom logic at the default handler in index.js lines 588–590.

How the customTag Callback Works

When htmlToPdfMake processes an HTML element that does not match any built-in case in the parseElement switch statement, execution falls through to the default branch. According to the source code at [index.js lines 588–590](https://github.com/aymkdn/html-to-pdfmake/blob/master/index.js#L588-L590), the library checks for options.customTag and invokes it using .call(this), binding the internal parser instance to your function.

Callback Parameters

The customTag function receives three parameters:

Parameter Type Description
element DOM Node The native HTML element being processed
ret Object The current PDFMake fragment (may be empty)
parents Array Ancestor nodes for style inheritance

Your callback may modify ret to add text, styles, or custom PDFMake keys, and must return the updated object. Because the function is called with call(this), you have access to internal helper methods such as applyStyle and parseStyle.

Practical Implementation Examples

Basic Custom Tag Handler

The following example demonstrates handling a simple <custom-tag> element by replacing it with a paragraph containing static text:

const html = `<custom-tag></custom-tag>`;

const result = htmlToPdfmake(html, {
  customTag({ element, ret }) {
    if (element.nodeName === 'CUSTOM-TAG') {
      // Replace the unknown tag with a paragraph containing custom text
      ret = { text: 'This is content inserted by a custom tag.' };
    }
    return ret;
  }
});

console.log(result);
// → { text: 'This is content inserted by a custom tag.' }

QR Code Generator Implementation

This advanced example from the official documentation shows how to transform a <code typecode="QR"> element into a PDFMake QR code node:

const html = `<code typecode="QR" style="foreground:black;background:yellow;fit:300px">
                texto in code
              </code>`;

const pdfDef = htmlToPdfmake(html, {
  customTag(params) {
    const { element, ret, parents } = params;

    // Only act on our special <code> element
    if (ret.nodeName === 'CODE') {
      // Apply inherited styles first
      ret = this.applyStyle({ ret, parents: parents.concat([element]) });

      // Extract the inner text that will become the QR payload
      ret.qr = ret.text[0].text; // ← QR content
      delete ret.text;           // QR node does not need regular text

      // Mark the node so PDFMake knows it is a QR
      ret.nodeName = 'QR';
      ret.style = (ret.style || []).concat('html-qr');
    }
    return ret;
  }
});

The resulting definition contains {nodeName:'QR', qr:'texto in code', style:['html-qr']}, which PDFMake renders as a QR code when using a compatible QR extension.

Wrapping Third-Party Widgets

Handle proprietary widget tags by extracting attributes and converting them to styled PDFMake elements:

const html = `<my-widget data-value="42"></my-widget>`;

const pdfDef = htmlToPdfmake(html, {
  customTag({ element, ret, parents }) {
    if (element.nodeName === 'MY-WIDGET') {
      // Use the data-value attribute to create a table cell with custom background
      ret = {
        background: '#f0f0f0',
        text: `Widget value: ${element.getAttribute('data-value')}`
      };
      // Re‑apply styles from ancestors (margin, alignment, etc.)
      ret = this.applyStyle({ ret, parents });
    }
    return ret;
  }
});

Accessing Internal Parser Methods

Because customTag is invoked using options.customTag.call(this, ...), the callback executes within the context of the parser instance. This binding gives you access to internal helper methods such as:

  • this.applyStyle({ ret, parents }) – Applies inherited CSS classes and inline styles to your custom node
  • this.parseStyle(styleString) – Parses CSS style strings into PDFMake-compatible style objects
  • this.computeStyle(style) – Computes final styles after processing

Leveraging these methods ensures your custom tags respect the same styling cascade as standard HTML elements.

Key Source Files Reference

File Purpose
[index.js](https://github.com/aymkdn/html-to-pdfmake/blob/master/index.js) Core parser; customTag hook resides in the default branch of parseElement (lines 588–590)
[README.md](https://github.com/aymkdn/html-to-pdfmake/blob/master/README.md) Official documentation of the customTag option (lines 38–50)
[example.js](https://github.com/aymkdn/html-to-pdfmake/blob/master/example.js) Usage examples that can be extended with customTag callbacks
[browser.js](https://github.com/aymkdn/html-to-pdfmake/blob/master/browser.js) Browser bundle supporting identical customTag functionality

Summary

  • The customTag callback in html-to-pdfmake intercepts unknown HTML elements during the parsing phase at index.js lines 588–590.
  • It receives the DOM element, current PDFMake fragment (ret), and parent nodes for style inheritance.
  • The callback runs with parser context (this.applyStyle, etc.), allowing reuse of internal styling logic.
  • Return a modified PDFMake node to replace the default handling of custom tags.
  • Works identically in Node.js (via jsdom) and browser (via DOMParser) environments.

Frequently Asked Questions

What happens if I don't define a customTag handler?

When the parser encounters an unrecognized tag without a customTag callback defined, it processes the element's children but returns an empty object for the tag itself. This means custom tags effectively disappear from the final PDF output unless you explicitly handle them.

Can I override standard HTML tags using customTag?

Yes. While customTag primarily handles unknown tags reaching the default branch, you can intercept standard tags by inspecting the ret.nodeName or element.nodeName inside your callback. However, for modifying behavior of built-in tags, preprocessing the HTML or using the styles option is often cleaner.

How do I access element attributes in my custom handler?

The element parameter passed to your callback is a native DOM node, so you can use standard DOM methods like element.getAttribute('data-value'), element.hasAttribute('typecode'), or element.attributes to inspect HTML attributes and drive conditional logic in your PDF generation.

Is the customTag callback supported when using the browser bundle?

Yes. The customTag functionality works identically in both Node.js and browser environments because the core parsing logic in index.js is environment-agnostic. Whether you load the library via require() in Node or include browser.js in a <script> tag, the customTag option is available in the options object passed to htmlToPdfmake.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →