DAX SVG measure · Progress to target
Progress ring (radial progress) with a DAX SVG measure
By Nawaf Sharaf · Founder & Reliability Engineer · Updated
Use it when
- You need a compact, square progress indicator next to a card value.
- The design language of the report uses rings or donuts already.
Skip it when
Readers compare many rows: arcs are harder to compare than bar lengths. In a long table, use the progress bar.
Three looks
The progress ring in three palettes; click one to edit it.
Data it needs
The builder asks for exactly these fields; type your own measure and column names there.
- Ratio measure
- 0 to 1. The ring is full from 1 upwards.
The full DAX
Swap [% Complete] for your own names.
Progress Ring =
-- VizForge SVG measure: progress ring.
-- Set Measure tools > Data category = Image URL, then size it under Format > Visual > Image size.
-- In the Card visual: Format > Visual > Image > Image source: Select from data.
-- Made with the free VizForge SVG measure builder: vizforge.ai/tools/svg-measure-builder
-- Need per-point tooltips, click-to-select inside the chart or Format-pane settings? Generate it as a real custom visual: vizforge.ai/visuals
VAR _Ratio = [% Complete]
VAR _Fill = MAX ( 0, MIN ( 1, _Ratio ) )
RETURN
IF (
ISBLANK ( _Ratio ),
BLANK (),
"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 48 48'><circle cx='24' cy='24' r='21.5' fill='none' stroke='%23E6E6E6' stroke-width='5'/><circle cx='24' cy='24' r='21.5' fill='none' stroke='%23118DFF' stroke-width='5' stroke-linecap='round' transform='rotate(-90 24 24)' stroke-dasharray='"
& FORMAT ( _Fill * 135.09, "0.0", "en-US" )
& " 135.09'/><text x='24' y='24' dy='0.35em' text-anchor='middle' font-family='Segoe UI, Helvetica, Arial, sans-serif' font-size='12' font-weight='600' fill='%23252423'>"
& SUBSTITUTE ( FORMAT ( _Ratio, "0%", "en-US" ), "%", "%25" )
& "</text></svg>"
)DAX user-defined function variant (VizForge.SvgProgressRing)
One-line call: Progress Ring = VizForge.SvgProgressRing ( [% Complete] )
-- 1) Paste into DAX query view and choose 'Update model with changes'.
-- Needs DAX user-defined functions (generally available since the June 2026 Power BI release).
DEFINE
/// Progress ring as an SVG image URL (VizForge). Optional parameters default to the colours you picked.
FUNCTION VizForge.SvgProgressRing = (
pValue : NUMERIC,
pTrackColor : STRING = "#E6E6E6",
pFillColor : STRING = "#118DFF",
pLabelColor : STRING = "#252423"
) =>
VAR _Ratio = pValue
VAR _Fill = MAX ( 0, MIN ( 1, _Ratio ) )
RETURN
IF (
ISBLANK ( _Ratio ),
BLANK (),
"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 48 48'><circle cx='24' cy='24' r='21.5' fill='none' stroke='"
& SUBSTITUTE ( pTrackColor, "#", "%23" )
& "' stroke-width='5'/><circle cx='24' cy='24' r='21.5' fill='none' stroke='"
& SUBSTITUTE ( pFillColor, "#", "%23" )
& "' stroke-width='5' stroke-linecap='round' transform='rotate(-90 24 24)' stroke-dasharray='"
& FORMAT ( _Fill * 135.09, "0.0", "en-US" )
& " 135.09'/><text x='24' y='24' dy='0.35em' text-anchor='middle' font-family='Segoe UI, Helvetica, Arial, sans-serif' font-size='12' font-weight='600' fill='"
& SUBSTITUTE ( pLabelColor, "#", "%23" )
& "'>"
& SUBSTITUTE ( FORMAT ( _Ratio, "0%", "en-US" ), "%", "%25" )
& "</text></svg>"
)
EVALUATE { VizForge.SvgProgressRing ( [% Complete] ) }
-- 2) Then create the measure and set Measure tools > Data category = Image URL:
-- Progress Ring = VizForge.SvgProgressRing ( [% Complete] )
-- Colours and thresholds are optional parameters: pass a value after the fields to override one.
-- Pass colours as plain "#RRGGBB"; the function encodes the # for the data URI.Placing it in a report
Square slot: 48 × 48. It shines as a Card visual image (Image source: Select from data) or in a matrix of teams; in a long table the rings get too small to read the centre label. Remember the Image URL data category. All setup steps.
Gotchas
- Below 28 px the centre label is dropped automatically: there is no room for legible text.
- Thickness is capped at a third of the size so the hole never closes.
- In the new Card visual, choose Image fit so the ring isn't stretched to the card's aspect ratio.
- Length. 529 characters whatever the data; no iteration.
- Context. The centre label shows the ratio for the current filter context. On a total row that is the ratio of totals only if [% Complete] is written as DIVIDE ( SUM done, SUM total ); an AVERAGEX of row ratios gives a different, usually misleading, number.
- Tooltip. Hovering shows the SVG text; a blank report-page tooltip hides it.
When an SVG measure isn't enough
An interactive radial visual can animate on load, show the underlying counts on hover and break down by category. See the Gauge custom visual for what an interactive version can do.
KPI card designs using it
Finished designs built on this pattern, from the KPI card gallery.