All Tools / Blog / How to Base64 Encode an Image for CSS and HTML

How to Base64 Encode an Image for CSS and HTML

3 min read

Instead of linking to an external image file, you can embed the image data directly in your CSS or HTML using a Base64 data URI. This eliminates the separate HTTP request for the image — useful for small icons, inline SVGs, and email templates where external requests are blocked.

What a data URI looks like

data:[mime-type];base64,[base64-encoded-data]

A real example for a small PNG:

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

That 1×1 transparent PNG is 68 bytes. Its Base64 data URI is 88 characters — about 30% larger, which is the standard overhead for Base64 encoding.

Using a Base64 image in CSS

/* Background image */
.icon {
    background-image: url('data:image/png;base64,iVBORw0KGgo...');
    width: 16px;
    height: 16px;
    background-size: contain;
}

/* As a list-style image */
li {
    list-style-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucy...');
}

Using a Base64 image in HTML

<!-- img tag -->
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgAB..." alt="Photo">

<!-- HTML email (inline images often blocked) -->
<img src="data:image/png;base64,iVBORw0KGgo..." width="200" height="100" alt="Logo">

Generating the data URI in Python

import base64
from pathlib import Path

def image_to_data_uri(path: str) -> str:
    mime_map = {
        ".png":  "image/png",
        ".jpg":  "image/jpeg",
        ".jpeg": "image/jpeg",
        ".gif":  "image/gif",
        ".svg":  "image/svg+xml",
        ".webp": "image/webp",
    }
    suffix = Path(path).suffix.lower()
    mime = mime_map.get(suffix, "image/png")
    with open(path, "rb") as f:
        encoded = base64.b64encode(f.read()).decode("utf-8")
    return f"data:{mime};base64,{encoded}"

# Usage
uri = image_to_data_uri("logo.png")
print(f"background-image: url('{uri}');")

Generating the data URI in JavaScript (browser)

// From a File object (e.g. file input)
function fileToDataUri(file) {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onload = (e) => resolve(e.target.result);
        reader.readAsDataURL(file);
    });
}

// Usage with file input
document.querySelector('input[type="file"]').addEventListener('change', async (e) => {
    const uri = await fileToDataUri(e.target.files[0]);
    document.querySelector('img').src = uri;
});

SVG: a special case

SVG images can be embedded without Base64 — you can inline the SVG XML directly with URL encoding, which is often smaller:

/* Base64 (larger) */
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucy...');

/* URL-encoded SVG (smaller for simple SVGs) */
background-image: url('data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22...');

/* Or inline directly in HTML */
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">...</svg>

For SVGs with complex paths, Base64 tends to be smaller. For simple icons (under ~500 bytes), URL-encoding wins.

When to use data URIs

Good use cases:

  • Small icons and logos (under ~4 KB) in CSS — saves an HTTP request, the data is usually cached with the stylesheet.
  • HTML email — many email clients block external image requests; embedding is the only reliable option.
  • Single-file deliverables — HTML reports, exported documents, self-contained demos.
  • Favicons — a common pattern for dynamically generated favicons.

When not to use data URIs:

  • Large images — a 200 KB photo becomes a 267 KB Base64 string, and it can't be cached independently from the HTML/CSS file.
  • Images reused on multiple pages — an external file cached by the browser is faster than re-embedding on each page.
  • Performance-critical pages — large data URIs block HTML parsing and increase initial payload size.

Size impact

Image size Base64 size Overhead
1 KB 1.37 KB +37%
10 KB 13.7 KB +37%
100 KB 137 KB +37%

The 33–37% overhead is constant. For anything above 4–5 KB, an external file with proper caching headers is usually faster.

Key takeaways

  • Data URI format: data:[mime];base64,[data] — works in src, background-image, and anywhere a URL is accepted.
  • Use Python's base64.b64encode() or FileReader.readAsDataURL() in browsers to generate them.
  • Best for small images (under ~4 KB) and HTML email where external requests are blocked.
  • SVGs can be URL-encoded instead of Base64-encoded — often smaller for simple icons.
  • Base64 adds ~37% overhead — don't embed large images this way.