To embed an image in HTML as base64, put a data URI in the src where the file path used to be:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Company logo" width="180">
The picture is now part of the HTML. Send the file anywhere and the image goes with it, because there is nothing separate left to lose.

The three parts of the URI
data:image/png;base64,iVBOR... breaks into pieces that each have to be right.
data:says the content follows inline rather than at an address.image/pngis the MIME type. Useimage/jpeg,image/gif,image/webporimage/svg+xmlas appropriate.;base64says the payload is base64 encoded.- Everything after the comma is the payload, on one unbroken line.
Get the type wrong and browsers usually still render it, but not always, and a mismatch is the sort of thing that works locally and fails in one place. Data URI covers the format beyond images.
Encoding a file
macOS or Linux:
base64 -i logo.png | tr -d '\n' > logo.txt
Windows PowerShell:
[Convert]::ToBase64String([IO.File]::ReadAllBytes("logo.png")) > logo.txt
Strip the line breaks. Some tools wrap the output at 76 characters, and a wrapped string inside an attribute will not decode.
Paste the result after the comma. It will be long. A small logo runs to thousands of characters, and that is normal.
The size cost
Base64 stores three bytes in four characters, so the text is about 33 percent larger than the original file.
| Original | As base64 | Sensible inline |
|---|---|---|
| 2 KB icon | ~2.7 KB | Yes |
| 20 KB logo | ~27 KB | Yes |
| 200 KB photo | ~267 KB | Only if the file must travel alone |
| 2 MB screenshot | ~2.7 MB | No, host it |
There is a second cost beyond bytes. A separate image file is cached by the browser and reused across pages; an inline one is re-downloaded with every copy of the HTML.
For one document sent once, that does not matter. For a site with a repeated header logo, it does.
Compress before you encode. Resizing a 2000 pixel screenshot down to the 800 pixels it is displayed at often saves more than the encoding costs.

SVG does not need base64
SVG is text already, so you can inline it directly and skip the size penalty:
<img src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'>...</svg>">
Use single quotes inside and percent-encode any # as %23, since a hash starts a fragment. Better still, paste the <svg> element straight into the page, where CSS can reach its parts. SVG in HTML goes through both routes.
In CSS as well
The same URI works anywhere CSS takes a URL:
.hero { background-image: url("data:image/jpeg;base64,/9j/4AAQSkZJRg..."); }
This is how a self-contained page keeps its background images. The stylesheet has to be inline too, or the CSS file becomes the thing that goes missing.
When to embed an image as base64, and when not to
| Situation | Inline as base64 |
|---|---|
| Small icon or logo in a page you will send as a file | Yes |
| A page that must work with no network | Yes |
| Photo gallery | No, host the images |
| Site header repeated across many pages | No, it defeats caching |
| An email body | No, several clients block data URIs |
| A page you are sending as a link | Not needed, reference the address |
That last row matters. Inlining is a workaround for files that travel on their own. Once the page has an address, the images can have addresses too.
Where it still breaks
Content Security Policy. A strict img-src rule blocks data URIs unless data: is listed. See content security policy.
Mail clients. Several strip or block inline image data, leaving an empty box.
Editing. A thousand-character attribute in the middle of your markup makes the file hard to read and diff. Keep inlining as a final step rather than an authoring habit.

The alternative to all of this
Inlining exists because a file sent on its own arrives without its neighbours. Give the page an address and that problem disappears.
Paste the HTML into a NOS document and it renders as written, with inline images or hosted ones. The document has its own link, so nothing has to be folded in to survive the trip.
Self-contained HTML covers the whole one-file approach, images not showing in HTML covers the failure it is meant to prevent, and turning HTML into a link is the route that avoids needing either.
Checking that it worked
Inlining is easy to get almost right, and the failures are quiet. Three checks settle it.
Move the file. Copy the HTML to a folder with no images in it and open it there. Anything that still draws is genuinely inside the file.
Look for the alt text. A broken data URI usually shows the alt text rather than an error, so an image that silently became a line of words is the sign of a truncated string.
Check the start of the payload. A PNG payload begins iVBOR, a JPEG begins /9j/, and a GIF begins R0lGOD. If the declared type and that prefix disagree, the type is wrong.

One habit saves trouble later. Keep the original image file next to the source, even after inlining, because you cannot usefully edit a picture that exists only as an attribute.