Handlebars charts on 6.1.0: getting HTML and CSS to actually render #44019
rlei-odes
started this conversation in
Show and tell
Replies: 1 comment 2 replies
|
Pretty good! This is a great place to put the results you learned along the way. A couple of corrections: Claude hallucinated a version 6.2, that has not be released and likely won't be. Superset is currently on 6.1.0 with 7.0.0 in the pipeline. And you shouldn't need to set |
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hey all, after discovering what a Handlebar chart even is, I was set back to discover that the rendering did not work on my installation. After even the llm giving up, some more digging got it to work and I was pleasantly surprised to see what is possible. In short, get an output from your data with html and css.
So here is the whole thing: the config that works, the two rules that decide
whether your CSS does anything, and a complete worked example you can paste. Point your llm of choice to this post.
Everything below was verified on 6.1.0.
That is a Handlebars chart on
cleaned_sales_data, which ships with Superset'sexample data — the SQL, template and CSS are all at the bottom.
1. The config for your installation
After editing the config restart the web process and hard-reload the tab. Please note these settings come with security trade-offs: anyone who can author a chart or markdown tile can then inject CSS app-wide.
2. Two rules that decide whether your CSS works
Rule 1 — ids reach the DOM with a
user-content-prefixThis is the one that cost a couple of experiments to get it right.
The sanitizer sets
clobberPrefix: 'user-content-', so:Your CSS must therefore say
#user-content-card { … }. A#cardselectormatches nothing, silently. Classes are not prefixed — only ids and names.
Rule 2 — on 6.1.0,
classworks on some tags and not othersWorks on
div,span,tr,td,th,p,table.Does not work on
ul,ol,li,a,code,h2,section. Those tagspin
classNameto fixed values in the sanitizer's default schema, and on 6.1.0an override for them is silently discarded. The tell in devtools is that the
attribute survives but arrives empty:
class="".The fix (#42202, merged 2026-08-04) is not in 6.1.0. It probably lands in the upcoming version. After upgrading past that point you can add per-tag entries and
classworks everywhere:
In practice Rule 2 stops mattering, because a descendant selector off one
id reaches everything:
Also works with no extra config: inline
style="…"on every tag including<li>, Handlebars-interpolated styles,<details>/<summary>, and thepresentational attributes already in the schema (
align,width,colSpan,title, …).3. The full example
Everything for the screenshot at the top. It uses
cleaned_sales_datafrom theshipped example data, so it should run as-is.
The query — a virtual dataset in SQL Lab
Aggregate in SQL and let the template do presentation only. That split matters
more than it looks: the
sumhelper is binary (sum a b), so there is noclean way to total an array in Handlebars — you would be abusing
{{#each}}asan accumulator.
MAX(SUM(sales)) OVER ()gives every row the largest row's revenue, which iswhat the share bar scales against. Scaling against the total instead gives
shares of 1–5% that render as invisible slivers.
Save it as a dataset, then in the chart set Query mode: Raw records and list
the five columns. In Aggregate mode Superset re-groups the already-grouped
result.
The HTML template
Three things worth pointing at:
the part that varies per row.
classon<tr>works even on 6.1.0, because<tr>carries noclassNamerule in the default schema, so the"*"rule reaches it. An idwould be wrong here: ids must be unique and get the prefix.
titleon an inner<span>, not on the<th>— the tooltip cue thensits under the label text only, so a column that carries an explanation looks
visibly different from one that does not.
The CSS — goes in the chart's CSS Styles panel
Note the last rule:
#user-content-foot codestyles the<code>tags eventhough
classon<code>is a dead end on 6.1.0. That is Rule 2's escapehatch in one line.
4. If you put it on a dashboard with filters
Worth knowing before you build on this. A native filter reaches a Handlebars
chart like any other, but Superset applies it to the outside of a virtual
dataset:
That is after your aggregates have already run. The example above has exactly
this problem: filter it to Ships and Trains and both bars collapse to slivers,
because
share_pctstill divides by Classic Cars — a row no longer in theresult. The widest bar goes from 100% to 18.2%. Nothing errors, the table just
quietly stops meaning what it says.
To push the filter into the aggregation, enable
ENABLE_TEMPLATE_PROCESSING(this one is a feature flag) and read the values with Jinja:
{% set p_line = filter_values('product_line', remove_filter=True) %} SELECT product_line, SUM(sales) AS revenue, ... FROM cleaned_sales_data WHERE 1 = 1 {% if p_line %} AND product_line IN ({{ "'" ~ p_line | join("','") ~ "'" }}) {% endif %} GROUP BY product_lineremove_filter=Trueis the load-bearing part — without it the filter is appliedtwice, once where you want it and once on the outer wrapper.
Two things to know before turning that flag on: it applies to every virtual
dataset, and Jinja parses the SQL before the database sees it — so a stray
{{anywhere, including inside a
--comment, breaks the dataset.5. Quick troubleshooting table
<div class="…">text on the chartESCAPE_MARKDOWN_HTMLis on"style"missing fromtagNamesHTML_SANITIZATION_SCHEMA_EXTENSIONSis not reaching the frontend, or your CSP is blocking inline styles (style-srcneeds'unsafe-inline'). Rule them out in that orderuser-content-prefix on your id selectorsclass=""on<li>/<ul>/<a>in devtoolsMissing helper: "add"Parse error … Expecting 'ID', got 'NUMBER'data.0used as a helper argument; writedata.[0]All reactions