React: Fetch a Binary Image with Axios and Display It as Base64
In previous article, we integrated request headers (like an authentication token) into an axios instance for a React TypeScript application.
In this follow‑up, we’ll see how to fetch binary data (an image) from the backend and convert it to a Base64 string that can be used directly in an <img> tag.
Our scenario: we want to fetch a user’s profile image from a backend endpoint that returns the image as an ArrayBuffer, convert it to a Base64 string, and set it on component state to render the image.
1) State to hold the Base64 image
We’ll store the image as a Base64 string in component state:
import React, { useEffect, useState } from 'react';
// (Optional) If you’re using a custom nominal type for clarity:
type Base64URLString = string;
const [userProfileImage, setUserProfileImage] = useState('' as Base64URLString);
Although the variable name says
Base64URLString, we’re going to use a standard Base64 data URL (data:image/jpeg;base64,....). If you truly require URL‑safe Base64 (Base64URL), you’d replace+with-and/with_. Fordata:..., standard Base64 is correct.
2) Fetch the image as ArrayBuffer using your Axios instance
Assuming you already created an axiosInstance (with auth headers attached in interceptors), request the binary payload by setting responseType: 'arraybuffer':
import axiosInstance from '@/lib/axiosInstance'; // your configured instance
useEffect(() => {
const controller = new AbortController();
axiosInstance
.get(`/user/profile/${props.user.userId}/pic`, {
responseType: 'arraybuffer',
signal: controller.signal, // optional cancellation for unmounts
})
.then(res => {
const base64 = arrayBufferToBase64(res.data);
setUserProfileImage(base64);
})
.catch(err => {
console.error('Failed to load profile image:', err);
setUserProfileImage(''); // fallback or show placeholder
});
return () => controller.abort();
}, [props.user.userId]);
Why
responseType: 'arraybuffer'?
This tells Axios to return raw binary data. We’ll convert that buffer to Base64 in the browser.
3) Convert ArrayBuffer → Base64
Here’s a small utility to convert ArrayBuffer to a Base64 string:
export function arrayBufferToBase64(buffer: ArrayBuffer): string {
// Convert ArrayBuffer to a binary string
let binary = '';
const bytes = new Uint8Array(buffer);
const chunkSize = 0x8000; // avoid call stack overflow for large arrays
for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode(...chunk as any);
}
// Encode the binary string as Base64
return btoa(binary);
}
The one‑liner
btoa(String.fromCharCode(...new Uint8Array(buffer)))works for small buffers. For larger images, it can overflow the call stack. The chunked loop above is safer.
If you must produce URL‑safe Base64 (Base64URL), post‑process like this:
export function toBase64Url(base64: string): string {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
}
For <img> data URLs, use standard Base64 (don’t URL‑safe transform it).
4) Render the image with a Base64 data URL
Once the state holds the Base64 content, render it in an <img> tag. Prefix the string with the appropriate media type and base64 marker:
<img
src={userProfileImage ? `data:image/jpeg;base64,${userProfileImage}` : undefined}
alt="user"
/>
If your backend could return different formats (PNG, JPEG, WebP), consider reading the MIME type from response headers (e.g., res.headers['content-type']) and build the prefix dynamically:
const mime = res.headers['content-type'] || 'image/jpeg';
const dataUrl = `data:${mime};base64,${base64}`;
Full Example (React Component)
import React, { useEffect, useState } from 'react';
import axiosInstance from '@/lib/axiosInstance';
type Base64URLString = string;
function arrayBufferToBase64(buffer: ArrayBuffer): string {
let binary = '';
const bytes = new Uint8Array(buffer);
const chunkSize = 0x8000;
for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode(...chunk as any);
}
return btoa(binary);
}
interface Props {
user: { userId: string };
}
export default function UserProfileImage({ user }: Props) {
const [userProfileImage, setUserProfileImage] = useState('' as Base64URLString);
const [loading, setLoading] = useState(true);
const [err, setErr] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setErr(null);
axiosInstance
.get(`/user/profile/${user.userId}/pic`, {
responseType: 'arraybuffer',
signal: controller.signal,
})
.then(res => {
const base64 = arrayBufferToBase64(res.data);
// Optionally pick up MIME from headers (default to jpeg)
const mime = res.headers['content-type'] || 'image/jpeg';
setUserProfileImage(`data:${mime};base64,${base64}`);
})
.catch(e => {
console.error(e);
setErr('Could not load profile image.');
setUserProfileImage('');
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [user.userId]);
if (loading) return <div>Loading image…</div>;
if (err) return <div>{err}</div>;
return (
<img
src={userProfileImage || undefined}
alt="user"
style={{ width: 120, height: 120, borderRadius: '50%', objectFit: 'cover' }}
/>
);
}
Notes
Object URLs:
If you don’t need Base64 (e.g., you just want to show the image), create a Blob and a temporary object URL:
const blob = new Blob([res.data], { type: res.headers['content-type'] || 'image/jpeg' });
const url = URL.createObjectURL(blob);
setUserProfileImage(url);
// Remember to revoke later:
useEffect(() => () => URL.revokeObjectURL(url), [url]);
Object URLs usually use less memory and avoid the CPU overhead of Base64 encoding.
Caching:
Consider caching the Base64 string (or object URL) if the profile image is used across screens to avoid repeated conversions.



Post Comment