Inline SVG Icons in Power Apps

Crisp, recolorable icons with zero PCF controls and zero uploads, just a data URI and EncodeUrl.

Last verified July 2026 against current Power Fx.

You don't need a PCF control or a media upload to use custom icons in Power Apps. Any Image control can render an SVG directly from a data URI, which means your icons are vector-crisp at any size, live in the app definition (no external files), and can be recolored with a formula.

The basic pattern

Set an Image control's Image property to a data URI. EncodeUrl() escapes the markup so the angle brackets and quotes survive as a URL:

Static inline SVG (no PCF, no upload)
"data:image/svg+xml;utf8," & EncodeUrl( "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='#2563EB' stroke-width='2'><path d='M5 12h14M12 5v14'/></svg>")

Use single quotes inside the SVG so you don't have to escape the double quotes of the Power Fx string. Set the SVG's viewBox and let the Image control's Width/Height handle sizing, the vector scales cleanly.

Dynamic color, the placeholder pattern

The intuitive move is to concatenate a hex variable straight into the string. It usually works, but the Image control can cache the resulting data URI and skip re-rendering when only the color changes, so your icon appears "stuck" on the old color. The robust fix is to keep one base string with a placeholder token and inject the color with Substitute():

Recolorable SVG that re-renders reliably
// EncodeUrl(varHex) turns "#2563EB" into "%232563EB" so the # survives the URI.Substitute( "data:image/svg+xml;utf8," & EncodeUrl( "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='__COLOR__'><circle cx='12' cy='12' r='10'/></svg>" ), "__COLOR__", EncodeUrl(varHex))
Why EncodeUrl(varHex)? The token __COLOR__ is alphanumeric, so it passes through EncodeUrl untouched, but the hex you substitute in still contains a #, which is a URL fragment delimiter. Encoding it to %23 keeps the data URI valid.

Driving color from state

Combine the pattern with a Switch to make status icons that recolor themselves:

Status-driven icon color
With( { hex: Switch(ThisItem.Status.Value, "Active", "#16A34A", "Pending", "#D97706", "Closed", "#6B7280", "#111827" )}, Substitute( "data:image/svg+xml;utf8," & EncodeUrl( "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='__C__'><circle cx='12' cy='12' r='10'/></svg>" ), "__C__", EncodeUrl(hex) ))

Two gotchas that waste an afternoon

"Optimize for devices" blanks inline SVGs. That setting renders controls server-side, and inline SVG data URIs don't survive the pipeline, they come through blank in the published app. Turn it off, or host the SVG as a file in SharePoint / a CDN and reference it by URL. See the docs for the full write-up.

Comma-decimal locales. If your app runs in a locale that uses a comma as the decimal separator, comma-separated numbers inside the SVG (like viewBox='0 0 24 24' or path coordinates) can be misread. Separate SVG numbers with spaces (as above), and if you build coordinate strings dynamically, use semicolons where the SVG grammar allows.

Practical tips

  • Keep icons to a single path where you can, smaller strings are faster to parse.
  • Store reusable icons as App.Formulas named formulas (e.g. nfIcons.search) so you write the markup once.
  • stroke='currentColor' does not inherit in a data URI, there's no CSS context. Always set an explicit fill/stroke, or use the placeholder pattern.
  • Grab clean 24×24 single-color paths from an open icon set (Lucide, Feather) and paste the d attribute.

FAQ

How do I show an SVG icon in Power Apps without uploading it?

Set an Image control's Image property to an SVG data URI: "data:image/svg+xml;utf8," & EncodeUrl("<svg ...>...</svg>"). EncodeUrl escapes the markup so it survives as a URL. No PCF control and no media upload required.

Why does my SVG icon not change color when a variable changes?

When you concatenate a hex straight into the SVG string, the Image control can cache the data URI and skip re-rendering. Keep one base string with a placeholder token and use Substitute() to inject the color, and pass the hex through EncodeUrl so the # survives the URL.

Why does my SVG show up blank in the published app?

The most common cause is the "Optimize for devices" setting, which renders controls server-side; inline SVG data URIs don't survive that pipeline and come through blank. Turn it off, or host the SVG as a file (SharePoint/CDN) and reference it by URL.