Skip to content
Back to Blog

Image to Base64: Data URIs Done Right

A base64 data URI lets you embed an image directly in HTML or CSS with no extra HTTP request. This guide shows the exact data:image syntax, the 33% size penalty to budget for, and when inlining is the wrong call.

SZ
Founder, Molixa
11 min read
Share
Image to Base64: Data URIs Done Right
Table of contents10 sections

Converting an image to base64 turns a binary file into a long ASCII string you can paste straight into HTML, CSS, JSON, or a database column. That string becomes a data URI like data:image/png;base64,iVBORw0KGgo..., and the browser renders it as if it loaded a real image file. The catch most converter pages never mention: base64 makes every image about 33% larger, so inlining is a tool you reach for in specific cases, not a default.

This guide shows the exact syntax, the correct MIME prefix for each format, how to encode an image in the browser and in code, and the caching math that decides whether inlining a given image is a smart move or a mistake.

What Is a Base64 Image Data URI?#

A data URI is a way to embed file contents directly inside a document instead of linking out to a separate file. For images, the format is fixed and predictable:

data:[<MIME type>][;base64],<encoded data>

A real PNG example looks like this (truncated):

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ...

Three parts matter:

  • data: tells the browser this is inline data, not a URL to fetch.
  • image/png;base64 is the MIME type plus the encoding flag. This is the part people get wrong.
  • Everything after the comma is the base64-encoded bytes of the actual image.

Because the whole image lives inside the string, the browser does not make a separate network request for it. That single property is the entire reason data URIs exist, and the source of every tradeoff in this article.

Quick mental model: base64 is not compression. It is the opposite. It is a way to represent binary data using only safe text characters, and that representation is always bigger than the original.

The MIME prefix per image format#

Using the wrong MIME type is the number one reason an embedded image renders as a broken icon. The prefix must match the actual file bytes, not the file extension you think you have.

Image formatCorrect data URI prefix
PNGdata:image/png;base64,
JPEGdata:image/jpeg;base64,
GIFdata:image/gif;base64,
WebPdata:image/webp;base64,
SVGdata:image/svg+xml;base64,
ICO (favicon)data:image/x-icon;base64,

Note that JPEG is image/jpeg, never image/jpg, even though the file extension is usually .jpg. SVG is a special case because it is text, not binary, so you can often skip base64 entirely and use URL-encoding instead (more on that below).

How to Convert an Image to Base64#

You have three practical paths depending on whether you want a one-off string, a browser-side conversion in your own app, or a build-step output. Here is the fastest route first, then the code.

Step 1: Get the string with a no-install converter#

For a quick one-off, you do not need to write any code. Drop the file into a base64 encoder and decoder, or use the dedicated image to base64 converter when you want the full data:image/... prefix generated for you. You paste or upload, you copy the output, you are done. This is the right move when you just need the string once for a CSS rule or an email template.

The conversion runs in your browser, so the image never leaves your machine. That matters when the image is a private asset, a client logo, or anything you would not paste into a random online tool.

Step 2: Encode in the browser with FileReader#

When you need to convert images inside your own web app (for example, a user uploads an avatar and you want to preview it before sending), the FileReader API gives you a data URI directly:

function fileToDataURL(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result); // already a full data: URI
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// usage
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async () => {
  const dataURL = await fileToDataURL(input.files[0]);
  document.querySelector('img').src = dataURL; // renders instantly
});

readAsDataURL returns the complete data:image/png;base64,... string, prefix included, so you can assign it straight to an img src or a background-image. No manual MIME detection needed.

Step 3: Encode on the server or at build time#

In Node.js you read the file as a buffer and call toString('base64'), then build the prefix yourself:

import { readFileSync } from 'node:fs';

const bytes = readFileSync('logo.png');
const base64 = bytes.toString('base64');
const dataURI = `data:image/png;base64,${base64}`;

In Python it is nearly identical:

import base64

with open("logo.png", "rb") as f:
    encoded = base64.b64encode(f.read()).decode("ascii")

data_uri = f"data:image/png;base64,{encoded}"

Build tools like webpack and Vite automate this. Vite, for instance, inlines any asset under a size threshold (4KB by default) as a base64 data URI automatically, and leaves larger files as normal URLs. That default threshold exists precisely because of the size math in the next section.

Where You Actually Use Base64 Images#

The string is useless until you place it somewhere. The three common homes:

In HTML, as an img source:

<img src="data:image/png;base64,iVBORw0KGgo..." alt="Inline logo" />

In CSS, as a background:

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

In JSON or an API payload, where you cannot send raw binary, so the image rides along as a string field:

{ "avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..." }

That JSON case is one of the strongest legitimate uses. When an API is JSON-only and you must transport an image inline, base64 is the standard answer.

The 33% Size Penalty Nobody Mentions#

Here is the part converter pages skip entirely. Base64 encodes 3 bytes of binary data into 4 ASCII characters. That ratio is fixed: 4/3, which is a roughly 33% increase before you even count padding. A 90KB PNG becomes about 120KB of base64 text.

That overhead does not disappear. It lands in your HTML or CSS file, which means:

  • Larger HTML or CSS payloads. The encoded bytes are now part of the document the browser must download and parse.
  • No streaming or progressive rendering. A linked image can render as it arrives. An inlined one only appears after its entire string has downloaded as part of the parent file.
  • Gzip helps, but not enough. Compression recovers some of the loss because base64 text is repetitive, but you rarely get back to the original binary size, and the CPU cost of decoding the string is real on low-end devices.

Warning: inlining a large hero image or photo is almost always a mistake. A 500KB photo becomes roughly 665KB of base64 dropped into your HTML, blocking the page from finishing parse while it downloads. Keep that as a linked file.

If your goal is a smaller image rather than an inlined one, encoding is the wrong lever. Shrink the file first with a free image compressor, then decide whether the now-smaller asset is worth inlining at all.

The decision comes down to one question: does avoiding an HTTP request save more than the 33% bloat costs you? Use this as a rule of thumb.

Inline as base64 whenLink as a normal file when
Tiny assets: small icons, 1px spacers, simple SVGsPhotos, hero images, anything over ~5KB
The image is critical and you want zero extra requestsThe image can lazy-load or appears below the fold
You are sending an image inside JSON or an email bodyThe image is reused across many pages (caching wins)
The asset is single-use and unique to one pageThe asset benefits from a CDN or browser cache

The caching point is the one developers most often forget. A linked image is cached once and reused on every page and every revisit. An inlined image is re-downloaded inside the document every single time, on every page, with no cache benefit of its own. For a logo that appears site-wide, linking wins decisively. For a one-off icon on a single landing page, inlining can shave a request with negligible cost.

On modern HTTP/2 and HTTP/3 connections, the original justification for inlining (avoiding the cost of many parallel requests) is much weaker than it was in the HTTP/1.1 era. Request multiplexing made small files cheap to fetch. That is another reason to inline sparingly today.

SVG: The Format Where You Often Skip Base64#

SVG is XML text, not binary, so base64 is usually the wrong choice for it. You can drop a URL-encoded SVG into CSS and skip the encoding overhead entirely:

.checkmark {
  background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"...%3E');
}

URL-encoding an SVG keeps it human-readable and avoids the 33% base64 penalty, because you are only escaping the handful of characters that break inside a CSS url(). Reserve base64 for binary formats (PNG, JPEG, WebP) where there is no readable alternative.

Decoding a Base64 Image Back to a File#

Going the other way is just as common. If you have a data URI and need the original file, strip the prefix and decode the remainder. In the browser:

function dataURLtoBlob(dataURL) {
  const [meta, b64] = dataURL.split(',');
  const mime = meta.match(/data:(.*?);base64/)[1];
  const binary = atob(b64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
  return new Blob([bytes], { type: mime });
}

For a quick manual decode without code, paste the string (with or without the prefix) into the same base64 codec tool and download the rebuilt image. If you want a deeper walkthrough of round-tripping images through base64 and data URLs, the guide on encoding and decoding base64 images and data URLs covers the full cycle with examples.

Common Mistakes That Break Inline Images#

A few errors account for nearly every broken-icon situation:

  • Wrong MIME type. Tagging a JPEG as image/png confuses some renderers. Match the prefix to the actual bytes.
  • Including line breaks. Some tools wrap base64 output at 76 characters. In an HTML attribute or CSS rule, those newlines can break the string. Use a single unbroken line.
  • Forgetting ;base64. Without that flag, the browser treats the data as plain text or URL-encoded content, not encoded binary.
  • Inlining gigantic images. Technically valid, practically a performance bug. Watch your document size.
  • Pasting private images into untrusted online encoders. Prefer a converter that runs client-side so the file never uploads.

Converting an Image to Base64, in Practice#

To recap the workflow for going from image to base64 the right way: pick the format-correct MIME prefix, encode with a client-side tool or FileReader, and budget for the roughly 33% size increase. Inline only small, single-use, render-critical assets, and keep photos and reused images as linked files so the browser cache can do its job. SVGs usually want URL-encoding, not base64, at all.

When you just need the string fast and safe, the base64 encoder and decoder handles both directions in your browser, and the dedicated image to base64 converter builds the full data:image/... data URI so you can paste it straight into your markup.

Frequently Asked Questions#

How do I convert an image to base64? Upload or paste the file into a client-side encoder, or use code: in the browser call FileReader.readAsDataURL(file), and in Node.js read the file as a buffer and call buffer.toString('base64'). Then prepend the correct prefix, such as data:image/png;base64,, to get a usable data URI.

Why is my base64 image larger than the original file? Base64 represents 3 bytes of binary as 4 text characters, which inflates size by about 33% before padding. It is an encoding, not a compression format, so the encoded version is always bigger. If you need a smaller asset, compress the image first, then decide whether to inline it.

What is the correct data URI prefix for a JPEG? It is data:image/jpeg;base64,, using image/jpeg and never image/jpg. The file extension on disk is usually .jpg, but the MIME type in the data URI must be jpeg. A mismatched prefix is the most common reason an inline image renders as a broken icon.

Should I inline images as base64 for performance? Only for tiny, single-use, render-critical assets like small icons. Inlining avoids one HTTP request but adds the 33% bloat to your HTML or CSS, prevents the browser from caching the image separately, and re-downloads it on every page. With HTTP/2 and HTTP/3, the request-saving benefit is smaller than it used to be, so link photos and reused images instead.

Do I need base64 for SVG images? Usually no. SVG is text, so you can URL-encode it inside a CSS url() and skip the 33% base64 overhead entirely while keeping the markup readable. Reserve base64 for binary formats like PNG, JPEG, and WebP where there is no text-based alternative.

Is it safe to use an online image-to-base64 converter? It depends on whether the tool processes the file in your browser or uploads it to a server. For private assets like client logos, use a converter that runs client-side so the image never leaves your machine. Molixa's base64 and image-to-base64 tools encode locally in the browser for exactly this reason.

More from Molixa

Try Molixa Tools

50+ free AI tools for content creation, SEO, coding, and more. No signup, no watermark.

Explore all tools