Custom tiles
When the built-in tile types don’t fit the visual you want, Saiku ships custom renderers that go further while staying safe. All of them are declarative — you describe the result with data, not code.
Each renderer appears as its own entry in the + Add tile menu (under “Custom”). Picking one drops a tile already bound to that renderer; you then configure it in the tile’s ⚙ editor exactly like any other tile — choose a cube, build a query, and supply the renderer’s config.
Ranked list tile
The Ranked list renderer draws the “movers” card most ops dashboards want: a numbered run of rows, each a label and a value, with the value coloured by whether it’s up or down.
Bind it to a query returning a label column and a value column, then configure:
| Field | Meaning |
|---|---|
| Subtitle | A muted line under the tile title (“Product department · MoM”) |
| Label column | Blank = inferred (see below) |
| Value column | Blank = inferred (see below) |
| Value format | Optional display pattern — $c1 → $27.4M. Blank keeps the cube’s own formatting |
| Rows | How many to show (default 6) |
| Order | Keep the query’s order, highest first, or lowest first |
| Value colour | By sign (up green / down red) or plain |
| Show rank numbers | The leading 1, 2, 3… |
Left blank, the columns are inferred structurally: the value is the
first measure column in the result and the label is what’s left. That
holds even when a dimension’s captions look like numbers — a prescriber
decile (10.0), a year, a store number — which is exactly where guessing
from the text would get it backwards.
Value format takes the same pattern vocabulary as the KPI tile and the
ECharts value axis: $cN compact currency,
$N plain currency, N% percent, N fractional digits. Worth setting on
any currency measure — a top-N card of raw $27,432,535.99 rows is hard
to scan.
Sorting happens before the row limit, so “highest first, 3 rows” is genuinely the top three of the whole result rather than the first three re-ordered. Colours and type follow the App theme — no CSS needed.
ECharts option tile
The ECharts option renderer lets you author a chart by writing an
ECharts option object
directly. It’s the escape hatch for shapes the built-in Chart tile
doesn’t expose — custom axis arrangements, rose variants, unusual
visual maps, and so on.
Because your option is handed to a live charting library, it is validated against a safe subset before it ever renders. The rules, enforced at every depth of the object:
-
No function values. ECharts calls things like
formatterfunctions with live data and DOM, so any function anywhere in the option is rejected. (Plain JSON can’t express a function; the check guards against a live object sneaking one in.) -
No remote URLs. Off-origin
http(s):, protocol-relative//host,url(...)targets pointing off-origin, and non-imagedata:URIs are all rejected — they’re exfil / SSRF vectors. What’s left is same-origin and relative references, plusdata:images in PNG, JPEG, GIF or WebP only.Note SVG is not on that list, even as a
data:image. An SVG can carry script, so it’s excluded on purpose — use a raster export if you need an inline icon. -
Allowlisted keys only. Only curated top-level keys (
title,grid,xAxis,yAxis,series,legend,tooltip,color,backgroundColor,visualMap,radar,polar, and a handful more) and a curated set of per-seriesfields are accepted. Anything outside the allowlist is rejected rather than silently dropped — the validator fails closed.
If your option violates any rule the tile shows a validation error instead of rendering, so you can fix it. The query you bind supplies the data (categories + series) the option draws.
Theme first, option second
Your option is layered over the App’s themed baseline. Anything it states wins; anything it leaves unsaid — title colour, axis label colour, gridlines, the series palette — is inherited from the theme.
Write only the parts that are genuinely bespoke. An option full of hard-coded hex values looks identical on day one and stops matching the App the first time somebody changes preset.
Formatting the value axis
Because functions are rejected, you can’t format an axis the usual
ECharts way — axisLabel.formatter would have to be a function. Instead
the editor has a Value axis format field taking the same pattern the
KPI tile uses:
| Pattern | Renders |
|---|---|
$c0 | $149K — compact currency |
$c1 | $48.2K |
$2 | $99.50 |
1% | 15.6% |
0 | 1,234 |
It’s applied to every type: "value" axis and compiled at render time,
so you never supply code and the no-functions rule stays absolute.
Category axes are left alone — a string template like "W{value}" in
your option still works there, since that’s declarative.
Graph tile
The Graph renderer turns a query’s records into a node-and-edge graph — an ownership tree, a relationship map, a flow between entities. Instead of an option object, you give it a column mapping: which columns in your result are the endpoints, ids, labels, and weights.
| Field | Required | Meaning |
|---|---|---|
sourceCol | yes | Column holding each row’s link source endpoint |
targetCol | yes | Column holding each row’s link target endpoint |
idCol | yes | Column holding a node’s canonical id (so a label can attach to the right node). For a plain edge list, set it equal to sourceCol |
labelCol | no | Column holding a friendly display name for the idCol node |
valueCol | no | Numeric column carried onto each link and summed into node weight |
layout | no | force (default) or circular |
Each row becomes one directed link source → target; nodes are
collected from every endpoint and de-duplicated by id. Rows missing an
endpoint are skipped. When you set valueCol, its value weights both the
link and its endpoint nodes — node size is scaled relative to the
weights in that graph, so the heaviest node always reaches the top of
the size band whatever the measure’s units are. A zero or negative weight
is the lightest node, not an unweighted one.
Configuring a custom tile
The flow is the same for all of them:
- + Add tile → Ranked list (or ECharts option, or Graph).
- Open the tile’s ⚙ editor.
- Bind a query — pick the cube and build the query whose result feeds the tile.
- Supply the config — the ranked-list fields, the ECharts
optionobject, or the graph column mapping. - Save. Invalid config surfaces as an inline error rather than a broken render.
Related
- Building pages & tiles — the built-in tile types and how binding works.
- The Chart tile — check here before writing an ECharts option by hand.
- Plugins — the advanced, admin-installed sandboxed-JS tile for fully custom widgets.