How to Encode Files to Base64 in JavaScript (FileReader API)
If you've ever needed to send an image inside a JSON payload or embed a tiny icon directly in your HTML, you've probably run into Base64. Most tutorials show how to Base64-encode strings with btoa(), but encoding a file
If you've ever needed to send an image inside a JSON payload or embed a tiny icon directly in your HTML, you've probably run into Base64. Most tutorials show how to Base64-encode strings with btoa(), but encoding a file — an actual image, PDF, or font chosen through an <input type="file"> — is a different and very practical skill.
In this tutorial, you'll learn how to turn any user-selected file into a Base64 string using the browser's FileReader API, strip out the raw Base64 payload when that's all you need, and put the result to work by embedding a small image with zero extra network requests.
The FileReader API in 30 seconds
FileReader is a built-in browser API for reading the contents of File or Blob objects asynchronously. The method we care about is readAsDataURL(file). It reads the file and hands you back a data URL — a string shaped like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...
A data URL packs three things into one string: the MIME type (image/png), the declared encoding (base64), and the actual Base64-encoded bytes after the comma.
Step 1: Reading a file with readAsDataURL
Here is a minimal example. Drop it into an HTML file with a file input:
<input type="file" id="picker" accept="image/*">
<pre id="output"></pre>
<script>
const picker = document.getElementById('picker');
const output = document.getElementById('output');
picker.addEventListener('change', () => {
const file = picker.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
output.textContent = event.target.result; // the data URL
};
reader.onerror = () => {
console.error('Could not read file:', reader.error);
};
reader.readAsDataURL(file);
});
</script>
The key detail: FileReader is asynchronous. Register onload before calling readAsDataURL, because reader.result is null until the read finishes. And always handle onerror, so a failed read doesn't fail silently.
Step 2: Extracting just the Base64 payload
Often you don't want the whole data URL — maybe your API expects the raw Base64 string. The payload is everything after the first comma:
const dataUrl = event.target.result;
const base64Payload = dataUrl.split(',')[1];
If you prefer to be explicit about the header format, you can split on the encoding marker instead:
const [header, base64Payload] = dataUrl.split(';base64,');
console.log(header); // "data:image/png"
console.log(base64Payload); // "iVBORw0KGgo..."
Both approaches work. Either way, guard against unexpected input — if the split doesn't produce two parts, the value wasn't a data URL.
A practical example: embedding a small image
Data URLs shine for small assets: icons, logos, or thumbnails you want available instantly with no extra HTTP request. This complete example reads an image and shows a live preview by using the data URL directly as the image source:
<input type="file" id="picker" accept="image/*">
<img id="preview" alt="preview" style="max-width: 300px; display: block;">
<script>
const picker = document.getElementById('picker');
const preview = document.getElementById('preview');
picker.addEventListener('change', () => {
const file = picker.files[0];
if (!file) return;
if (file.size > 100 * 1024) {
alert('Pick an image under 100 KB for this demo.');
return;
}
const reader = new FileReader();
reader.onload = (event) => {
preview.src = event.target.result; // data URL works as an image source
};
reader.readAsDataURL(file);
});
</script>
Pick a small PNG and it appears immediately — no upload, no server round trip.
A few things to watch out for
- Size overhead. Base64 inflates data by about 33%. A 100 KB image becomes roughly a 133 KB string. Fine for icons; terrible for photo galleries — use real file hosting for large assets.
- Memory. A giant data URL sits in memory as one big string. Avoid reading multi-megabyte files this way in production code.
-
Strings vs. files.
btoa()andatob()handle text;FileReaderhandles files and binary data. Mixing them up is the most common beginner mistake —btoa()throws on characters outside the Latin-1 range. -
Decoding back. To turn a data URL back into a downloadable file,
fetch(dataUrl).then(res => res.blob())is a handy one-liner.
Wrapping up
The pattern is always the same: create a FileReader, listen for load, call readAsDataURL, then use the data URL whole or split off the payload after the comma. With that one pattern, you can preview uploads instantly, inline small assets, and build file-handling features without any backend.
And when you just need to quickly encode or decode a Base64 string by hand — no code, no file picker — a free online tool like ToolStack's Base64 encoder and decoder does it right in your browser with no signup, and everything stays private on your machine.
Happy coding!
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.