[GUIDE] How to Preview Base64 Encoded Images in the Browser
Five proven ways to display Base64-encoded images on the page — direct src assignment, FileReader, canvas, blob conversion, and framework-specific patterns. Includes debugging tips.
// THE 30-SECOND ANSWER
If you have a Base64-encoded image string and you just want to see it on the page, the shortest path is to assign it as a data: URI to img.src. Three lines of JavaScript:
const img = new Image();
img.src = 'data:image/png;base64,' + base64String;
document.body.appendChild(img);
// If the string already starts with 'data:image/...;base64,'
// — just assign it directly without prepending anything:
img.src = base64StringWithDataUriPrefix;
// METHOD 1: img.src — THE DIRECT APPROACH
Every <img> element treats a data URI as a regular URL. Assign the URI to src and the browser handles the decoding, format detection, and rendering for you.
This works for any image format the browser supports natively: PNG, JPEG, GIF (animated and static), WebP, SVG, BMP, ICO. The Base64 string can come from anywhere — an API response, localStorage, a clipboard paste, a database row.
The MIME type in the data URI matters for older browsers and some embedded WebViews. If you only have a raw Base64 payload (no data: prefix) and you don't know the format, you can either probe the magic bytes (see Method 5) or default to image/png — most browsers will sniff and recover even if the MIME type is wrong.
// Plain HTML — no JavaScript needed if the string is static
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt="preview">
// Dynamic JavaScript — load from a variable, fetch, or input field
function renderBase64Image(base64, mimeType = 'image/png', containerId = 'preview') {
const container = document.getElementById(containerId);
const img = new Image();
img.onload = () => {
container.innerHTML = '';
container.appendChild(img);
};
img.onerror = () => {
container.textContent = 'Invalid image data';
};
// Tolerate both raw Base64 and full data: URIs
img.src = base64.startsWith('data:')
? base64
: `data:${mimeType};base64,${base64}`;
}
renderBase64Image(myBase64String, 'image/jpeg');
// METHOD 2: FILEREADER FOR USER-UPLOADED FILES
If the user uploads an image via <input type="file">, you don't need to manually Base64-encode it. FileReader.readAsDataURL() hands you a complete data URI you can drop straight into img.src:
<input type="file" id="fileInput" accept="image/*">
<img id="preview" alt="">
<script>
document.getElementById('fileInput').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
// event.target.result is a complete data URI
// e.g. 'data:image/png;base64,iVBORw0KGgo...'
document.getElementById('preview').src = event.target.result;
};
reader.onerror = () => {
console.error('FileReader failed:', reader.error);
};
reader.readAsDataURL(file);
});
</script>
// METHOD 3: CANVAS FOR GENERATED OR MODIFIED IMAGES
When you draw or process an image on a <canvas>, canvas.toDataURL() exports the result as a data URI in one call. Useful for: chart screenshots, signature pads, QR codes, watermarks, or anything generated client-side that you want to display or download as an image.
// 1. Draw on canvas
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#00FF41';
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = '#000';
ctx.font = '20px monospace';
ctx.fillText('Hello, Base64!', 20, 100);
// 2. Export to data URI
const pngDataURL = canvas.toDataURL('image/png');
// pngDataURL === 'data:image/png;base64,iVBORw0KGgo...'
// 3. Display directly
const img = new Image();
img.src = pngDataURL;
document.body.appendChild(img);
// JPEG with quality control (0–1)
const jpegDataURL = canvas.toDataURL('image/jpeg', 0.85);
// WebP if supported
const webpDataURL = canvas.toDataURL('image/webp', 0.9);
// METHOD 4: FETCH + BLOB FOR REMOTE BASE64 PAYLOADS
If the Base64 payload is large (over 50 KB) or you'll display it many times, converting it to a Blob URL via URL.createObjectURL() is more memory-efficient than keeping the full data URI in DOM. The blob URL is short (blob:https://example.com/12345-abcde), can be revoked when no longer needed, and avoids the 33% Base64 overhead in DOM strings.
// Convert a Base64 string to a Blob, then to a blob: URL
function base64ToBlobURL(base64, mimeType = 'image/png') {
// Strip the data: prefix if present
const payload = base64.includes(',') ? base64.split(',')[1] : base64;
// Decode Base64 to binary string
const binary = atob(payload);
// Convert binary string to Uint8Array
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
// Wrap in a Blob and create an object URL
const blob = new Blob([bytes], { type: mimeType });
return URL.createObjectURL(blob);
}
// Usage
const blobURL = base64ToBlobURL(myBase64, 'image/jpeg');
const img = new Image();
img.src = blobURL;
document.body.appendChild(img);
// IMPORTANT: free the memory when you're done with the image
img.onload = () => URL.revokeObjectURL(blobURL);
// METHOD 5: AUTO-DETECT FORMAT FROM MAGIC BYTES
If you receive a raw Base64 payload (no data: prefix) and you don't know whether it's PNG, JPEG, GIF, or WebP, peek at the first decoded bytes — every format has a unique signature.
function detectImageFormat(base64) {
// Look at the first ~12 characters of the Base64 string
const head = base64.replace(/^data:[^,]+,/, '').slice(0, 16);
if (head.startsWith('iVBORw0KGgo')) return 'image/png';
if (head.startsWith('/9j/')) return 'image/jpeg';
if (head.startsWith('R0lGOD')) return 'image/gif';
if (head.startsWith('UklGR')) return 'image/webp';
if (head.startsWith('PHN2Zy') ||
head.startsWith('PD94bWw')) return 'image/svg+xml';
if (head.startsWith('Qk')) return 'image/bmp';
if (head.startsWith('AAABAA')) return 'image/x-icon';
return 'application/octet-stream'; // unknown
}
// Build a correct data URI even when the user only gave us the payload
function toDataURI(base64) {
if (base64.startsWith('data:')) return base64; // already complete
const mime = detectImageFormat(base64);
return `data:${mime};base64,${base64}`;
}
// REACT — DISPLAYING BASE64 IMAGES IN COMPONENTS
In React, treat the data URI like any other string prop. Memoize the conversion if the input changes rarely:
import { useMemo } from 'react';
function Base64Image({ base64, mimeType = 'image/png', alt }) {
const src = useMemo(() => {
if (!base64) return '';
return base64.startsWith('data:')
? base64
: `data:${mimeType};base64,${base64}`;
}, [base64, mimeType]);
if (!src) return <div>No image</div>;
return <img src={src} alt={alt} loading="lazy" />;
}
// Usage
<Base64Image
base64={apiResponse.avatar}
mimeType="image/jpeg"
alt="User avatar"
/>
// For large images, prefer blob URLs (Method 4) and useEffect for cleanup:
function LargeBase64Image({ base64 }) {
const [blobURL, setBlobURL] = useState('');
useEffect(() => {
const url = base64ToBlobURL(base64, 'image/png');
setBlobURL(url);
return () => URL.revokeObjectURL(url); // cleanup on unmount
}, [base64]);
return blobURL ? <img src={blobURL} alt="" /> : null;
}
// VUE 3 — COMPOSITION API EXAMPLE
Same idea, Vue syntax:
<script setup>
import { computed } from 'vue';
const props = defineProps({
base64: String,
mimeType: { type: String, default: 'image/png' },
alt: { type: String, default: '' }
});
const src = computed(() => {
if (!props.base64) return '';
return props.base64.startsWith('data:')
? props.base64
: `data:${props.mimeType};base64,${props.base64}`;
});
</script>
<template>
<img v-if="src" :src="src" :alt="alt" loading="lazy" />
<div v-else>No image</div>
</template>
// SVELTE — REACTIVE STATEMENT
<script>
export let base64 = '';
export let mimeType = 'image/png';
export let alt = '';
$: src = base64
? (base64.startsWith('data:') ? base64 : `data:${mimeType};base64,${base64}`)
: '';
</script>
{#if src}
<img {src} {alt} loading="lazy" />
{:else}
<div>No image</div>
{/if}
// ERROR HANDLING — WHAT BREAKS AND WHY
Image loads as a broken-icon placeholder. The Base64 payload is truncated, contains whitespace mid-string, or uses URL-safe characters (-_) inside a standard data URI. Strip whitespace with str.replace(/\s+/g, ''); convert URL-safe back to standard with str.replace(/-/g, '+').replace(/_/g, '/'); and verify the length is correct (Base64 length must be a multiple of 4 with proper = padding).
Image renders but is the wrong format. The data:image/png;base64, prefix says PNG, but the actual bytes are JPEG. The browser sniffs the magic bytes and renders it correctly anyway — but some headless browsers and embedded WebViews don't. Use Method 5 to detect the real format from the bytes.
Image is huge in DOM and the page feels slow. A 1 MB Base64 string in a React component re-renders every state update. Switch to a blob: URL (Method 4) — it's a 50-character string in DOM regardless of image size.
Image works in Chrome but not in Safari. Safari has stricter CSP defaults and rejects data: URIs in some contexts (<a download>, service worker fetches). Test in Safari and fall back to a blob: URL if needed.
The Base64 string itself is invalid. Sometimes the 'Base64' you receive is double-encoded, hex-encoded, or simply corrupted. Paste it into our Base64 to Image decoder first to confirm it's valid before debugging your code.
// PERFORMANCE: data: URI VS blob: URL — A QUICK BENCHMARK
For a 500 KB JPEG displayed in <img>:
• data: URI in src: 668 KB string in DOM (33% Base64 overhead). Browser parses the URI, decodes Base64, decodes JPEG. ~80 ms first paint on a mid-tier laptop.
• blob: URL via URL.createObjectURL: 56-character string in DOM. Browser fetches the in-memory blob, decodes JPEG. ~40 ms first paint. ~50% memory savings.
For a 5 KB icon, the difference is invisible — both render in < 5 ms. The takeaway: under ~50 KB, use whichever is convenient. Above that, prefer blob URLs.
// TESTING YOUR PREVIEW IN ISOLATION
If you suspect a bug, isolate the Base64 string from your application code first. Three quick checks:
1. Paste it into our online tool. Drop the string into the Base64 to Image converter. If the image renders correctly there, the data is good and the bug is in your code. If it doesn't render there either, the data is corrupted upstream.
2. Open it as a URL in a fresh tab. Copy the data URI (including the data:image/png;base64, prefix), paste into a new browser tab, hit Enter. Modern browsers will display the image directly. (Some block data: in the address bar — use the inline test in step 1 if so.)
3. Check the length. A valid Base64 string has length divisible by 4 (after removing whitespace). Padding (=) makes up the difference: 0 to 2 trailing = characters. If your length isn't divisible by 4, the string is truncated or extended.
// Quick validation snippet
function looksLikeValidBase64(str) {
const cleaned = str.replace(/^data:[^,]+,/, '').replace(/\s+/g, '');
if (cleaned.length === 0) return false;
if (cleaned.length % 4 !== 0) return false;
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) return false;
return true;
}
// 30-SECOND CHEAT SHEET
Quickest: assign the data URI to img.src. Done.
From a file upload: FileReader.readAsDataURL(file).
From a canvas: canvas.toDataURL('image/png').
For large or repeated images: convert to a blob and use URL.createObjectURL(blob).
Unknown format: sniff the magic bytes (iVBORw0KGgo = PNG, /9j/ = JPEG, etc.) before building the data URI.
Debugging: paste into the Base64 to Image converter first — eliminates whether the data is the problem.
Related reads: Understanding Data URIs · Base64 image embedding · Base64 in JavaScript: browser & Node