Base64 images in HTML: how and when to embed a picture

A base64 image is the picture's bytes encoded as text and placed in the src attribute, so it cannot be separated from the page. Right for icons and small charts, wrong for photographs: every three bytes become four characters and the page cannot draw until all of it has arrived.

A base64 image in HTML is a picture written into the src attribute as text instead of referenced as a file: data:image/webp;base64, followed by the encoded bytes.

The markup. The highlighted line is the part this term is about.
The markup. The highlighted line is the part this term is about.

The image is then part of the HTML, and sending the file alone sends the picture.

That is exactly what a page that will be emailed or pasted needs for its icons and small charts, and exactly wrong for photographs, which grow by a third and hold up rendering.

This guide covers producing the string, compressing first, the arithmetic, and when a full address is the better choice.

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..." alt="Weekly signups">

The picture is now inside the HTML. Nothing can separate them — no folder to keep together, no address to go stale.

Producing a base64 image

In a shell

A base64 image is the file's bytes written as text inside the tag, so it cannot go missing. Every three bytes become four characters, which is fine for icons and charts and ruinous for photographs.
A base64 image is the file's bytes written as text inside the tag, so it cannot go missing. Every three bytes become four characters, which is fine for icons and charts and ruinous for photographs.
base64 -w0 chart.png > chart.b64

-w0 prevents line breaks. Without it the output is wrapped at 76 characters and a newline inside a src attribute breaks it.

In a browser

<script>
  var r = new FileReader();
  r.onload = function () { console.log(r.result); };
  r.readAsDataURL(file);   // gives the full data URI, prefix included
</script>

readAsDataURL returns the complete string starting data:image/png;base64, — the least error-prone route, since there is no prefix to assemble by hand.

Assembling it by hand

data:image/png;base64,<the base64 text>
data:image/jpeg;base64,<the base64 text>
data:image/webp;base64,<the base64 text>

The media type must match the actual file. A JPEG labelled as PNG will not display, and nothing explains why.

A page split across files ✗ Works only inside its own folder ✗ Styling vanishes when sent alone ✗ Images turn into empty boxes ✗ Breaks the moment a file is renamed One self-contained file ✓ Renders anywhere it lands ✓ Styling travels with it ✓ Images are carried inside ✓ Nothing to keep together
An embedded image travels inside the file. A referenced one travels only if the folder does.

Compress first

This is the step people skip, and it is the one that matters most.

A screenshot from a phone can be several megabytes. Encoded, it is a third larger again as text inside your HTML. The same image resized to twice its display width and saved as WebP might be 80KB, which encodes to about 107KB.

Order of operations:

  1. Resize to roughly twice the display width — no more.
  2. Compress — WebP for photographs and flat colour, PNG for screenshots with fine text.
  3. Then encode.

Encoding an uncompressed image is how a one-page report becomes a 12MB file.

When embedding is worth it

Case Verdict
A small icon or logo Yes — removes a request
A favicon in a single-file page Yes
A diagram in a report sent as one file Yes
A page that must work with no network Yes
A page rendered in a restricted preview Yes — outside requests are blocked
A photograph No
A portfolio with eight images No
Any page served normally from a site No

Why photographs are the wrong case

Three costs that compound:

No progressive rendering. The image is part of the document, so the browser has nothing to show until the whole document has arrived. Separate images appear one by one as they load.

No independent caching. A separate image is cached once and reused. An embedded one is re-downloaded with every copy of the page.

No lazy loading. loading="lazy" cannot defer something already inside the document, so images below the fold are paid for whether or not anyone scrolls.

Prefer SVG where the content allows

For icons, logos, charts and diagrams, inline SVG beats base64 on every axis: smaller, sharp at any zoom, colourable by your CSS so it follows dark mode, and readable as text in the file rather than an opaque blob.

Reach for base64 only when the content is genuinely a photograph or a raster screenshot.

The blocked case

A strict Content-Security-Policy that omits data: from its image directive blocks data URIs. So an embedded icon can work locally and vanish once published. If images disappear after deployment and the console mentions a policy, that is the cause.

Three base64 mistakes that cost the most

Skipping the compression step. A screenshot saved from a phone is three or four megabytes; encoded, it is five, and the page shows nothing until the last character has arrived. Resize to the display width and save as WebP first, and the same image is forty kilobytes.

Losing the type. data:;base64, with no image/png or image/webp leaves the browser guessing, and some do not guess.

Embedding the same image twice. A logo that appears in the header and the footer is encoded twice, doubling its cost. Put it once in an inline SVG symbol or a CSS rule and reference it.

Where the string comes from, concretely

On a Mac or Linux terminal, base64 -i chart.webp prints the string. On Windows PowerShell, [Convert]::ToBase64String([IO.File]::ReadAllBytes("chart.webp")). In a browser, an <input type="file"> and FileReader.readAsDataURL return the complete data URI with the type already in front.

Any of the three takes seconds; the part that takes judgement is deciding which images deserve it.

Embedding an image as base64: 4 steps

  1. Resize and compress the image first. To the width it is shown at, as WebP or a compressed PNG. Encoding makes whatever you start with a third larger.
  2. Encode it. Any base64 tool, a one-line shell command, or FileReader.readAsDataURL in the browser.
  3. Put it in the src with its type. data:image/webp;base64, then the string, plus width, height and alt on the tag.
  4. Keep photographs out. Anything over about 100 KB goes at a full https:// address instead, or the page becomes megabytes of text that has to arrive before anything draws.

Questions people ask

How do I convert an image to base64?

base64 -w0 file.png in a shell, or FileReader.readAsDataURL in a browser. The browser route gives you the complete data URI including the prefix.

How much bigger does it get?

A third larger than the original bytes - base64 encodes every three bytes as four characters, so that ratio is exact rather than an estimate. Compress the image before encoding.

When should I embed rather than link?

When the file must work offline, inside a restricted preview, or as a single attachment. For anything else, a separate file is better.

Why does the page load slowly with embedded images?

An embedded image is part of the document, so nothing renders until the entire document has arrived. Separate images load progressively.

Keep reading