You've got a small SVG icon — a chevron, a checkmark, a background pattern — and you don't
want to burn a separate HTTP request just to load it. So you try pasting the raw markup
into a CSS url() and it breaks immediately: the icon doesn't show, or the
whole CSS rule gets silently ignored. Quotes conflict, a stray # cuts the
string off early, and suddenly a five-minute task turns into twenty minutes of trial and
error.
The fix isn't complicated once you know what's actually happening. A data URI just needs
specific characters encoded — not the whole string — and SVG being plain text (not
binary) means you almost never need base64 for it. Get the encoding right once, and you
can drop any small SVG straight into background-image, mask-image,
or list-style-image with zero extra requests.
To convert an SVG into a data URI for CSS, minify the markup, swap its double quotes for
single quotes, and URL-encode only the characters that break CSS parsing — mainly
#, ", and <>. Skip base64: since SVG is
plain text, URL-encoding produces a smaller, human-readable string that drops straight
into background-image: url().
What is a data URI, and why use it for SVG?
A data URI embeds a file's actual content inside the URL itself, instead of pointing to a separate file. The format looks like this:
data:[<mediatype>][;base64],<data>
For SVG, that means the icon's XML markup lives directly inside a CSS
url() value — the browser parses it in place, with no round trip to a
server. This matters most for small, one-off icons: chevrons, checkmarks, dividers, and
simple background patterns that would otherwise cost a full HTTP request each.
- Media type. For SVG this is
image/svg+xml, telling the browser exactly how to interpret the data that follows. - Encoding flag. Either omitted (meaning URL-encoded/plain text) or set to
;base64, which tells the browser to decode the data as base64 first. - The data itself. The SVG markup, either percent-encoded or base64-encoded, sitting after the comma.
The key insight: because SVG is already text, not a binary format like a JPEG, it doesn't need base64 to be safely embedded. It just needs the handful of characters that conflict with URL or CSS syntax swapped out — everything else can stay as plain, readable text.
Why it matters
Inlining an SVG as a data URI isn't just a neat trick — it changes how the browser loads and renders your styles in a few concrete ways:
- Fewer HTTP requests. Each external icon file is a separate network round trip. For pages with dozens of small icons, inlining them removes that overhead entirely.
- No missing-file risk. A data URI can't 404 — the icon travels with the CSS rule that uses it, so there's nothing separate to deploy, path, or cache-bust incorrectly.
- Works anywhere CSS accepts a URL. The same encoded string drops into
background-image,mask-image,border-image, orlist-style-imagewithout any markup changes to the HTML. - Colocated with the style that uses it. A hover-state icon swap, for instance, can live entirely inside a single CSS rule instead of referencing a second image file.
Step-by-step: convert SVG to a data URI
- Minify the SVG first. Strip comments, editor metadata, and unnecessary decimal precision before encoding anything — design tools often export SVGs two or three times larger than they need to be.
-
Replace double quotes with single quotes. Change every
"inside the SVG's attributes to', since you'll wrap the whole data URI in double quotes inside your CSSurl(). -
Encode the characters that break URLs or CSS. At minimum, encode
#→%23,<→%3C,>→%3E, and%→%25. Everything else can generally stay as plain text. -
Drop the XML declaration and namespace clutter. Remove any
<?xml ...?>prolog — it isn't needed inside a data URI and just adds extra characters to encode. -
Skip the base64 flag. Use the plain form,
data:image/svg+xml,, rather thandata:image/svg+xml;base64,— no;base64means the browser reads your encoded text directly. -
Paste it into the CSS declaration. Wrap the finished string in double quotes inside
url(), as shown in the example below. -
Test it in the browser at real size. Some encoders miss an edge-case character in complex paths or gradients — always render the result before shipping it, especially for icons with
#in fill or stroke colors.
/* Raw SVG */
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="#333" d="M5 12h14M12 5l7 7-7 7"/>
</svg>
/* As a CSS-ready data URI */
.icon-arrow {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23333' d='M5 12h14M12 5l7 7-7 7'/%3E%3C/svg%3E");
}
Common mistakes that break the icon
1. Leaving the # character unencoded
In a URL, # marks the start of a fragment identifier. An unencoded
#333 fill color or an #-prefixed id reference will
cause the browser to treat everything after it as a fragment, quietly breaking the icon.
Always encode it as %23.
2. Mixing double quotes inside and outside the URI
If your SVG's attributes use double quotes and you also wrap the data URI in double
quotes in CSS, the first internal quote closes the url() value early. Switch
the SVG's attribute quotes to single quotes before encoding — it's the single most common
cause of a "correctly encoded" icon that still won't render.
3. Reaching for base64 by default
Base64 is necessary for binary formats like JPEG or PNG, but SVG is text. Base64-encoding it adds roughly a third more characters for no benefit and makes the resulting CSS completely unreadable and undiffable in version control.
4. Skipping minification before encoding
Encoding a bloated, unminified SVG just carries that bloat into your CSS file. Comments, editor-generated metadata, and excessive path precision should be stripped before encoding, not after — there's nothing to gain by inlining wasted bytes.
Real-world encoding examples
These are representative results from minifying and URL-encoding common icon types, compared to their original exported SVG size:
The pattern holds across icon types: minifying before encoding does most of the size work,
URL-encoding beats base64 for anything text-based, and the vast majority of "it just won't
show up" cases trace back to an unencoded # or a quote conflict.
Comparison: URL-encoding vs base64
Both methods work in every modern browser, but they trade off differently depending on what you need from the result:
| Method | Size overhead | Readability | Effort | Best for |
|---|---|---|---|---|
| URL-encoding (percent-encoding) | 10–20% | Human-readable, diffable | Low | Nearly every SVG icon in CSS |
| Base64 encoding | ~33% | Unreadable | Low | Build-tool-generated output, complex SVGs with heavy quoting |
| External SVG file | 0% | Fully readable | Low | Icons reused across many pages, cache-friendly assets |
| Inline <svg> in HTML | 0% | Fully readable | Low | Icons that need CSS styling of individual paths (not usable in url()) |
Free tool: SVG to Data URI Converter
The Rebrixe SVG to Data URI Converter runs entirely in your browser. Paste in your SVG
markup, and it minifies, escapes quotes, and URL-encodes the result into a ready-to-paste
url() value — with a live preview so you can confirm it renders before you
copy it into your stylesheet. No account, no upload, no watermark.
Turn your SVG into CSS-ready code in seconds
Paste your markup, get a properly encoded data URI, and preview it live before you copy it.
Open the SVG to Data URI Converter →Frequently asked questions
data:[mediatype][;base64],<data>. For SVG, this means the icon's
markup lives directly inside a CSS url() value, so the browser never has
to make a separate HTTP request to fetch it.
# marks the start of a fragment identifier. If your SVG
contains an unencoded # (common in fill colors like #333 or
in id references), the browser treats everything after it as a fragment and the icon
fails to render. Encoding it as %23 fixes this.
url() value early and the browser reads the remaining SVG as
invalid CSS.
background-image,
mask-image, border-image, and list-style-image
— anywhere CSS accepts a url() value.