Custom visual SDK reference

A custom visual's code draws its tile with the uni global: the rows the report sends, selection, saved state, the report theme, assets and the outside content an administrator allows. This page is the reference for that global, for the template tier that draws without JavaScript, and for the limits and checks that go with them. For what a custom visual is and how it is shared, see Custom visuals and the visual library; for the editing workflow, see The custom visual Studio.

What the SDK gives you

Paint, events and helpers

Read the data

Selection and the slicer role

State, format and theme

Assets, images, embeds and libraries

Template directives

The template tier draws HTML and CSS only. The directives:

DirectiveWhat it does
data-uni-repeat="row in rows"Repeats the element over an array, such as rows or groups('Region'); index is the item number.
data-uni-if="expression"Renders the element only when the expression is truthy.
data-uni-select="Field"A click selects the row's value, or a group's key; Ctrl or Cmd adds. The renderer writes data-uni-selected as on, off or neutral.
data-uni-suppress="select"Cancels the selection from clicks inside the element, for controls on a selectable card.
data-uni-srcWrites an asset: reference or an allowlisted https image to src; unknown assets and http addresses never load.
data-uni-embedMarks the element as an embedded player's box.
data-uni-classAdds the classes whose expressions are truthy, as in { 'is-active': row.Level == 2 }.
data-uni-style-<property>Sets one allowed CSS property; custom properties and the layout, colour, type and SVG paint families are allowed.

Limits

WhatCap
Source filesvisual.html 96 KB, visual.css 64 KB, visual.js 128 KB, 256 KB together
Template96 KB, 20,000 rendered nodes, expressions nesting up to 32 deep
Viewer state16 KB
Embeds32 per visual
Slicer set1,000 values
Native export400 elements; 2,000 characters of text; a table 30 columns by 100 rows; a chart 200 categories and 20 series

Checks and diagnostics

Questions, answered

How do I show a value in the report's own format?

uni.format.value(value, field) applies the format saved for that field, and uni.data.text(row, field) does the same when you read a row.

How do I react when new data arrives?

You do not need to register anything: the host runs your uni.render callback again on every data, size, theme, selection, filters, options, state or mode change. uni.on('data', cb) and the other events give you the same moments for side reads.

Is there an SDK for the template tier?

No. A Template visual runs no author JavaScript, so uni does not exist in it; read rows, groups and options through interpolations and the directives above. Switch to HTML + JS when you need libraries or the full uni global.

Why does uni.data.requestMore() do nothing?

It cannot load more in SDK v1: the call resolves false and records the warning uni.data.requestMore() is not available in SDK v1; the row limit is fixed by the tile. Set the tile's rows under General > Limits instead.

How much can I keep in uni.state?

Up to 16 KB. A bigger patch is refused with uni.state.set: state would be N bytes, above the 16384-byte cap; keep only what the visual needs to restore itself, and a set made inside a paint is applied when the paint ends.

When should I cancel timers, animation frames and observers?

In uni.onDispose, registered once at the top of the file. It runs when your code is replaced or the tile closes, newest first, so an old loop cannot keep running beside a new one.

Which libraries can I import, and how?

The twelve listed above, by bare name, for example import * as d3 from 'd3'. Only the libraries the visual declares reach the frame; anything else is refused and the refusal names it.

Why is uni.select doing nothing?

Either selection is switched off for the tile, which warns uni.select: selection is disabled for this visual (interactions.select = false), or the tile is in the slicer role, where uni.select is disabled and uni.slicer.set replaces it. Check uni.capabilities.select and uni.capabilities.slicer.

What does a row's __selected value mean?

on, off or neutral. Your own marks set it, and so does Highlight from another tile: the rows the selection keeps read on, the rest read off. neutral means nothing selects that row.

Why does uni.image reject an image?

The rejection carries a code: host-not-allowed, not-https, not-an-image, too-large or upstream. A refusal by host, scheme, type or size is remembered, so a repeat fails the same way; an upstream failure is retried. Ask an administrator to allow the host, or load from one that is already allowed; see Sandbox, network and the allowlist.

Is uni.unsafeHtml actually unsafe?

No. It is the only entry that accepts raw markup, and the markup runs through the sandbox's sanitiser first; uni.html escapes every interpolated value. Keep row values in text positions either way, because escaping does not make a value safe inside href, src or style.

Why does a colour disappear when I write var(--primary)?

Theme names are colour channels, not colours: var(--primary) holds channel numbers, so the browser drops the declaration. Write hsl(var(--primary)) or var(--uni-primary).