How to Handle Nested HTML Elements in html-to-pdfmake: A Complete Guide

The html-to-pdfmake library handles nested HTML elements by recursively traversing the DOM tree in index.js, converting each node into either a text object or a stack array depending on whether mixed content is detected.

The aymkdn/html-to-pdfmake library transforms HTML strings into pdfmake document definitions. When working with complex documents containing nested lists, tables within tables, or mixed inline and block elements, understanding how the library processes hierarchy is essential for generating correct PDF output.

Understanding the Recursive Parsing Engine

The core mechanism for handling nested HTML resides in the parseElement method located at index.js (line 41). This function implements a depth-first traversal of the DOM tree created by DOMParser.

When you call htmlToPdfMake(htmlString), the library first instantiates a DOMParser at line 21 to parse the string into a document object. The root element is then passed to parseElement(element, []), which initiates the recursive walk.

Each invocation of parseElement processes the current node and then iterates over element.childNodes using [].forEach.call at line 89. Child nodes trigger recursive calls to parseElement, building a tree of pdfmake objects that mirrors the original HTML structure.

How Nested Elements Become pdfmake Stacks

The library distinguishes between simple text nodes and complex nested structures using the searchForStack method at index.js (line 237). This determination controls whether a node renders as a text entry or a stack (array of mixed objects).

Plain text nodes become {text: "content"} objects. Mixed content—such as a <p> tag containing both text and an <img>—becomes a stack: {stack: [{text: "..."}, {image: "..."}]}.

The searchForStack function analyzes the processed children to detect heterogeneous content types. When it identifies mixed inline and block elements, it signals the parent to wrap children in a stack array, preserving the document order and structure required by pdfmake.

Practical Examples of Handling Nested HTML

Nested Lists

When converting nested <ul> or <ol> structures, parseElement processes each <li> and recursively handles any child lists found within.

const htmlToPdfMake = require('html-to-pdfmake');

const html = `
  <ul>
    <li>Item 1
      <ul>
        <li>Sub-item A</li>
        <li>Sub-item B</li>
      </ul>
    </li>
    <li>Item 2</li>
  </ul>
`;

const pdfDef = htmlToPdfMake(html);
console.log(JSON.stringify(pdfDef, null, 2));

The output produces a nested structure where the inner <ul> is automatically wrapped in a stack because it appears after regular text within the same <li>:

{
  "ul": [
    {
      "stack": [
        {"text":"Item 1"},
        {
          "ul": [
            {"text":"Sub-item A"},
            {"text":"Sub-item B"}
          ]
        }
      ]
    },
    {"text":"Item 2"}
  ]
}

Mixed Inline and Block Elements

When a paragraph contains both formatted text and block-level elements like images, searchForStack ensures proper array wrapping.

const html = `
  <p>
    This is <strong>bold</strong> and an image:
    <img src="logo.png" width="50" />
  </p>
`;

const pdfDef = htmlToPdfMake(html);

The library detects the mixture of text nodes and the image element, returning a stack that preserves the content order:

{
  "stack": [
    {"text":"This is "},
    {"text":{"bold":true,"text":"bold"}},
    {"text":" and an image:"},
    {"image":"logo.png"}
  ]
}

Deeply Nested Tables

Tables containing other tables are handled through recursive calls where each cell's content is processed independently.

const html = `
  <table>
    <tr><td>
      <table><tr><td>Inner</td></tr></table>
    </td></tr>
  </table>
`;

const pdfDef = htmlToPdfMake(html);

The outer table's cell receives a nested table object ({table: {body: …}}) because the recursive parseElement call returns a complete pdfmake table element, which the parent cell inserts into its body array.

Style Inheritance in Nested Structures

The applyStyle method at index.js (line 555) handles CSS inheritance by merging styles from all ancestor elements. As parseElement traverses upward through the DOM tree, it accumulates parent styles and classes, applying them to child nodes before returning the final object.

This ensures that a <span> inside a <div class="highlight"> receives the highlight styling, and nested <strong> tags within lists maintain their bold formatting while inheriting list-specific margins or colors from parent <ul> elements.

Summary

  • Recursive parsing via parseElement in index.js is the core mechanism for handling nested HTML elements.
  • The library automatically chooses between text and stack representations using searchForStack to accommodate mixed content.
  • Style inheritance is managed by applyStyle, which merges ancestor CSS classes and inline styles into child nodes.
  • Complex structures like nested lists, tables within tables, and mixed inline/block content are preserved accurately through depth-first DOM traversal.

Frequently Asked Questions

How does html-to-pdfmake handle deeply nested lists?

The library processes each <li> element recursively, detecting when a list item contains both text and child lists through the searchForStack method. When mixed content is found, it wraps the elements in a stack array, ensuring the nested structure renders correctly in the final PDF while maintaining proper indentation and hierarchy.

What happens when inline and block elements are mixed in html-to-pdfmake?

When parseElement encounters a parent node containing both text nodes and block-level elements like images, the searchForStack function identifies this heterogeneous content and signals the parent to return a stack array rather than a simple text object. This preserves the sequential order of inline text and block elements in the generated pdfmake definition.

Does html-to-pdfmake support nested tables?

Yes, nested tables are fully supported through recursive processing. When parseElement encounters a <table> inside a <td>, it processes the inner table completely and returns a table object, which the parent cell then inserts into its body array. This allows for complex table layouts with sub-tables inside individual cells.

How are CSS styles inherited in nested HTML elements?

Style inheritance is handled by the applyStyle method at line 555 of index.js, which merges styles and classes from all ancestor elements before returning the final node object. As the recursive parser traverses the DOM tree, it accumulates parent styling information, ensuring that child elements inherit font families, colors, and other CSS properties from their containing elements while allowing specific overrides.

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 →