Sitemap

Power BI Custom Visuals with SVG Code: Legend

3 min readApr 8, 2025

--

Use Cases:
When I create visualization using Power BI, I often come across situations where there is insufficient space to include color legends for every chart, or default color legend is not wide enough to display entire legend. One day, when I learnt about SVG code for custom visuals, I decided to create my own custom legends that is responsive to the column contents. After several try and error with help from SuperGrok, I have come up with the following code that requires minimal adjustment.

Preparation:
(1) Before we get started, you will need the following visuals from AppSource:
HTML Content

Press enter or click to view image in full size

(2) You will also need a separate query to store your color code. The Color query will contain one column for Color Code and one column for Dimensions

In the following example, I have created the Color query in DAX:

Color = DATATABLE(
“Dimensions”, STRING,
“Color Code”, STRING,
{
{“AAA”, “#ABABAB”},
{“BBB”, “#9185BE”},
{“CCC”, “#05CD99”},
{“DDD”, “#90CDB2”},
{“EEE”, “#3491FA”},
{“FFF”, “#FFEF18”}
}
)

(3) In Power BI, create new Measure, then paste the following code.

Legend =
— ADJUSTABLE —
VAR LegendField = DISTINCT(SELECTCOLUMNS(‘Table’, “LegendValue”, ‘Table’[ColumnName]))
VAR circle_width = 10
VAR default_color = “#008080”

— Every code below this portion is NON ADJUSTABLE —
VAR CategoriesWithIndex = ADDCOLUMNS(LegendField, “Index”, RANKX(LegendField, [LegendValue], , ASC, Dense))

VAR gap_between_circle_and_text = (circle_width/2)+1
VAR text_char_width = (circle_width/2)-0.2
VAR inter_set_padding = circle_width/2
VAR CategoriesWithWidth = ADDCOLUMNS(CategoriesWithIndex, “text_width”, LEN([LegendValue]) * text_char_width,
“set_width”, circle_width + gap_between_circle_and_text + (LEN([LegendValue]) * text_char_width) + inter_set_padding)

Get Eric Chan’s stories in your inbox

Join Medium for free to get updates from this writer.

VAR CategoriesWithPosition = ADDCOLUMNS(
CategoriesWithWidth, “starting_x”, SUMX( FILTER(CategoriesWithWidth, [Index] < EARLIER([Index])), [set_width]))

VAR SVGWidth = SUMX(CategoriesWithWidth, [set_width])

VAR SVGStart = “<svg width=’” & SVGWidth & “‘ height=’”&circle_width*2&”’ viewBox=’0 0 “ & SVGWidth & “ 20' xmlns=’http://www.w3.org/2000/svg'>"

VAR SVGCircles = CONCATENATEX(CategoriesWithPosition, “<circle cx=’” & ([starting_x] + 9) & “‘ cy=’”&circle_width&”’ r=’”&circle_width/2&”’ fill=’” & CALCULATE(SELECTEDVALUE(‘Color’[Color Code], default_color), TREATAS({[LegendValue]}, ‘Color’[Dimensions])) & “‘/>”, “”, [Index], ASC)

VAR SVGTexts = CONCATENATEX(CategoriesWithPosition, “<text x=’” & ([starting_x] + circle_width + gap_between_circle_and_text) & “‘y=’”&circle_width+3&”’ font-size=’”&circle_width&”’>” & [LegendValue] & “</text>”, “”, [Index], ASC)

VAR SVGEnd = “</svg>”

RETURN

SVGStart & SVGCircles & SVGTexts & SVGEnd

(4) Place the Legend measure to Values

Press enter or click to view image in full size

Result

Press enter or click to view image in full size

--

--

Eric Chan
Eric Chan

Written by Eric Chan

Analytical and detail-oriented storyteller with over 10 years of experience in data analytics.