Skip to content

[Feat] tooltip template syntax #9551

Open
@ibgreen

Description

@ibgreen

Target Use Case

Declarative use of tooltips and new "PopupWidget". Currently requires javascript to build the HTML string.

Use of javascript options should of course still be supported.

Proposal

Below is a summary that covers popular templating systems for data‐driven tooltips—focusing on those familiar to both JavaScript and Python data visualization users. This summary highlights each system’s syntax and, in particular, details their numeric formatting capabilities. We also include a section on Plotly’s hovertemplate and a look at systems built around d3-format.


Templating Systems for Data-Driven Tooltips in Deck.gl

A Comparative Overview for JavaScript & Python

Data visualization libraries often rely on declarative templating systems to embed data into tooltips or labels. Many systems are popular in both the JavaScript and Python ecosystems. Below are several approaches that have been widely adopted by data scientists and visualization experts:

  • Plotly’s Hovertemplate
  • Mustache.js (Logic-less Templates)
  • Handlebars.js (Enhanced Mustache with Helpers)
  • Lodash _.template (ERB-Style Templates)
  • Python: Jinja2
  • D3-format–Based Custom Templating

Each system provides a different balance of declarative syntax, inline formatting power (especially for numbers), and integration complexity.


Plotly’s Hovertemplate

Overview:
Plotly’s hovertemplate is used to define custom, data-driven tooltips in Plotly charts. Its syntax is entirely declarative and designed for inline formatting.

Syntax & Formatting Options:

  • Placeholders: Use %{variable} to insert a data value.
  • Inline Formatting:
    • Numeric: %{value:.2f} displays a number with two decimal places.
    • Commas & Separators: %{value:,} adds thousand separators.
    • Dates: %{date:%Y-%m-%d} formats a date using a custom format.
  • Declarative & Compact: The tooltip is defined as a single string, with no need for additional code or helpers.

Familiarity:
Widely used in both Plotly.py (Python) and Plotly.js (JavaScript), making it very intuitive for users coming from data visualization backgrounds.


Mustache.js (Logic-less Templates)

Overview:
Mustache is known as a “logic-less” templating system using double curly braces ({{...}}). It is favored for its simplicity and minimalism.

Syntax & Formatting Options:

  • Basic Replacement:
    • Example: {{value}} simply replaces the placeholder with the corresponding value.
  • Conditional Sections:
    • Use {{#condition}}...{{/condition}} to render content only if a value is truthy.
  • Numeric Formatting:
    • Limitations: Mustache does not support inline formatting (e.g., you cannot specify rounding or comma separators directly).
    • Workaround: Pre-format your numeric values in your data (or supply a function in the data object) before rendering.

Familiarity:
Its syntax is similar to templating languages like Jinja2 (Python) and is popular in lightweight web applications. However, its lack of inline formatting may require extra preprocessing when precise numeric presentation is needed.


Handlebars.js (Enhanced Mustache)

Overview:
Handlebars extends Mustache by adding control structures (such as if/else and loops) and, importantly, custom helpers that enable inline formatting.

Syntax & Formatting Options:

  • Variable Insertion:
    • Basic: {{value}}
  • Helpers for Formatting:
    • Numeric Formatting: With a custom helper (or using community-provided ones), you might write:
      {{format value ".2f"}}
      This helper can leverage JavaScript’s number formatting (or even call d3-format under the hood) to display a number with two decimals.
    • Conditional Logic:
      {{#if value}}
        {{value}}
      {{else}}
        N/A
      {{/if}}
  • Extensibility:
    • You can register a variety of helpers (for dates, percentages, etc.), making it much more powerful than Mustache for inline formatting.

Familiarity:
The syntax is close to Mustache and Jinja2, making it accessible to both JavaScript and Python users. Its additional power comes at the cost of a slightly larger library size.


Lodash _.template (ERB-Style Templates)

Overview:
Lodash’s templating function uses ERB-style delimiters to allow embedding arbitrary JavaScript code. This approach grants full access to JavaScript’s formatting methods.

Syntax & Formatting Options:

  • Template Delimiters:
    • <%= value %> for variable interpolation (automatically HTML-escaped).
    • <% ... %> for arbitrary JavaScript code.
  • Numeric Formatting Inline:
    • Directly call JavaScript methods:
      '<%= value.toFixed(2) %>'
    • Or integrate d3-format:
      '<%= d3.format(".2f")(value) %>'
  • Full Flexibility:
    • Any JavaScript expression is allowed, meaning you can implement complex logic and formatting inline.

Familiarity:
This approach is very powerful for developers comfortable with JavaScript code, but its “code-heavy” nature may feel less declarative compared to other systems.


Python: Jinja2

Overview:
Jinja2 is the go-to templating engine for Python, used extensively in web frameworks like Flask and in data visualization projects.

Syntax & Formatting Options:

  • Variable Substitution:
    • Use {{ variable }} to insert values.
  • Filters for Formatting:
    • Numeric Formatting:
      {{ value | floatformat(2) }}
      This formats a floating point number to two decimal places.
    • Custom Formatting:
      • You can define and register custom filters that, for example, mimic d3-format styles or Python’s format specifiers:
        {{ value | format(",.2f") }}
  • Control Structures:
    • Logic can be added via {% if %}, {% for %}, etc.
  • Extensibility:
    • Easily extend Jinja2 with custom filters to handle dates, percentages, or more complex numerical formats.

Familiarity:
Jinja2’s syntax is similar to Handlebars and Mustache, making it very familiar to Python developers and data scientists. Its robust filtering system makes inline formatting straightforward.


D3-format–Based Custom Templating

Overview:
While there isn’t a standalone templating engine built solely around d3-format, it’s common to integrate d3-format into existing templating systems (such as Handlebars or Lodash templates) to achieve powerful numeric formatting.

Approach & Formatting Options:

  • d3-format Library:
    • d3-format provides functions to format numbers (e.g., d3.format(".2f") for two decimal places, d3.format(",") for thousand separators).
  • Integration Examples:
    • In Lodash Templates:
      '<%= d3.format(".2f")(value) %>'
    • In Handlebars:
      • Register a helper:
        Handlebars.registerHelper("d3format", function(value, spec) {
          return d3.format(spec)(value);
        });
      • Then use it in your template:
        {{d3format value ".2f"}}
  • Advantages:
    • This approach leverages the full power of d3-format for numeric presentation—including support for percentages, SI-prefixes, and custom locale settings.
  • Custom Template System:
    • You could even build a lightweight, custom templating system that directly interprets a Plotly–like syntax (e.g., %{value:.2f}) and internally routes numeric placeholders through d3.format. This would give users a familiar, concise format specification without the overhead of a full templating engine.

Familiarity:
For users already comfortable with d3 and its formatting capabilities (common in data visualization workflows), this approach can be very appealing. It bridges the gap between declarative configuration (as in Plotly) and the power of JavaScript’s formatting functions.


Comparison Table

System Syntax Example Numeric Formatting Familiarity Notes
Plotly’s Hovertemplate %{value:.2f} Inline specifiers for decimals, commas, dates (e.g., %{value:,} or %{date:%Y-%m-%d}) Very high among Plotly users (Python & JS) Declarative; built into Plotly chart definitions.
Mustache.js {{value}} None built-in: Pre-format numbers or use lambdas in the data object. High (similar to Jinja2 in Python) Extremely lightweight; limited inline formatting.
Handlebars.js {{format value ".2f"}} With custom helpers (or community libraries) you can support formats like fixed decimals, commas, etc. High; very familiar to those with Mustache/Jinja2 experience Offers balance between simplicity and inline formatting power.
Lodash _.template <%= value.toFixed(2) %> or <%= d3.format(".2f")(value) %> Full JavaScript expressions allow use of native methods or d3-format for sophisticated numeric and date formatting. Moderate to high (for code-savvy users) Very flexible but more “code-heavy” and less declarative.
Jinja2 (Python) `{{ value floatformat(2) }}` Built-in filters (like floatformat or custom filters) allow similar formatting to Plotly (e.g., `{{ value format(",.2f") }}`)
D3-format–Based Templating {{d3format value ".2f"}} (via Handlebars or Lodash integration) Leverages d3-format (e.g., d3.format(".2f")) to provide advanced numeric formatting (percentages, SI prefixes, etc.) High for users familiar with d3 and data viz libraries Can be integrated as a helper in existing templating engines.

Conclusion

For Deck.gl tooltips, where a declarative, data-driven approach is desired, the choice of templating system depends on the desired balance between simplicity and inline formatting power:

  • Plotly’s Hovertemplate offers an elegant, fully declarative approach with powerful inline numeric and date formatting. It’s ideal if you want to mimic Plotly’s tooltip configuration and are targeting users familiar with Python and Plotly.
  • Handlebars.js provides a friendly syntax along with the flexibility of custom helpers, making it an excellent choice when you need inline formatting (including numeric formatting) without sacrificing clarity.
  • Mustache.js is best when you prefer extreme simplicity and can pre-format data externally, though its lack of inline formatting may be limiting.
  • Lodash _.template is the most flexible option for developers comfortable with writing inline JavaScript, allowing direct use of native methods or d3-format for numeric formatting—but it’s less declarative.
  • Jinja2 stands out on the Python side, offering rich filtering (like floatformat) and extensibility through custom filters, making it very familiar and powerful for Python data scientists.
  • D3-format–Based Templating (via custom helpers) is especially exciting for users already comfortable with d3’s formatting functions, offering a direct bridge between declarative templates and advanced numeric formatting.

Choosing the right system for Deck.gl will depend on your user base and the complexity of the formatting required. For many applications, adopting a Plotly hovertemplate–like syntax or integrating a Handlebars/Jinja2 style with d3-format helpers may offer the best mix of declarative simplicity and robust inline formatting.


Activity

chrisgervang

chrisgervang commented on Mar 29, 2025

@chrisgervang
Collaborator

While this lays out a bunch of options, I think this is missing a discussion about how it fits in with the existing templating in pydeck and @deck.gl/json. Does it replace it? Would we have two? Would this extend to getTooltip or just widgets like InfoWidget.

ibgreen

ibgreen commented on Mar 29, 2025

@ibgreen
CollaboratorAuthor

ould this extend to getTooltip or just widgets like InfoWidget.

I envision that we build a solid templating feature renderTemplate(template, data). We probably build our own, to avoid a dependency.

We should use it wherever we want to support templating as long as we can do so without breaking existing code (needs to be checked for getTooltip).

how it fits in with the existing templating in pydeck and @deck.gl/json

Good question. I looked for existing templating code but could not find it. If we have it, it could be one of the candidates.

PS -A big motivation for having a strong templating feature in core is that AI assistants can easily generate inline deck.gl visualizations as long as they are declarative.

chrisgervang

chrisgervang commented on Mar 29, 2025

@chrisgervang
Collaborator

I looked for existing templating code but could not find it. If we have it, it could be one of the candidates.

http://deck.gl/docs/api-reference/json/conversion-reference

If I remember correctly, @donmccurdy wrote the library many moons ago and it was eventually in-sourced

ibgreen

ibgreen commented on Mar 29, 2025

@ibgreen
CollaboratorAuthor

You are referring to the expression evaluation library used to write simple declarative "callbacks" for accessors.

That's not a string templating library in the way other such systems work, though it could perhaps be used that way if extended with formatting primitives - or perhaps combined with a templating library to add more powerful capabilities.

chrisgervang

chrisgervang commented on Mar 30, 2025

@chrisgervang
Collaborator

Could you provide some examples of what you'd like to render?

I could be misunderstanding something since I don't have a lot of experience using the expression system. I think the json module does a basic form of string templating in these examples.

For tooltip it could look something like getTooltip: "@@='<div>name: '+name+'</div>'" where "name" resolves to the value of data.name.

I'm mainly mentioning it for completeness and can poke holes at its limitations.

ibgreen

ibgreen commented on Mar 30, 2025

@ibgreen
CollaboratorAuthor

If you have data with price and itemname fields, you would use a feature similar to Plotly's hovertemplate like this

new InfoWidget({
  mode: 'hover',
  layerId: 'item-price-layer',
  template: ' <b>%{itemname}</b> <i>Price</i>: $%{y:.2f}'
})

The %{fieldname} syntax is what the template system replaces with the values of the hovered row in the table. There is additional syntax within those for numeric formatting as you can see.

Update: We'd probably need to add a layerId prop as well to ensure the template is only applied to the correct layer's data.

ibgreen

ibgreen commented on Mar 30, 2025

@ibgreen
CollaboratorAuthor

Or maybe we could support multiple layers:

new InfoWidget({
  mode: 'hover',
  layerTemplates: {
   'item-price-layer': ' <b>%{itemname}</b> <i>Price</i>: $%{y:.2f}'
  }
})
changed the title [-][Feat] declarative tooltip support[/-] [+][Feat] tooltip template syntax[/+] on Mar 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    [Feat] tooltip template syntax · Issue #9551 · visgl/deck.gl