Skip to content
Back to Blog
Tutorials

Image to Base64 & Data URIs: When to Inline Images (2026)

Should you convert an image to Base64? See when data URIs help, the 33% size cost, CSS/HTML inlining, caching tradeoffs, and when a normal image file wins.

11 min read

When you convert an image to Base64, you get a data URI: a string like data:image/png;base64,iVBORw0KGgo… that you can paste straight into an HTML src or a CSS url(). The browser decodes it on the spot and shows the picture with no separate download. No file to host, no extra request.

So should you do it? Here is the short rule. Inline an image as Base64 when it is small (under about 2 KB), rarely changes, and you want to skip one HTTP request. Think tiny icons and logos. For everything else, keep it as a normal image file: large images, anything reused across pages, anything you want the browser to cache. The catch is that Base64 makes a file about 33% larger, and once that text is embedded in your HTML or CSS it can no longer be cached on its own.

If you want the exact numbers for a specific file, the Image to Base64 converter does the encoding in your browser and shows the precise size increase, so you can decide with real data instead of a rule of thumb. This guide covers what that data URI actually is, the math behind the size tax, a decision matrix for when inlining pays off, and the cases where a plain file wins.

What “image to Base64” actually produces: the data URI

Converting an image to Base64 does not give you a file. It gives you one long string that follows the data URI format defined in RFC 2397 (see MDN’s data: URL reference for the full spec). The string has three parts:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…
└──┬─┘ └───┬───┘ └─┬──┘ └─────────┬──────────┘
data:   MIME type  marker   the encoded image bytes

The MIME type tells the browser what kind of image it is decoding. The common ones for images are image/png, image/jpeg, image/gif, image/webp, image/svg+xml, and image/x-icon for favicons. The ;base64, marker says the payload that follows is Base64 rather than plain text. Everything after the comma is the image, re-expressed as printable ASCII.

That last part matters for privacy. The conversion runs entirely in your browser through the FileReader API’s readAsDataURL, so nothing is uploaded to a server. You can drop a pre-launch screenshot or unreleased artwork into the tool and watch the Network tab stay empty. For the mechanics of how raw bytes become that ASCII string, understanding Base64 covers the encoding from the ground up, and the complete Base64 guide extends the same data-URL idea to fonts, PDFs, and other file types.

A real example: a 68-byte transparent PNG

Here is the smallest practical case, a 1×1 transparent PNG, 68 bytes on disk, written out as a complete data URI:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==

Paste that into a browser address bar and you will see (well, not see, since it is transparent) a valid image render with zero network activity. Notice the trailing ==: that is padding, which we will get to. This is also exactly what text Base64 looks like, just applied to image bytes instead of text. If you only need to encode or decode plain text strings, the Base64 encode/decode tool handles that case.

The 33% size tax (and why it compounds)

Base64 works in fixed groups: every 3 bytes of binary become 4 ASCII characters. Four-thirds is roughly 1.33, which is where the +33% figure comes from. Add a byte or two of padding plus the data:image/png;base64, prefix and the overhead is slightly higher for tiny files. A concrete example: a 9 KB PNG becomes about 12 KB of text.

Why exactly 3-to-4? Base64 uses a 64-character alphabet: AZ, az, 09, plus + and /. Sixty-four symbols is 6 bits of information per character, while binary bytes are 8 bits each. The lowest common multiple of 6 and 8 is 24 bits, which is 3 bytes or 4 Base64 characters, so the encoder works through the image 24 bits at a time. When the image length is not a clean multiple of 3, one or two = characters pad the final group. That math is fixed; no encoder setting shrinks the 33%.

That 33% is the visible cost. The hidden cost is that it compounds, and this is the part most “just inline it” advice skips:

  • The image is re-downloaded whenever the containing file changes. An external logo.png is its own resource. Inline it into styles.css, and now any edit to that stylesheet, even a one-line color tweak, invalidates the cache for the image too. Visitors re-download the picture they already had.
  • It cannot be cached independently. A normal image file is fetched once and reused across every page and every visit. An inlined data URI is part of the document, so it ships again on every page that embeds it and on every cache miss of that document.
  • CSS is render-blocking. The browser will not paint until it has the CSS. Stuff a large data URI into a stylesheet and you have made a render-blocking resource bigger, delaying first paint for the whole page.

Does gzip or brotli cancel the 33% out?

Partly, not fully. Base64 text is repetitive enough that gzip and brotli compress it well, clawing back a good chunk of the inflation over the wire. But two things remain true. First, the compressed Base64 is usually still a little larger than the compressed original binary, because you have handed the compressor a less efficient starting point. Second, and this is the part that bites, compression does nothing about caching or render-blocking. A smaller-over-the-wire data URI is still re-downloaded with its host file and still cannot be cached on its own.

So compressing the bytes is not the same as removing the cost of inlining them. If the distinction between minifying, gzipping, and brotli is fuzzy, the code minification guide lays out how those layers stack, and why squeezing the bytes never fixes the caching problem that inlining creates.

When to use a Base64 image (the decision matrix)

The whole decision comes down to a handful of factors. Here they are side by side:

FactorLean toward inlining (Base64)Lean toward a normal file
SizeUnder ~2 KB (green)Over ~10 KB (red); 2–10 KB is a judgment call (amber)
ReuseOne page, a place or twoRepeated across many pages
Change frequencyAlmost never changesEdited often
ContextHTML email, self-contained widget or bookmarklet, JSON/API payload, a critical above-the-fold icon worth one saved requestContent images, shared cacheable assets

Those size thresholds are not arbitrary. They mirror the traffic-light badge built into the Image to Base64 converter: green under 2 KB, amber up to 10 KB, red above. The tool reads your actual file and tells you which bucket it lands in.

A simple rule of thumb

If you remember one line, make it this: under ~2 KB and used in only one or two places, inlining usually pays off; over ~10 KB or reused across pages, a normal cached file almost always wins. The 2–10 KB middle is where you weigh the saved request against the lost cache for your specific situation.

Good fits in detail

A few cases where Base64 genuinely earns its keep:

  • HTML email. Many email clients block externally hosted images by default for privacy, which breaks any layout that depends on a remote logo. A small inlined data URI renders immediately with no server fetch. Keep these to logos and icons; never inline a photograph into an email.
  • Self-contained widgets and bookmarklets. A bookmarklet or an embeddable widget has to work with zero external dependencies. Inlining its icons keeps everything in a single droppable file.
  • JSON and API payloads. Shipping a thumbnail inside a JSON document or a config file is sometimes the cleanest option: one round trip, one object, no second request to wire up.
  • A critical above-the-fold icon. When a tiny logo is part of your Largest Contentful Paint and you want to shave one request off the critical path, inlining can help. Emphasis on tiny.

One pattern ties these together: in each case the asset travels with something else and would otherwise need its own delivery channel. An email cannot rely on your CDN, a bookmarklet has no second file to fetch, and a JSON response arrives as a single payload. So the alternative to inlining here is not a cached file but a missing image, which changes the calculus entirely. The useful question for a Base64 fit is not only whether the asset is small, but whether a separate file is even an option in the first place.

When NOT to inline: caching, lazy loading, and Core Web Vitals

The flip side is longer, because inlining quietly disables several things the browser does well.

You lose independent caching. This stings most for returning visitors. A normal image sits in their cache after the first visit and loads instantly forever after. An inlined image has no independent cache entry; it rides along with the document every single time, so a repeat visitor pays the byte cost again and again.

You lose lazy loading. The loading="lazy" attribute lets the browser defer images that are below the fold until the user scrolls near them. A data URI is parsed and “downloaded” the instant the HTML is read, so there is nothing to defer. Inline a dozen below-the-fold images and you have forced all of them into the initial load.

You enlarge render-blocking resources. As noted earlier, a data URI inside CSS bloats a resource that blocks first paint. The bigger that stylesheet, the longer the page sits blank.

Decoding is more expensive on mobile. A data URI is Base64-decoded every time its document loads, and on low-end phones that extra CPU work adds up. Worse, the bytes never land in the browser’s disk cache, so a heavy inlined image is re-decoded on each visit instead of being cached and decoded once like a normal file.

There is also a historical reason this advice has shifted. The original case for inlining, made loudly in the HTTP/1.1 era, was request reduction: each connection could fetch one resource at a time, so a page with 40 small icons paid 40 round trips. HTTP/2 changed that by multiplexing many requests over a single connection, which made extra small files cheap. The big payoff of inlining, fewer requests, mostly evaporated, while the costs stayed: lost caching, no lazy loading, bigger render-blocking files. If you read older articles enthusiastic about Base64 sprites, weigh them against the protocol your site actually runs on today.

The Core Web Vitals angle

Inlining cuts both ways on LCP (Largest Contentful Paint). For a small, above-the-fold image that is the LCP element, removing a request can nudge LCP earlier. But inline a large image and you do the opposite: you delay the document or stylesheet it lives in, pushing LCP later for the whole page. The size threshold decides which way it goes.

For CLS (Cumulative Layout Shift), inlining changes nothing about the core rule: an image still needs explicit width and height (or an aspect-ratio box) so the browser can reserve space before it renders. A data URI without dimensions shifts layout exactly like a remote image without dimensions.

A better lever than inlining is usually shrinking the source. Compressing an image before you encode it makes both the file and any resulting data URI smaller. The browser vs Node image compression guide covers how to do that client-side or in a build step, and WebP vs AVIF vs JPEG helps you pick a format that is small to begin with.

How to inline images in HTML, CSS, Markdown, and JSON

Once you have a data URI, here is how it drops into each context. These are the four ready-to-paste snippets the Image to Base64 converter generates for you.

HTML: paste the URI into any src:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…" alt="logo">

CSS: wrap it in url() for a background-image (this is the canonical base64 image in CSS pattern):

.icon {
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0i…");
}

Markdown: a self-contained image link for READMEs, GitHub issues, and notebooks where you cannot host a file:

![chart](data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ…)

JSON: an embedded asset inside an API or config payload:

{ "icon": "data:image/png;base64,iVBORw0KGgo…" }

All four work anywhere a URL is accepted: img src, CSS background, mask-image, even a favicon <link>. Every modern browser supports the data: scheme.

Generating these quickly

Building these by hand is error-prone: one wrong MIME type or a stray line break and the image silently fails to render. Drop your file into the Image to Base64 converter and it produces all four snippets with their own copy buttons, plus the exact size increase so you know up front whether the asset belongs inline at all.

SVG: the special case where Base64 usually loses

SVG breaks the usual logic, because SVG is text, not binary. Base64 exists to make binary data text-safe, but SVG is already XML text. Encoding it as Base64 just inflates a string that did not need encoding, and makes it unreadable in the process. So for SVG specifically, Base64 is almost always the wrong choice.

Compare three ways to inline the same icon:

/* 1. Base64 data URI — adds the 33% tax to text that didn't need it */
.a { background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0i…"); }

/* 2. URL-encoded data URI — percent-encode a handful of characters, no 33% tax */
.b { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'…%3C/svg%3E"); }

/* 3. Inline <svg> directly in the HTML — fully styleable with CSS */
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
  <path d="M12 2 L22 22 H2 Z" fill="currentColor" />
</svg>

Option 2 (URL-encoding) is usually smaller than option 1, stays human-readable, and compresses better. You only percent-encode the characters that would break the URI (<, >, #, and quotes) and leave the rest legible. The URL encoder/decoder approach is documented in the tool itself; reach for Base64 SVG only when a build pipeline specifically demands it.

Why an inline <svg> often beats a Base64 PNG icon

If you are choosing between a Base64-encoded PNG icon and an inline <svg>, the SVG usually wins. It scales to any size without blurring and carries no 33% tax, and unlike any data URI you can style it with CSS, animate it, and recolor it with currentColor. A Base64 PNG is a fixed-resolution blob you cannot touch once encoded. Reserve raster Base64 for cases where you genuinely need a photograph or a raster screenshot inline.

Decoding the other way: Base64 back to an image

The reverse problem is just as common: you have a Base64 string, pulled from an API response, a log line, or a stylesheet you are debugging, and you need to see the actual picture.

Two details trip people up. First, raw Base64 versus a full data URI. A complete data URI (data:image/png;base64,…) carries its own MIME type; a bare payload (iVBORw0KGgo…) does not. To render a bare payload you either prepend a correct data: prefix or let a tool infer the format from the leading bytes: iVBORw0KGgo means PNG, /9j/ means JPEG, R0lGOD means GIF.

Second, line wrapping. Base64 from email or older tooling is often wrapped at 76 characters per RFC 2045. Those newlines must be stripped before decoding, or the string is invalid in an HTML attribute or url().

In the browser you can hand a complete data URI straight to an <img>:

<img src="data:image/png;base64,iVBORw0KGgo…" alt="decoded">

On the server, Node reconstructs the file from the payload:

import { writeFileSync } from "node:fs";

const b64 = "iVBORw0KGgoAAAANSUhEUgAA…"; // raw payload, no data: prefix
writeFileSync("output.png", Buffer.from(b64, "base64"));

For a no-code path, use the Base64 to Image converter: paste a string (with or without the prefix, line breaks and all), preview it, read its dimensions and MIME type, and download a real PNG, JPG, GIF, or SVG. It strips whitespace, tolerates a missing prefix, and detects the format from magic bytes automatically.

One sanity check worth doing on a decoded image: look at its reported dimensions. If you pulled one string out of a file that held several and the result is 1×1, you probably grabbed a tracking pixel instead of the asset you wanted. And remember that decoding is purely mechanical and lossless: a Base64 PNG comes back as the exact same PNG, byte for byte, with no recompression. The only thing that changed along the way was the container, a text string on the way out and a binary file on the way back.

FAQ

Should I convert my images to Base64?

Only when it is worth it: small (under ~2 KB), rarely-changing icons or logos where skipping one HTTP request matters, plus HTML email, self-contained widgets, and JSON payloads. Large images or anything reused across pages should almost always stay as normal files, so you keep caching and lazy loading.

How much larger does Base64 make an image?

About +33%. Base64 encodes every 3 bytes of binary as 4 ASCII characters, plus a little padding and the data: prefix. A 9 KB PNG becomes roughly 12 KB of text. To convert an image to Base64 and see the exact increase for your file, the tool reports the precise number in its metadata bar.

Does Base64 make images load faster?

For a very small above-the-fold icon it can, by saving one request’s round trip. For larger or reused images it is usually slower: you lose independent caching, you cannot lazy-load it, and inlining it into CSS enlarges a render-blocking resource. Size is the deciding factor.

Can I use a Base64 image in CSS?

Yes: background-image: url("data:image/png;base64,…"). It is fine for tiny icons. Just remember the data URI becomes part of the stylesheet, so the whole file re-downloads whenever the CSS changes, and the image cannot be cached separately from it.

Should I use SVG or Base64 for icons?

Prefer an inline <svg> or a URL-encoded SVG data URI. SVG is text, scales cleanly, and carries no 33% tax, so it is usually smaller than a Base64 PNG and you can style it with CSS. Reach for Base64 only when you specifically need a raster icon.

How do I convert a Base64 string back to an image?

In the browser, drop a full data:image/…;base64,… URI into an <img src>. On a server, use Buffer.from(b64, "base64") to write the file. A raw payload needs a data: prefix added, and line-wrapped strings need their newlines stripped first. The Base64 to Image tool handles all of that and lets you download the result.

Tags: base64 data-uri images performance css web-performance

Related Articles

View all articles