How to Format ECharts Tooltips with Custom HTML Templates

ECharts formats tooltips through a formatter option that accepts string templates with placeholders ({a}, {b}, {c}), synchronous functions returning HTML strings, or asynchronous functions using a callback parameter, with content rendered via createTooltipMarkup in src/component/tooltip/tooltipMarkup.ts and automatically HTML-encoded via encodeHTML to prevent XSS.

ECharts provides flexible tooltip customization through its formatter option, allowing developers to inject custom HTML templates into chart tooltips. According to the apache/echarts source code, the tooltip markup engine processes these formatters through a secure pipeline that converts templates into either DOM elements or canvas-rich text. Understanding how buildTooltipMarkup and TooltipMarkupStyleCreator interact with your custom HTML ensures you can create rich, interactive tooltips without compromising security.

The Formatter API

The tooltip.formatter option supports three distinct patterns for generating content:

  • String template: Static HTML containing placeholders like {a} (series name), {b} (data name), {c} (value), and {d} (percent).
  • Function: JavaScript function receiving data parameters and returning an HTML string.
  • Asynchronous function: Function receiving a callback parameter to resolve content after async operations.

All formatter types ultimately pass through createTooltipMarkup in src/component/tooltip/tooltipMarkup.ts, which builds a markup tree before rendering.

String Template Formatters

String templates provide the simplest approach for basic HTML formatting. The template engine replaces placeholders with actual data values before passing the result through the markup builder.

option = {
    tooltip: {
        trigger: 'axis',
        formatter: `
            <div style="font-weight:bold;">{a}</div>
            <div>{b} : {c} ({d}%)</div>
        `
    },
    xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
    yAxis: { type: 'value' },
    series: [{ 
        type: 'bar', 
        name: 'Sales', 
        data: [120, 200, 150] 
    }]
};

In src/component/tooltip/tooltipMarkup.ts (lines 72–78), the string is processed by buildSection and automatically HTML-encoded via encodeHTML to prevent injection attacks while preserving your template structure.

Function-Based Formatters

For dynamic content requiring calculations or conditional logic, use a function formatter defined in src/component/tooltip/TooltipModel.ts (lines 35–38).

Synchronous Functions

The function receives a params object (or array for axis triggers) containing seriesName, name, value, and other metadata:

option = {
    tooltip: {
        trigger: 'item',
        formatter: function (params) {
            return `
                <div style="color:#fff;background:#333;padding:5px;">
                    <strong>${params.seriesName}</strong><br/>
                    ${params.name}: <em>${params.value}</em>
                </div>`;
        }
    },
    series: [{ 
        type: 'pie', 
        data: [
            {value: 335, name: 'A'}, 
            {value: 123, name: 'B'}
        ] 
    }]
};

Asynchronous Functions with Callback

For data fetched from APIs, the formatter accepts a third callback parameter. The implementation in test/tooltip.html (lines 998–1013) demonstrates this pattern:

option = {
    tooltip: {
        trigger: 'axis',
        formatter: function (params, ticket, callback) {
            setTimeout(function () {
                const html = `
                    <div>Fetched at ${new Date().toLocaleTimeString()}</div>
                    <div>Sum: ${params.reduce((s, p) => s + p.value, 0)}</div>`;
                callback(ticket, html);
            }, 800);
            return 'loading…';
        }
    },
    series: [{ type: 'line', data: [10, 20, 30] }]
};

The callback(ticket, html) resolves the tooltip content while the initial return value displays temporary loading text.

Render Modes and Security

ECharts supports two rendering paths that affect how your HTML template becomes visual output.

HTML Mode and XSS Protection

When renderMode is 'html' (default), the tooltip renders as a DOM element. The encodeHTML function in src/component/tooltip/tooltipMarkup.ts (lines 72–78) escapes all user-provided placeholder values before insertion, preventing XSS attacks while allowing your literal HTML markup to function normally.

RichText Mode for Canvas/SVG

For canvas-based charts without DOM overlay, set renderMode: 'richText'. In this mode, TooltipMarkupStyleCreator (lines 150–180 in tooltipMarkup.ts) converts your formatter output into styled text fragments rather than HTML elements:

option = {
    tooltip: {
        renderMode: 'richText',
        formatter: '{a}<br/>{b}: {c}'
    },
    series: [{ type: 'scatter', data: [[10, 20], [15, 25]] }]
};

Both modes share the same formatter syntax, but buildTooltipMarkup generates different internal representations depending on the render target.

Key Implementation Files

Understanding the ECharts tooltip architecture requires familiarity with these specific files:

Summary

  • String templates use placeholders ({a}, {b}, {c}) for simple HTML formatting without JavaScript logic.
  • Function formatters provide full control via params objects and return literal HTML strings.
  • Async formatters utilize the callback(ticket, html) pattern demonstrated in test/tooltip.html for deferred content loading.
  • HTML mode automatically encodes placeholder values via encodeHTML in tooltipMarkup.ts to prevent XSS.
  • RichText mode converts output through TooltipMarkupStyleCreator for canvas/SVG rendering without DOM elements.

Frequently Asked Questions

How do I access the series color in a custom tooltip formatter?

The params object passed to function formatters includes a color property containing the item's assigned color. Access it via params.color to style markers or borders in your HTML template.

Can I use JavaScript event listeners in tooltip HTML?

Yes, but only when renderMode is set to 'html'. The resulting DOM element supports standard HTML including button and anchor tags with event handlers. Avoid inline event attributes in favor of delegated events due to the tooltip's dynamic DOM insertion.

Why is my HTML showing as plain text instead of rendering?

Verify that renderMode is not set to 'richText', which converts HTML tags to literal text rather than DOM elements. Also ensure your tooltip.formatter returns a string, not a DOM node, as ECharts handles the insertion internally.

What is the performance impact of async tooltip formatters?

Async formatters trigger DOM updates via callback, which causes tooltip repositioning after content loads. For high-frequency charts with real-time data, async tooltips may cause flickering or layout shifts; consider pre-fetching data or using synchronous formatters with cached values instead.

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 →