Updated March 2026

Power BI SVG Visuals & Custom KPI Cards: The Complete Guide

Build pixel-perfect KPI dashboards using SVG DAX measures and the new card visual. Five copy-paste recipes for traffic lights, sparklines, bullet charts, progress bars, and star ratings — no custom visuals required.

5 SVG RecipesCopy-Paste DAXNew Card VisualNo AppSource Required

Quick Answer: What Are SVG Visuals in Power BI?

SVG visuals in Power BI use DAX measures that return SVG XML strings, rendering inline graphics directly in tables and matrices without custom visuals. Combined with the new card visual (introduced in 2024), you can create pixel-perfect KPI dashboards that work everywhere — Power BI Service, email subscriptions, PDF exports, and embedded reports. The technique requires zero AppSource marketplace dependencies, works across all deployment models (Pro, PPU, Fabric), and gives you complete control through DAX.

This guide covers the new card visual configuration, five production-ready SVG patterns with full DAX code, and enterprise KPI design patterns our team uses at EPC Group when building executive dashboards for Fortune 500 clients. Every DAX measure below is copy-paste ready.

Why Use SVG in Power BI?

Custom visuals from AppSource solve many problems, but they introduce dependencies that enterprise environments cannot always accept. SVG visuals rendered through DAX eliminate those dependencies entirely. Here is why leading organizations are adopting this approach for their dashboard implementations.

No Custom Visual Dependency

AppSource visuals are maintained by third-party vendors. If a vendor discontinues their visual or introduces a breaking change, your reports break. SVG DAX measures depend only on the Power BI engine itself. Your visuals will work as long as Power BI exists, because they are just DAX strings rendered as images.

No AppSource Marketplace Approval Required

Many enterprise tenants restrict AppSource visuals through admin policies. Healthcare organizations under HIPAA and government agencies under FedRAMP often block custom visuals entirely. SVG measures bypass this restriction because they are standard DAX — no admin approval needed, no security review of third-party code, no organizational visual store management.

Works in All Deployment Models

SVG measures work identically in Power BI Pro, Premium Per User (PPU), Premium capacity, and Microsoft Fabric workspaces. Some AppSource visuals have licensing requirements or feature limitations in certain capacity tiers. DAX-based SVGs have zero licensing constraints.

Renders in Email Subscriptions and PDF Exports

Many custom visuals do not render in Power BI email subscriptions or PDF exports. SVG DAX measures render as static images, which means they appear correctly in subscriptions, exports, and screenshot scenarios. This is critical for executive dashboards where stakeholders receive scheduled email reports.

Fully Controlled by DAX

Every pixel of an SVG visual is driven by your DAX formulas. Colors, sizes, positions, labels — everything is a calculation. This means your visuals respond dynamically to filter context, slicers, and cross-filtering just like any other measure. You can apply business logic directly to the visual representation.

The New Card Visual (2024+)

Microsoft introduced a completely redesigned card visual in 2024 that replaces both the legacy card and multi-row card. The new card visual is dramatically more capable and is now the default for KPI display. If you are still using the old card visual, switch immediately — the new version is superior in every way.

Key Features of the New Card Visual

FeatureDescriptionLegacy Card
Multiple Callout ValuesDisplay several KPIs on one card with independent formattingSingle value only
Reference LabelsShow comparison text like "vs. Last Quarter" with variance valuesNot available
Built-in SparklinesAdd trend line directly to the card — no DAX SVG neededNot available
Target/Actual ComparisonVisual comparison against target with automatic variance calculationManual setup required
Conditional FormattingApply rules to callout value, reference labels, background, and borders independentlyLimited options
Sub-valuesAdd secondary metrics below the main callout for contextNot available
Layout ControlHorizontal or vertical stacking, alignment, spacing, and paddingFixed layout

Step-by-Step: Creating a KPI Card with the New Visual

1

Insert the New Card Visual

From the Visualizations pane, select the "Card (new)" icon. If you see the legacy card, click the "Build visuals" dropdown arrow next to it and select the new version. In reports created after 2024, the new card is the default.

2

Add the Callout Value

Drag your primary KPI measure (e.g., Total Revenue) into the "Callout value" well. Format the display units, decimal places, and font size in the Format pane under Callout values. You can add multiple callout values for a multi-KPI card.

3

Configure Reference Labels

In the Format pane, expand "Reference labels" and toggle it on. Set the value type to "Comparison" and select your comparison measure (e.g., Prior Year Revenue). The card automatically calculates and displays the variance with up/down indicators.

4

Add a Sparkline

In the Callout value section of the Format pane, expand "Sparkline" and toggle it on. Select a date field for the X-axis and the measure for the Y-axis. Choose line or bar style. The sparkline renders as a compact trend line below the callout value.

5

Apply Conditional Formatting

Right-click the callout value in the Visualizations pane and select "Conditional formatting". You can apply rules to the font color (e.g., green for positive variance, red for negative), the background color, and the reference label independently. Use "Rules" mode for threshold-based formatting or "Field value" mode for a DAX-driven color measure.

The new card visual handles most standalone KPI display needs. Where SVG measures become essential is when you need inline indicators inside table and matrix visuals— traffic lights next to product names, sparklines per sales rep, progress bars per project. That is the focus of the next sections.

SVG Basics for Power BI

The core technique is simple: a DAX measure returns a string that Power BI renders as an image. The string must follow the data:image/svg+xml;utf8,data URI format, followed by valid SVG markup. When the column containing this measure is configured with the "Image URL" data category, Power BI renders the SVG inline.

The Basic Pattern

Every SVG measure in Power BI follows this structure. The measure must return a single text string per row:

SVG Basic Example =
VAR _Value = [Your Measure]
VAR _Color =
    SWITCH(
        TRUE(),
        _Value >= 90, "#22C55E",   -- Green
        _Value >= 70, "#EAB308",   -- Yellow
        "#EF4444"                   -- Red
    )
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" &
        "<circle cx='10' cy='10' r='9' fill='" & _Color & "'/>" &
    "</svg>"
RETURN
    _SVG

Setting Up the Image Column

After creating your SVG measure, you must tell Power BI to render it as an image rather than displaying the raw text string:

  1. Add a Table or Matrix visual to your report canvas.
  2. Drag your SVG measure into the Columns (Table) or Values (Matrix) well.
  3. Select the SVG measure field in the visual.
  4. Go to the Column tools tab in the ribbon (or the measure properties in the model view).
  5. Set Data category to "Image URL".
  6. In the visual's Format pane under Cell elements > Image, set the Image size to "Fit" and adjust the Image height to your preferred pixel size (20–30px works well for inline indicators).

Important: SVG Encoding Rules

Power BI's SVG rendering requires specific encoding. Use single quotes for SVG attributes (not double quotes, which conflict with the DAX string). Do not use # directly for hex colors inside some older Power BI versions — if colors do not render, use %23 instead (e.g., %2322C55E for #22C55E). Always include the xmlns attribute on the root SVG element. The viewBoxattribute controls the coordinate system and aspect ratio — always set it explicitly.

5 SVG Pattern Recipes with Full DAX Code

Each recipe below is production-tested and ready to paste into your Power BI model. Adjust the table and column references to match your data model. For star schema data models, these measures should be created in the fact table or a dedicated measures table.

Recipe 1: Traffic Light Indicator

The most common SVG pattern in Power BI. Displays a colored circle (green, yellow, or red) based on KPI thresholds. Use this for status columns in project trackers, SLA compliance tables, and quality scorecards. The circle renders at a consistent size and aligns cleanly with text in adjacent columns.

Assumption: You have a measure called [Score %] that returns a value between 0 and 100.

Traffic Light =
VAR _Score = [Score %]
VAR _Color =
    SWITCH(
        TRUE(),
        _Score >= 90, "#22C55E",   -- Green: On Track
        _Score >= 70, "#EAB308",   -- Yellow: At Risk
        _Score >= 50, "#F97316",   -- Orange: Behind
        "#EF4444"                   -- Red: Critical
    )
VAR _Label =
    SWITCH(
        TRUE(),
        _Score >= 90, "On Track",
        _Score >= 70, "At Risk",
        _Score >= 50, "Behind",
        "Critical"
    )
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20'>" &
        "<circle cx='10' cy='10' r='9' fill='" & _Color & "'/>" &
        "<text x='24' y='15' font-family='Segoe UI,sans-serif' font-size='12' fill='%23374151'>" &
            _Label &
        "</text>" &
    "</svg>"
RETURN
    _SVG

Tip: If you only need the circle without text, change the viewBox to 0 0 20 20 and remove the <text>element. For a minimalist dot, reduce the radius to 5–6.

Recipe 2: Sparkline Chart

Renders a mini line chart inside a table row, showing trend over time per category. This is the most powerful SVG pattern because it adds temporal context to every row. Use it for revenue trends per product, monthly headcount per department, or daily incident counts per system.

Assumption: You have a Calendar table with a [Date] column and a Sales table with an [Amount] column. The sparkline shows the last 12 months.

Sparkline SVG =
VAR _Dates =
    SELECTCOLUMNS(
        FILTER(
            ALLSELECTED( Calendar[Date] ),
            Calendar[Date] >= EDATE( MAX( Calendar[Date] ), -12 ) &&
            Calendar[Date] <= MAX( Calendar[Date] )
        ),
        "Date", Calendar[Date]
    )
VAR _Data =
    ADDCOLUMNS(
        _Dates,
        "@Val", CALCULATE( SUM( Sales[Amount] ) )
    )
VAR _MinVal = MINX( _Data, [@Val] )
VAR _MaxVal = MAXX( _Data, [@Val] )
VAR _Range = IF( _MaxVal - _MinVal = 0, 1, _MaxVal - _MinVal )
VAR _Count = COUNTROWS( _Data )
-- SVG dimensions
VAR _W = 150
VAR _H = 30
VAR _Padding = 2
-- Build the SVG path
VAR _Points =
    CONCATENATEX(
        ADDCOLUMNS(
            ADDCOLUMNS(
                _Data,
                "@Index",
                RANKX( _Data, [Date], , ASC, DENSE ) - 1
            ),
            "@X", _Padding + ( [@Index] / MAX( _Count - 1, 1 ) ) * ( _W - 2 * _Padding ),
            "@Y", _Padding + ( 1 - DIVIDE( [@Val] - _MinVal, _Range ) ) * ( _H - 2 * _Padding )
        ),
        IF( [@Index] = 0, "M", "L" ) &
            FORMAT( [@X], "0.0" ) & "," & FORMAT( [@Y], "0.0" ),
        " ",
        [@Index], ASC
    )
-- Determine trend color
VAR _FirstVal = MINX( TOPN( 1, _Data, [Date], ASC ), [@Val] )
VAR _LastVal = MINX( TOPN( 1, _Data, [Date], DESC ), [@Val] )
VAR _Color = IF( _LastVal >= _FirstVal, "%2322C55E", "%23EF4444" )
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 " & _W & " " & _H & "'>" &
        "<path d='" & _Points & "' fill='none' stroke='" & _Color & "' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/>" &
        "<circle cx='" & FORMAT( _Padding + ( ( _Count - 1 ) / MAX( _Count - 1, 1 ) ) * ( _W - 2 * _Padding ), "0.0" ) &
            "' cy='" & FORMAT( _Padding + ( 1 - DIVIDE( _LastVal - _MinVal, _Range ) ) * ( _H - 2 * _Padding ), "0.0" ) &
            "' r='3' fill='" & _Color & "'/>" &
    "</svg>"
RETURN
    _SVG

How it works: The measure iterates over the last 12 months of dates, calculates a value per month, normalizes each value to fit within the SVG viewBox, and builds an SVG <path> element using M (move to) and L (line to) commands. The line color is green if the trend is upward, red if downward. A dot highlights the most recent data point.

Recipe 3: Bullet Chart

A bullet chart compares actual performance against a target with qualitative ranges (poor, satisfactory, good) in the background. This is ideal for sales targets, budget utilization, and OKR tracking. The Stephen Few-inspired design packs a large amount of information into a compact horizontal bar.

Assumption: You have measures [Actual] and [Target], both returning values in the same unit. The chart scales to the maximum of the two.

Bullet Chart =
VAR _Actual = [Actual]
VAR _Target = [Target]
VAR _MaxVal = MAX( _Actual, _Target ) * 1.2  -- 20% headroom
-- Qualitative ranges as percentages of max
VAR _Poor = 0.5       -- 0-50% = poor (dark gray)
VAR _Satisfactory = 0.75  -- 50-75% = satisfactory (medium gray)
-- SVG dimensions
VAR _W = 200
VAR _H = 24
VAR _BarH = 10
VAR _BarY = ( _H - _BarH ) / 2
-- Calculate positions
VAR _ActualX = DIVIDE( _Actual, _MaxVal ) * _W
VAR _TargetX = DIVIDE( _Target, _MaxVal ) * _W
VAR _PoorX = _Poor * _W
VAR _SatX = _Satisfactory * _W
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 " & _W & " " & _H & "'>" &
        -- Qualitative range: poor (darkest)
        "<rect x='0' y='0' width='" & FORMAT( _PoorX, "0.0" ) & "' height='" & _H & "' fill='%23D1D5DB'/>" &
        -- Qualitative range: satisfactory (medium)
        "<rect x='" & FORMAT( _PoorX, "0.0" ) & "' y='0' width='" & FORMAT( _SatX - _PoorX, "0.0" ) & "' height='" & _H & "' fill='%23E5E7EB'/>" &
        -- Qualitative range: good (lightest)
        "<rect x='" & FORMAT( _SatX, "0.0" ) & "' y='0' width='" & FORMAT( _W - _SatX, "0.0" ) & "' height='" & _H & "' fill='%23F3F4F6'/>" &
        -- Actual value bar
        "<rect x='0' y='" & FORMAT( _BarY, "0.0" ) & "' width='" & FORMAT( _ActualX, "0.0" ) & "' height='" & _BarH & "' fill='%232563EB'/>" &
        -- Target marker line
        "<line x1='" & FORMAT( _TargetX, "0.0" ) & "' y1='2' x2='" & FORMAT( _TargetX, "0.0" ) &
            "' y2='" & FORMAT( _H - 2, "0" ) & "' stroke='%23111827' stroke-width='2.5'/>" &
    "</svg>"
RETURN
    _SVG

Reading the chart: Three background shades represent qualitative ranges (poor, satisfactory, good). The blue bar shows actual performance. The black vertical line marks the target. When the blue bar reaches or passes the black line, the target has been met.

Recipe 4: Progress Bar

A horizontal progress bar showing percentage complete with an embedded label. Use this for project completion tracking, milestone progress, data quality scores, and capacity utilization. The color shifts from red through yellow to green as the value approaches 100%.

Assumption: You have a measure [Completion %] that returns a decimal between 0 and 1 (e.g., 0.73 for 73%).

Progress Bar =
VAR _Pct = MIN( [Completion %], 1 )  -- Cap at 100%
VAR _PctDisplay = FORMAT( _Pct, "0%" )
-- Dynamic color based on completion
VAR _Color =
    SWITCH(
        TRUE(),
        _Pct >= 0.9, "%2322C55E",   -- Green
        _Pct >= 0.7, "%2384CC16",   -- Lime
        _Pct >= 0.5, "%23EAB308",   -- Yellow
        _Pct >= 0.3, "%23F97316",   -- Orange
        "%23EF4444"                   -- Red
    )
-- SVG dimensions
VAR _W = 180
VAR _H = 22
VAR _BarW = _Pct * _W
VAR _Radius = 4
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 " & _W & " " & _H & "'>" &
        -- Background track
        "<rect x='0' y='0' width='" & _W & "' height='" & _H &
            "' rx='" & _Radius & "' fill='%23E5E7EB'/>" &
        -- Filled portion
        "<rect x='0' y='0' width='" & FORMAT( MAX( _BarW, _Radius * 2 ), "0.0" ) &
            "' height='" & _H & "' rx='" & _Radius & "' fill='" & _Color & "'/>" &
        -- Percentage label
        "<text x='" & FORMAT( _W / 2, "0" ) & "' y='" & FORMAT( _H / 2 + 1, "0" ) &
            "' text-anchor='middle' dominant-baseline='middle'" &
            " font-family='Segoe UI,sans-serif' font-size='11' font-weight='600'" &
            " fill='" & IF( _Pct > 0.45, "white", "%23374151" ) & "'>" &
            _PctDisplay &
        "</text>" &
    "</svg>"
RETURN
    _SVG

Customization: Adjust the _Radius variable for rounded corners (set to 0 for sharp edges). The label color automatically flips from dark to white when the bar is more than 45% filled, ensuring readability. Change _W and _H to resize the bar.

Recipe 5: Star Rating

Displays a 1–5 star rating using SVG polygon shapes. Each star is either filled (gold) or empty (gray outline), based on the rating value. Use this for customer satisfaction scores, product ratings, vendor assessments, and employee performance tiers.

Assumption: You have a measure [Rating] that returns an integer between 1 and 5.

Star Rating =
VAR _Rating = ROUND( [Rating], 0 )
-- Star polygon points (centered in 20x20 viewbox)
VAR _StarPath = "10,1 12.9,7.4 20,7.8 14.7,12.6 16.2,19.5 10,16 3.8,19.5 5.3,12.6 0,7.8 7.1,7.4"
-- Build 5 stars side by side
VAR _Star1Fill = IF( _Rating >= 1, "%23F59E0B", "none" )
VAR _Star1Stroke = IF( _Rating >= 1, "%23F59E0B", "%23D1D5DB" )
VAR _Star2Fill = IF( _Rating >= 2, "%23F59E0B", "none" )
VAR _Star2Stroke = IF( _Rating >= 2, "%23F59E0B", "%23D1D5DB" )
VAR _Star3Fill = IF( _Rating >= 3, "%23F59E0B", "none" )
VAR _Star3Stroke = IF( _Rating >= 3, "%23F59E0B", "%23D1D5DB" )
VAR _Star4Fill = IF( _Rating >= 4, "%23F59E0B", "none" )
VAR _Star4Stroke = IF( _Rating >= 4, "%23F59E0B", "%23D1D5DB" )
VAR _Star5Fill = IF( _Rating >= 5, "%23F59E0B", "none" )
VAR _Star5Stroke = IF( _Rating >= 5, "%23F59E0B", "%23D1D5DB" )
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 24'>" &
        -- Star 1
        "<polygon points='" & _StarPath & "' fill='" & _Star1Fill &
            "' stroke='" & _Star1Stroke & "' stroke-width='1' transform='translate(2,2) scale(0.95)'/>" &
        -- Star 2
        "<polygon points='" & _StarPath & "' fill='" & _Star2Fill &
            "' stroke='" & _Star2Stroke & "' stroke-width='1' transform='translate(24,2) scale(0.95)'/>" &
        -- Star 3
        "<polygon points='" & _StarPath & "' fill='" & _Star3Fill &
            "' stroke='" & _Star3Stroke & "' stroke-width='1' transform='translate(46,2) scale(0.95)'/>" &
        -- Star 4
        "<polygon points='" & _StarPath & "' fill='" & _Star4Fill &
            "' stroke='" & _Star4Stroke & "' stroke-width='1' transform='translate(68,2) scale(0.95)'/>" &
        -- Star 5
        "<polygon points='" & _StarPath & "' fill='" & _Star5Fill &
            "' stroke='" & _Star5Stroke & "' stroke-width='1' transform='translate(90,2) scale(0.95)'/>" &
    "</svg>"
RETURN
    _SVG

How it works: Each star is a <polygon> element with 10 points defining a five-pointed star shape. The transform attribute positions each star horizontally. Filled stars get a gold fill (#F59E0B), and empty stars get a transparent fill with a gray outline. To support half-star ratings, you would need to use SVG <defs> and <clipPath> elements, which is more complex but follows the same pattern.

Building an Executive KPI Dashboard

The real power emerges when you combine the new card visual with SVG indicators in a cohesive dashboard layout. Here is a production dashboard architecture we use at EPC Group for executive reporting engagements. This layout delivers at-a-glance insight with drill-down depth.

Dashboard Layout Architecture

Row 1: KPI Cards (New Card Visual × 4)

Four cards across the top, each using the new card visual with sparklines enabled:

  • Revenue Card: Callout value = Total Revenue, reference label = YoY variance %, sparkline = monthly revenue trend (12 months). Conditional formatting: green callout if positive YoY, red if negative.
  • Margin Card: Callout value = Gross Margin %, reference label = vs. target, sparkline = quarterly margin trend. Use a DAX trend arrow measure (up/down triangle SVG) as a sub-value.
  • Customer Satisfaction Card: Callout value = CSAT score, sub-value = star rating SVG measure rendered inline. Reference label = sample size (e.g., "Based on 2,340 responses").
  • Pipeline Card: Callout value = Pipeline $, reference label = conversion rate %, sub-value = progress bar SVG showing pipeline coverage ratio against annual target.

Row 2: Performance Matrix with SVG Indicators

A matrix visual showing business units or product lines as rows, with these column values:

  • Column 1: Business Unit name (text)
  • Column 2: Revenue (formatted currency)
  • Column 3: Sparkline SVG (12-month revenue trend per unit)
  • Column 4: Traffic Light SVG (performance status vs. target)
  • Column 5: Progress Bar SVG (% of annual target achieved)
  • Column 6: YoY Growth % (formatted number with conditional font color)

Row 3: Trend Charts and Detail Tables

Full-width line chart showing revenue over time (standard Power BI line chart, not SVG) alongside a detail table with drill-through navigation. SVG indicators in the detail table provide quick status assessment before users drill through for granular data. This combination of standard visuals with SVG-enhanced tables delivers the best balance of interactivity and information density.

// Example: Trend Arrow SVG for the Margin Card sub-value

Trend Arrow =
VAR _Current = [Gross Margin %]
VAR _Prior = [Prior Period Margin %]
VAR _Direction = IF( _Current >= _Prior, "up", "down" )
VAR _Color = IF( _Direction = "up", "%2322C55E", "%23EF4444" )
VAR _ArrowUp = "M10,2 L18,14 L13,14 L13,22 L7,22 L7,14 L2,14 Z"
VAR _ArrowDown = "M10,22 L18,10 L13,10 L13,2 L7,2 L7,10 L2,10 Z"
VAR _Path = IF( _Direction = "up", _ArrowUp, _ArrowDown )
VAR _Change = FORMAT( ABS( _Current - _Prior ), "0.0%" )
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 24'>" &
        "<path d='" & _Path & "' fill='" & _Color & "' transform='scale(0.8) translate(0,1)'/>" &
        "<text x='24' y='17' font-family='Segoe UI,sans-serif' font-size='12' font-weight='600' fill='" & _Color & "'>" &
            IF( _Direction = "up", "+" , "-" ) & _Change &
        "</text>" &
    "</svg>"
RETURN
    _SVG

Conditional Formatting Beyond Basics

SVG visuals are one piece of the KPI presentation toolkit. Power BI's built-in conditional formatting capabilities complement SVG measures and should be used together for the most effective dashboards. Here are the advanced techniques most teams overlook.

Background Color by Rules

Right-click any measure in a table or matrix, select Conditional formatting > Background color, then choose "Rules" format style. Define threshold-based rules: if value is between 0 and 50, set background to light red (#FEE2E2); 50–80 set to light yellow (#FEF3C7); 80–100 set to light green (#DCFCE7). This works on any measure column, including adjacent to SVG columns, creating a layered visual encoding.

Font Color by Field Value

Create a DAX measure that returns a hex color string based on your business logic. Then in conditional formatting for font color, select "Field value" and point it to your color measure. This gives you DAX-driven control over text colors without SVG complexity. Common use: coloring YoY growth values green/red based on positive/negative variance.

YoY Color =
VAR _Growth = [YoY Growth %]
RETURN
    SWITCH(
        TRUE(),
        _Growth >= 0.10, "#15803D",   -- Dark green: 10%+ growth
        _Growth >= 0,    "#22C55E",   -- Green: positive growth
        _Growth >= -0.10, "#EF4444", -- Red: negative growth
        "#991B1B"                     -- Dark red: 10%+ decline
    )

Icon Sets in Tables

Power BI includes a built-in icon set feature under conditional formatting. Right-click a column, select Conditional formatting > Icons, and configure rules to display arrows, flags, circles, or other icons. While less flexible than SVG measures, icon sets are simpler to configure and perform better on high-row-count tables because the icons are rendered natively rather than through DAX string computation.

Data Bars in Tables

For simple inline bars, Power BI's built-in data bars (Conditional formatting > Data bars) may be sufficient. They render a colored bar behind the number in each cell, similar to Excel's data bars. The advantage over SVG progress bars is native performance and zero DAX overhead. The disadvantage is limited styling — you cannot add labels, rounded corners, or multi-segment bars. Use data bars for quick implementations and SVG progress bars for polished executive reports.

Web URL Conditional Navigation

Set a column's data category to "Web URL" and the values become clickable links. Combine this with a DAX measure that constructs URLs dynamically based on filter context — for example, linking each row to a detail page in a web application, a SharePoint document library filtered by project, or a drill-through report URL. This creates an interactive navigation experience that extends beyond Power BI's native drill-through.

Performance Considerations

SVG measures are evaluated by the DAX formula engine for every row in the visual. The formula engine is single-threaded and handles string operations, which makes it the slower of Power BI's two engines (the storage engine is multi-threaded and handles data retrieval). Understanding this architecture is critical for building performant dashboards.

Row CountSimple SVG (Traffic Light)Complex SVG (Sparkline)Recommendation
< 100 rowsInstantFast (<1s)No concerns
100–500 rowsFastModerate (1–3s)Acceptable for most scenarios
500–2,000 rowsModerateSlow (3–8s)Optimize or paginate
2,000+ rowsNoticeableUnacceptableAggregate data, limit visible rows

Optimization Strategies

1

Use SWITCH Instead of Nested IF Chains

SWITCH(TRUE(), ...) is both more readable and slightly more efficient than deeply nested IF statements. The DAX engine evaluates SWITCH conditions sequentially and stops at the first match, similar to IF, but with less parsing overhead for complex branching.

2

Pre-compute Values in VAR Statements

Define all calculated values as VARs at the top of the measure. VAR values are computed once and reused. Without VARs, the same sub-expression may be recalculated every time it appears in the string concatenation, multiplying computation time.

3

Minimize SVG Complexity Per Cell

Each SVG element (circle, rect, path, text) adds to the string length and rendering time. A traffic light with one circle is fast. A sparkline with 12 path segments and a circle is slower. Avoid adding decorative elements (borders, shadows, gradients) unless they serve a clear purpose.

4

Aggregate Before Visualizing

If your table shows 5,000 product SKUs with sparklines, consider aggregating to product categories (maybe 50 rows) instead. Use hierarchies or drill-through to allow users to access detail. The visual impact of sparklines in a 5,000-row table is minimal anyway — users cannot meaningfully compare that many small charts.

5

Use Top N Filtering

Apply a Top N filter on the visual to show only the top 20 or 50 items. This caps the number of SVG measures that need to be evaluated while focusing attention on the most important items. Combine with a "Show All" bookmark for users who need the complete list.

Enterprise KPI Design Patterns

Individual SVG measures are building blocks. Enterprise deployments need standardization, reusability, and governance. Here are the patterns we implement at EPC Group for organizations rolling out KPI dashboards across multiple departments.

Standardize KPI Colors with Theme JSON

Define your organization's KPI colors in a Power BI theme file. This ensures every report uses the same green, yellow, and red across the entire tenant. Create a theme JSON file and distribute it through your organization's visual theme gallery:

{
  "name": "Enterprise KPI Theme",
  "dataColors": [
    "#2563EB", "#7C3AED", "#059669", "#D97706",
    "#DC2626", "#0891B2", "#4F46E5", "#15803D"
  ],
  "tableAccent": "#2563EB",
  "good": "#22C55E",
  "neutral": "#EAB308",
  "bad": "#EF4444",
  "maximum": "#15803D",
  "center": "#EAB308",
  "minimum": "#991B1B"
}

Reference these same hex codes in your SVG DAX measures. When the branding team changes the "good" color from #22C55E to a different shade, you update the SVG measures to match. For large deployments, consider creating a parameter table that stores color codes so they can be changed in one place.

Create Reusable SVG Measure Templates

Build a library of SVG measure templates in a dedicated "Measures" table. Each template follows a consistent naming convention:

-- Naming convention: _SVG.[Type].[Context]
-- Examples:
_SVG.TrafficLight.SalesPerformance
_SVG.Sparkline.RevenueByMonth
_SVG.ProgressBar.TargetCompletion
_SVG.BulletChart.BudgetVsActual
_SVG.StarRating.CustomerSatisfaction

-- Generic template measure that accepts parameters:
_SVG.TrafficLight.Generic =
VAR _Value = SELECTEDMEASURE()
VAR _GreenThreshold = 0.9
VAR _YellowThreshold = 0.7
-- ... (rest of the traffic light logic)

Document Thresholds in a Configuration Table

Rather than hard-coding threshold values in DAX measures, store them in a configuration table in your data model. This allows business users to adjust thresholds without modifying DAX code:

-- Configuration table: KPI_Thresholds
-- | KPI_Name           | Green_Min | Yellow_Min | Red_Max |
-- | Sales Performance  | 90        | 70         | 69      |
-- | Budget Utilization | 85        | 60         | 59      |
-- | SLA Compliance     | 95        | 80         | 79      |

Traffic Light (Config-Driven) =
VAR _Score = [Score %]
VAR _KPIName = SELECTEDVALUE( KPI_Thresholds[KPI_Name] )
VAR _GreenMin =
    LOOKUPVALUE(
        KPI_Thresholds[Green_Min],
        KPI_Thresholds[KPI_Name], _KPIName
    )
VAR _YellowMin =
    LOOKUPVALUE(
        KPI_Thresholds[Yellow_Min],
        KPI_Thresholds[KPI_Name], _KPIName
    )
VAR _Color =
    SWITCH(
        TRUE(),
        _Score >= _GreenMin, "%2322C55E",
        _Score >= _YellowMin, "%23EAB308",
        "%23EF4444"
    )
VAR _SVG =
    "data:image/svg+xml;utf8," &
    "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" &
        "<circle cx='10' cy='10' r='9' fill='" & _Color & "'/>" &
    "</svg>"
RETURN
    _SVG

Dynamic Thresholds with What-If Parameters

Power BI's What-If parameter feature lets users adjust thresholds interactively using a slicer. Create a What-If parameter for "Green Threshold" (range 50–100, increment 5), and another for "Yellow Threshold". Reference these parameter values in your SVG measures. Users can then drag the slicer to see how different threshold settings would reclassify their KPIs — a powerful tool for threshold calibration workshops with business stakeholders. This approach eliminates the back-and-forth of "can you change the threshold to 85 and let me see how it looks?" scenarios.

Frequently Asked Questions

What are SVG visuals in Power BI?
SVG visuals in Power BI are inline graphics rendered directly inside table and matrix visuals using DAX measures that return SVG XML strings. Instead of importing a custom visual from AppSource, you write a DAX measure that outputs a data URI containing SVG markup such as "data:image/svg+xml;utf8,<svg>...</svg>". Power BI renders this as an image when the column is set to Image URL in the formatting pane. This technique gives you full control over the visual appearance using standard SVG elements like circles, rectangles, paths, and text, all driven dynamically by your data model.
How do I add an SVG image to a Power BI table?
To add an SVG image to a Power BI table, follow these steps. First, create a DAX measure that returns a string starting with "data:image/svg+xml;utf8," followed by valid SVG markup. Second, add a table or matrix visual to your report canvas. Third, place the SVG measure in the Values well of the table. Fourth, select the measure field in the table, go to the Column tools tab (or Format pane), and set the Data Category to "Image URL". Power BI will then render the SVG inline in each row of the table. The SVG scales to the row height, and you can control the width by setting the viewBox attribute in your SVG markup.
Can I create sparklines in Power BI without custom visuals?
Yes, you can create sparklines in Power BI without any custom visuals using two approaches. The first approach uses DAX SVG measures that generate an SVG path element from your data points, rendering a mini line chart inside a table cell. This requires iterating over a date table with CONCATENATEX to build the path coordinates. The second approach, available since the 2024 updates, uses the built-in sparkline feature in the new card visual, which lets you add a sparkline trend line directly to a card without writing any DAX. For table-based sparklines, the SVG DAX approach remains the only native option without AppSource visuals.
What is the new card visual in Power BI?
The new card visual in Power BI, introduced in 2024 as a replacement for the legacy card and multi-row card, is a redesigned visual with significantly more functionality. It supports multiple values (callout values) on a single card, reference labels that show comparison text like "vs. last month", built-in sparkline trend lines, conditional formatting on all elements, sub-values for additional context, target and actual value comparisons, and customizable layouts. You can configure each element independently with different fonts, colors, and formatting rules. It is the recommended visual for KPI display and is the default card type in new Power BI reports.
Do SVG visuals work in Power BI Service?
Yes, SVG visuals rendered through DAX measures work in Power BI Service, including published reports, dashboards, apps, and embedded scenarios. They also render correctly in Power BI email subscriptions and PDF exports, which is a significant advantage over many AppSource custom visuals that may not render in these contexts. The SVG is treated as an inline image, so it follows the same rendering pipeline as any other image URL in Power BI. The only limitation is that SVG visuals in Power BI do not support interactivity such as tooltips or click events because they are static rendered images.
How do I create a traffic light indicator in Power BI?
To create a traffic light indicator in Power BI, write a DAX measure that evaluates a KPI value against thresholds and returns an SVG circle with the appropriate fill color. For example, use SWITCH(TRUE(), [KPI Value] >= 90, "#22C55E", [KPI Value] >= 70, "#EAB308", "#EF4444") to determine the color, then wrap it in an SVG string: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><circle cx='10' cy='10' r='9' fill='" & [Color] & "'/></svg>". Set the Data Category of the measure column to Image URL, and each row displays a colored circle based on the KPI threshold.
Do SVG measures affect report performance?
SVG measures can affect report performance because the DAX engine must evaluate the measure for every row in the visual, and string concatenation is handled by the formula engine rather than the faster storage engine. For tables with fewer than 500 rows, performance impact is negligible. For tables with 1,000 to 5,000 rows, you may notice a slight delay during rendering. Beyond 5,000 rows, complex SVG measures with multiple CONCATENATEX iterations (like sparklines) can cause noticeable slowdowns. To optimize, pre-compute values in VAR statements, use SWITCH instead of nested IF chains, minimize the number of SVG elements per cell, and consider aggregating data to reduce row counts before applying SVG measures.
Can I use SVG visuals in paginated reports?
SVG visuals created through DAX measures do not render in Power BI paginated reports (SSRS-based .rdl files) because paginated reports use a different rendering engine that does not interpret data URI SVG strings as images. For paginated reports, you need to use the built-in indicator and gauge features in Report Builder, or embed images from external URLs. However, if your paginated report connects to a Power BI dataset, you can still use the underlying DAX measures for calculations and apply the paginated report native formatting tools for visual indicators like traffic lights and gauges.

Continue Learning

Explore more Power BI resources from our consulting team:

Need Help Building KPI Dashboards?

Our Power BI consultants have built executive KPI dashboards with SVG visuals for Fortune 500 companies across healthcare, finance, and government. Get a free consultation to discuss your dashboard project.

Ready to Transform Your Data Strategy?

Get a free consultation to discuss how Power BI and Microsoft Fabric can drive insights and growth for your organization.