How to Embed Images in HTML Using Base64 Data URIs

Learn how to convert images to Base64 and embed them directly in HTML and CSS. Includes examples, use cases, performance considerations, and best practices.

Try it yourself

Use our free Image to Base64 Converter — no sign-up, runs in your browser.

Open tool →

You can embed images directly in HTML and CSS without separate image files by converting them to Base64 data URIs. This eliminates HTTP requests, simplifies deployment, and works great for small icons and logos. This guide shows you how.

What is a Base64 Data URI?

A data URI (also called data URL) is a way to embed file content directly in HTML, CSS, or JavaScript using a special URI scheme. For images, it looks like this:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." />

The format breaks down as:

  • data: - URI scheme identifying this as a data URI
  • image/png - MIME type of the content
  • ;base64 - encoding method
  • ,iVBORw0... - the Base64-encoded image data

The browser decodes the Base64 string and renders the image as if you had linked to a separate file.

Why embed images as Base64?

Pros:

  • Fewer HTTP requests - no round-trip to the server for tiny icons
  • Atomic deployment - HTML and images ship together
  • Works offline - no external dependencies
  • No broken image links - useful for email HTML templates
  • Simplifies development - one file instead of many

Cons:

  • 33% size increase - Base64 encoding adds ~33% overhead
  • No browser caching - embedded images are re-downloaded with the HTML
  • Larger HTML/CSS files - bloated page weight
  • No lazy loading - all Base64 images load immediately
  • Poor performance for large images - defeats all browser optimizations

Rule of thumb: Use Base64 for images under 5-10 KB (icons, logos, bullets). Use regular <img> tags for anything larger.

How to convert images to Base64

Online tool (easiest)

Use our Image to Base64 Converter:

  1. Drag and drop your image
  2. Choose “Data URI” output mode
  3. Copy the result
  4. Paste directly into your HTML src attribute

JavaScript (browser)

function imageToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// Usage with file input
document.querySelector('input[type="file"]').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const dataUri = await imageToBase64(file);
  console.log(dataUri);
  // data:image/png;base64,iVBORw0KGgo...
});

Node.js

const fs = require('fs');
const path = require('path');

function imageToBase64(imagePath) {
  const image = fs.readFileSync(imagePath);
  const ext = path.extname(imagePath).slice(1);
  const mimeType = `image/${ext === 'jpg' ? 'jpeg' : ext}`;
  const base64 = image.toString('base64');
  return `data:${mimeType};base64,${base64}`;
}

const dataUri = imageToBase64('./logo.png');
console.log(dataUri);

Python

import base64
import mimetypes

def image_to_base64(image_path):
    mime_type, _ = mimetypes.guess_type(image_path)
    with open(image_path, 'rb') as f:
        encoded = base64.b64encode(f.read()).decode()
    return f"data:{mime_type};base64,{encoded}"

data_uri = image_to_base64('logo.png')
print(data_uri)

Command line

# macOS/Linux
echo "data:image/png;base64,$(base64 -i logo.png)" | pbcopy

# Linux (xclip)
echo "data:image/png;base64,$(base64 -w 0 logo.png)" | xclip -selection clipboard

Embedding in HTML

Image tag

<img 
  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." 
  alt="Logo"
  width="100"
  height="100"
/>

Favicon

<link 
  rel="icon" 
  type="image/png" 
  href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..."
/>

Background image in inline CSS

<div style="background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...');">
  Content
</div>

Embedding in CSS

Base64 images work perfectly in CSS background-image, list-style-image, border-image, and content properties.

Background image

.icon {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...');
  background-size: contain;
  background-repeat: no-repeat;
  width: 24px;
  height: 24px;
}

SVG icons (often better than Base64)

For SVG files, you can skip Base64 entirely and inline the SVG markup:

.icon {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z"/></svg>');
}

This avoids the 33% Base64 overhead. Just remember to URL-encode special characters:

  • #%23
  • <%3C
  • >%3E
  • &%26

Multiple icons in one stylesheet

.icon-home {
  background-image: url('data:image/svg+xml;base64,PHN2Zy...');
}

.icon-settings {
  background-image: url('data:image/svg+xml;base64,PHN2Zy...');
}

.icon-user {
  background-image: url('data:image/svg+xml;base64,PHN2Zy...');
}

Embedding in React/JSX

function Logo() {
  const logoSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...";
  
  return (
    <img 
      src={logoSrc} 
      alt="Logo"
      width={100}
      height={100}
    />
  );
}

Or import and use directly:

import logo from './logo.png'; // Webpack/Vite converts to Base64 automatically

function Logo() {
  return <img src={logo} alt="Logo" />;
}

Most bundlers (Webpack, Vite, Parcel) have size thresholds - small images become Base64, large images get separate files.

Real-world examples

Email HTML templates

External images often get blocked by email clients. Base64 ensures images always display:

<!DOCTYPE html>
<html>
<body>
  <img 
    src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." 
    alt="Company Logo"
    style="width: 200px;"
  />
  <p>Welcome to our newsletter!</p>
</body>
</html>

Loading spinners

Embed a spinner GIF to avoid the irony of a loading indicator that doesn’t load:

<div id="loading">
  <img src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///..." alt="Loading..." />
</div>

Placeholder images

Use Base64 for tiny blurred placeholders (progressive image loading):

<img 
  src="data:image/jpeg;base64,/9j/4AAQSkZJRg..." 
  data-src="high-res-photo.jpg"
  class="lazy-load"
  alt="Photo"
/>

Browser extensions

Chrome/Firefox extensions can’t always load local files. Base64 sidesteps this:

// content.js
const icon = document.createElement('img');
icon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...";
document.body.appendChild(icon);

Performance considerations

When to use Base64

Good use cases:

  • Icons under 5 KB
  • Logos and badges
  • Small UI elements (arrows, bullets, decorations)
  • Loading spinners
  • Favicons
  • Single-page applications where fewer HTTP requests matter
  • Email HTML templates

Bad use cases:

  • Photos and large images (defeats caching and lazy loading)
  • Anything over 10-20 KB
  • Images used across multiple pages (better to cache separately)
  • Responsive images with multiple sizes
  • Images that change frequently

HTTP/2 impact

With HTTP/2, multiple small file requests happen in parallel with minimal overhead. The “reduce HTTP requests” argument for Base64 is weaker now. Still useful for single-file deployment scenarios (emails, offline HTML files, simple tools).

Caching strategy

Base64 images embedded in HTML or CSS get cached with the parent file. If your image rarely changes but your HTML/CSS updates often, separate image files with long cache headers perform better:

<!-- Separate file = cached for 1 year -->
<img src="/logo.png" alt="Logo" />
Cache-Control: public, max-age=31536000, immutable

Bundle size

If you’re using a bundler, check your configuration. Most have size thresholds:

// Webpack
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024 // 8 KB threshold
          }
        }
      }
    ]
  }
};

Common MIME types

FormatMIME TypeNotes
PNGimage/pngBest for icons, logos, transparency
JPEGimage/jpegPhotos, gradients
GIFimage/gifAnimations, simple graphics
WebPimage/webpModern format, better compression
SVGimage/svg+xmlVector graphics, infinitely scalable
ICOimage/x-iconFavicons
BMPimage/bmpUncompressed (avoid for web)

Troubleshooting

Image doesn’t display

Check MIME type: Ensure it matches the actual image format. image/jpeg for .jpg files, not image/jpg.

Check Base64 validity: Copy the Base64 string (without the data:image/...;base64, prefix) and decode it using a Base64 decoder. If decoding fails, re-encode the image.

Check for line breaks: Some Base64 encoders insert line breaks every 76 characters. Remove them:

const base64 = encodedString.replace(/\n/g, '');

Performance issues

If your page is slow:

  1. Check image sizes - anything over 50 KB embedded is a red flag
  2. Use WebP format instead of PNG/JPEG for better compression
  3. Move large images to separate files
  4. Lazy load Base64 images if they’re below the fold

Invalid character errors

If you see “Invalid character” when decoding:

  • Ensure the Base64 string contains only A-Z, a-z, 0-9, +, /, and =
  • Remove the data:image/png;base64, prefix before decoding
  • Check for accidental whitespace or newlines

Tools and resources

Conclusion

Base64 data URIs are a powerful technique for embedding small images directly in HTML and CSS. They eliminate HTTP requests, simplify deployment, and work great for icons, logos, and tiny UI elements.

Just remember:

  • Use sparingly - only for images under 5-10 KB
  • Never for photos or large graphics
  • Prefer separate files for frequently-changing images
  • Consider HTTP/2 parallel loading advantages
  • Test performance impact with real-world data

For a quick conversion, try our Image to Base64 tool - drag, drop, and copy the data URI in seconds.

Ready to try it?

Free, client-side Image to Base64 Converter — nothing sent to a server.

Open Image to Base64 Converter →