Interactive HTML logo
Interactive HTMLFree Power BI visual

Build interactive Power BI reports with plain HTML

Interactive HTML is a free Power BI visual. Write your layout in a DAX measure, add a few attributes, and your cards filter the page, open drawers and switch tabs. You write no JavaScript.

Get StartedTry the demo
Free21 attributesNo JavaScriptWorks with AI
Powered byBizAcuityData analytics, since 2011
Power BI reportDemo
Interactive HTML visualClick a card to see its details.

This window is a demo. In Power BI, the cards and panel are one Interactive HTML visual, built from a DAX measure.

What you can do with it

If you have used the HTML Content visual, you already know how to start. Interactive HTML shows the same markup, then lets it respond to clicks.

Filter the whole page

Click a card and every other visual responds, just like a bar chart. Ctrl+click selects several.

Open drawers and modals

Show details for the row you clicked. No bookmarks and no duplicate pages.

Switch tabs inside one visual

Build tabs, toggles and density switches with two attributes.

Use Power BI’s own menus

Right-click for Include, Exclude and Show as a table. Hover for native tooltips.

Match your theme

Colours can follow the report theme. Show different content when editing and reading.

Stay safe

Scripts, event handlers and external links are removed. The visual makes no network requests.

Let AI write your measures

You do not need to learn the attributes first. Copy the prompt below into Claude, Copilot, ChatGPT or any AI assistant, describe the card you want in plain English, and paste the measure it gives you into Power BI.

1

Copy the prompt

It holds the full rules, so the AI writes measures that work the first time.

2

Describe what you want

Add your table and column names and say what the card should show and do.

3

Paste into Power BI

Create a new measure, paste the result, and put it in the HTML well.

Things you can ask for

  • A KPI card per service showing MTTR against target, red when it is over
  • Clicking a card filters the whole page
  • A drawer that breaks the number down
  • Three tabs: Overview, Trend and Notes
  • Sort links for highest and lowest

Example request to send after the prompt: “Using the table Sales with columns Region and Amount, make a card per region that shows total amount, filters the page when clicked, and opens a drawer with the region’s name and amount.”

You write Power BI DAX measures that return HTML for the Interactive HTML visual. Output a complete measure, nothing else. After the measure, add one line starting "Bind:" that says which column to put in the Category well.

## How the visual works
- A DAX measure returns an HTML string. The visual sanitizes and renders it.
- Field wells: HTML (the measure, required), Category (renders the markup once per value, each with its own selection identity), Tooltips (extra values).
- With a Category bound, your markup renders once per category row. Panel state and selection are per row automatically.
- If I do not give table or column names, use clear placeholders such as 'Table'[Column].

## Hard rules
1. DAX strings use double quotes, so write ALL HTML attributes with SINGLE quotes: style='...' not style="...".
2. Concatenate with &. Wrap measure values in FORMAT() to control their text.
3. NEVER emit <script>, on* handlers, javascript: URLs, @import, expression(), or url() pointing anywhere but a data:image URI. They are stripped.
4. All styling is ordinary inline CSS. There is no styling vocabulary.
5. Declare panels (data-pbi-view) as TOP-LEVEL siblings of the card, never nested inside it.
6. drawer/modal panels are relocated in the DOM, so style them inline or by their own class, never with a descendant selector.
7. data-pbi-when elements start hidden until a matching data-pbi-set fires.
8. Escape HTML entities in text: &amp; &lt; &gt; &mdash; &rsaquo; &times;.
9. Images must be embedded: <img src> and CSS url() take only data:image URIs (png, jpeg, gif, webp, svg+xml, avif). The visual makes no network requests, so an external image URL is removed. srcset is always removed.

## Vocabulary (21 attributes, exhaustive - use nothing else)
SELECTION
data-pbi-select        empty | "multi"   cross-filter the page on click
data-pbi-clear         empty             clear the selection
data-pbi-selected      empty             marker; gets .pbi-is-selected
data-pbi-highlighted   empty             marker; gets .pbi-is-highlighted
data-pbi-context-menu  empty             native right-click menu
PANELS
data-pbi-view          identifier        declare a named panel
data-pbi-as            drawer|modal|inline|tab   how it presents
data-pbi-click         open:id|close:id|toggle:id
data-pbi-group         identifier        grouped views act as radios
data-pbi-persist       empty             survives bookmarks/navigation
STATE
data-pbi-set           key=value         set state on click
data-pbi-when          key=value         show only while state matches
PRESENTATION
data-pbi-tooltip       text, may use {{Field}} tokens
data-pbi-scope         live | static     static keeps colour when dimmed
data-pbi-theme-color   foreground|background|accent1..accent8   sets --pbi-theme on the element
data-pbi-when-mode     view | edit | export
data-pbi-empty         empty             shown when there are no rows
POWER BI ACTIONS
data-pbi-open-url      absolute https URL   host prompts the viewer first
data-pbi-sort          field|field:asc|field:desc   client-side reorder
data-pbi-tooltip-page  empty             report-page tooltip
data-pbi-drill         down|up|next      BEST-EFFORT; Desktop ignores it. Prefer native drill buttons.

## Grammars
- identifiers (view ids, groups, state keys): ^[A-Za-z][A-Za-z0-9_-]{0,63}$
- state values: ^[A-Za-z0-9_-]{1,64}$
- No expressions are evaluated anywhere. Values are matched literally.

## Classes the visual applies (style these)
.pbi-is-selected .pbi-is-dimmed .pbi-is-highlighted .pbi-is-open

## Not supported - do not emit, suggest the alternative instead
data-pbi-goto-page    -> native button with a Page navigation action
data-pbi-target-visual -> Format > Edit interactions
data-pbi-drillthrough -> data-pbi-context-menu offers it natively
data-pbi-query        -> bring the data in through a field well
data-pbi-fetch        -> load it into the model
data-pbi-writeback    -> the Power Apps visual
data-pbi-script       -> express it with this vocabulary
data-pbi-filter       -> data-pbi-select to filter the page; a native slicer for a filter that persists; right-click > Include to filter just this visual

## Do not reinvent the platform
<details>, <dialog>, the Popover API, :has(), :target and CSS transitions all work and survive sanitization. Use them directly rather than the vocabulary.

## Style defaults, unless asked otherwise
Segoe UI; white card on a 1px #e1dfdd border, 10px radius, 12-14px padding; uppercase 10px letter-spaced labels in #8b93a8; 26px 650-weight figures; #1d5fb8 for links and actions; green #006300 / red #a02525 for good and bad.
If something does not work, turn on Debug in the visual’s Format pane. Copy any warning it shows and paste it back to the AI. It will fix the measure.

Get started in five steps

You need Power BI Desktop and the visual from Microsoft AppSource.

1

Add the visual

In Power BI Desktop, open the Visualizations pane, select … (more options), then Get more visuals. Search for Interactive HTML and select Add. If you have the .pbiviz file instead, choose Import a visual from a file.

Interactive HTML visual logoLook for this logo and the name Interactive HTML in the results.
2

Place it on the page

Select the new icon in the Visualizations pane. With no fields yet, it shows a short message telling you which fields it needs. That is normal.

3

Fill the three field wells

WellRequired?What to put there
HTMLYesThe measure that returns your markup.
CategoryFor filteringA column such as Region. Your markup repeats once per value, each with its own selection.
TooltipsNoExtra values for tooltips and for {{Field}} text.
No data yet? Create a table with Modeling → New table and paste the demo data below.
Demo Services =
DATATABLE (
    "Service", STRING, "Tier", STRING, "MTTR", DOUBLE,
    "Incidents", INTEGER, "P1", INTEGER, "Target", DOUBLE,
    {
        { "Payments", "Tier 1", 4.2, 47, 3, 6.0 },
        { "Identity", "Tier 1", 7.8, 31, 5, 6.0 },
        { "Data Platform", "Tier 2", 3.1, 22, 1, 6.0 },
        { "Network Core", "Tier 1", 9.4, 58, 8, 6.0 },
        { "Collaboration", "Tier 3", 2.6, 14, 0, 6.0 }
    }
)
4

Write your first measure

DAX text uses double quotes, so write HTML attributes with single quotes. Create this measure, put it in the HTML well, and put Sales[Region] in Category. Replace the table and column names with yours.

Interactive Card =
VAR Region = SELECTEDVALUE ( Sales[Region] )
VAR Amount = FORMAT ( SUM ( Sales[Amount] ), "#,##0" )
RETURN
"<div data-pbi-select data-pbi-tooltip='Click to filter the page'
  style='padding:14px;border:1px solid #e1dfdd;border-radius:8px;
  font-family:Segoe UI;background:#fff;cursor:pointer'>
  <div style='font-size:12px;color:#605e5c'>" & Region & "</div>
  <div style='font-size:24px;font-weight:600'>" & Amount & "</div>
</div>"
5

Click a card

One card appears per region. Click one and the page filters. Ctrl+click adds more. Click the background to clear. Selected cards get the class pbi-is-selected and the others get pbi-is-dimmed, so you can style both.

Stuck? Open the visual’s Format pane and turn on Debug. It shows how many rows rendered and names any attribute it did not understand.

The 21 attributes in plain English

Everything else is normal HTML and CSS. Every attribute starts with data-pbi-, and you add it to any element.

Selection

AttributeWhat it does
selectFilters the page when clicked. Use select="multi" to add to the selection without Ctrl.
clearClears the selection when clicked. An open panel stays open.
selectedMarks an element to receive pbi-is-selected while its row is selected.
highlightedMarks an element to receive pbi-is-highlighted when another visual highlights the row.
context-menuShows Power BI’s right-click menu for the row.

Panels

AttributeWhat it does
view="name"Declares a panel with a name. Place it beside the card, not inside it.
asSets how the panel appears: drawer, modal, inline or tab.
clickOpens, closes or toggles a panel: open:name, close:name, toggle:name.
group="name"Panels in the same group close each other when one opens.
persistSaves the panel or state in bookmarks and keeps it when you change pages.

Tabs and remembered choices

AttributeWhat it does
set="tab=summary"Remembers a choice when clicked.
when="tab=summary"Shows an element only while that choice is active. Nothing shows until a choice is made, so decide on a default tab.

Look and context

AttributeWhat it does
tooltipShows a native Power BI tooltip. Use {{Field}} to insert values from the Tooltips well.
scopeWith static, an element stays readable when its card is dimmed.
theme-colorUses a report theme colour such as accent1 instead of a fixed colour.
when-modeShows an element only in view or edit mode, for example an author-only note.
emptyMarks the message to show when there are no rows.

Power BI actions

AttributeWhat it does
open-urlOpens a full https:// link in a new tab. Power BI asks the viewer first.
sort="MTTR:desc"Reorders the rows already shown. It is instant and runs no new query.
tooltip-pageShows a report-page tooltip. The page must be set to “Allow use as tooltip”.
drillBest effort only. Power BI Desktop ignores it. Put several fields in Category and use the drill buttons in the visual header.

Copy-and-paste examples

These use the demo table from step 3, with 'Demo Services'[Service] in Category.

A card that opens its own drawer

Card With Drawer =
VAR Svc = SELECTEDVALUE ( 'Demo Services'[Service] )
VAR Mttr = FORMAT ( SUM ( 'Demo Services'[MTTR] ), "0.0" )
RETURN
"<div data-pbi-select style='font-family:Segoe UI;padding:12px;border:1px solid #e1dfdd;
  border-radius:10px;background:#fff;margin:6px;cursor:pointer'>
  <div style='font-size:11px;color:#8b93a8'>" & Svc & "</div>
  <div style='font-size:26px;font-weight:650'>" & Mttr & " hrs</div>
  <span data-pbi-click='open:detail' style='color:#1d5fb8;font-weight:600;cursor:pointer'>Breakdown</span>
</div>
<section data-pbi-view='detail' data-pbi-as='drawer' style='font-family:Segoe UI;padding:18px;background:#fff'>
  <span data-pbi-click='close:detail' style='float:right;cursor:pointer;font-size:20px'>&times;</span>
  <h3>" & Svc & "</h3>
  <p>Mean time to repair: <strong>" & Mttr & " hrs</strong></p>
</section>"

Tabs with no bookmarks

Tabs =
"<div style='font-family:Segoe UI;padding:12px'>
  <nav style='display:flex;gap:16px;border-bottom:1px solid #e7eaf4;padding-bottom:8px'>
    <span data-pbi-set='tab=summary' style='cursor:pointer'>Summary</span>
    <span data-pbi-set='tab=trend' style='cursor:pointer'>Trend</span>
  </nav>
  <div data-pbi-when='tab=summary' style='padding-top:12px'>Summary panel</div>
  <div data-pbi-when='tab=trend' style='padding-top:12px'>Trend panel</div>
</div>"

Avoid these common problems

Use single quotes

Double quotes inside a DAX string end it early. Write style='…'.

Panels sit beside the card

Declare drawers and modals as siblings of the card, not inside it.

Style panels directly

Drawers and modals move in the page. Use inline styles or their own class, not .card .drawer.

Tabs start empty

Nothing shows until a tab is clicked. Choose one tab as the resting state.

Embed your images

The visual makes no web requests. Use data:image/… links. Web images are removed.

Rows stack

Each category row appears under the last. Design a row-style card, not a grid.

Field names change

Power BI may rename a field to “Sum of Incidents”. Both names work in {{ }}. If text shows literally, check the name.

Strict mode removes more

The Strict security setting also removes <svg> and <style>. Keep styling inline.

Limits. Interactive HTML cannot go to another report page, filter one named visual, query the model on demand, write back to data, or run JavaScript. Use a native button for page navigation and Edit interactions to control which visuals respond. To keep a filter, use a native slicer.

Questions

Does it cost anything?

No. Interactive HTML is free.

Do the people who open my report need to install it?

No. The visual travels inside the report file, so viewers install nothing.

Is it safe?

Yes. Scripts, event handlers and javascript: links are removed before anything is shown. The visual cannot make network requests, so your report cannot send data out.

It won’t load in my company. Why?

Your Power BI admin controls this. They need to allow visuals created with the Power BI SDK in the tenant settings. If the setting that allows only certified visuals is on, ask your admin to add Interactive HTML under Organizational visuals.

Can I export to PDF or PowerPoint, or send email subscriptions?

Not yet. Power BI only supports certified custom visuals in those routes, and this visual is not certified yet. Everything on screen works normally. If a report must be exported, also show the key numbers in a native visual.

How do I update to a newer version?

Import the newer file over the old one. It keeps the same ID and replaces the old version. Your measures and field settings stay as they are.

Which settings can I change without DAX?

In the Format pane: security strictness, selection limit (default 50, up to 250), maximum rows (default 1,000, up to 30,000), drawer width, dimming, animation, theme, fonts and Debug.

Ready to make your reports interactive?

Get Interactive HTML free, or talk to the BizAcuity team about cards, drawers and dashboards built for your reports.

Get it on AppSourceTalk to BizAcuity

The free six-page sample report on AppSource shows every feature working on built-in data.

BizAcuity
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.