Strapi Image Optimization — Formats, Compression, and Responsive Images
In this tutorial, you will learn how Strapi handles image optimization — including automatic format conversion, compression, responsive image generation through breakpoints, and how to serve optimized images to improve page load times.
What You'll Learn
- How Strapi generates responsive image formats (thumbnail, small, medium, large)
- How to configure image breakpoints and formats
- How WebP and AVIF formats improve performance
- How Cloudinary provides automatic optimization
- How to reference optimized images in your frontend
- Image performance best practices
Why It Matters
Images are the largest contributor to page weight. An unoptimized 5MB hero image can take 10 seconds to load on a mobile connection. Strapi's image optimization pipeline reduces file sizes by 60-80% through format conversion and compression, and generates responsive sizes so mobile devices never download desktop-sized images.
Real-World Use
A recipe site with high-resolution food photos (4000x3000 pixels, 8MB each) uses Strapi's responsive image generation. The frontend selects the small format (500px wide, ~50KB) for the thumbnail grid and the large format (1000px wide, ~200KB) for the detail page. Mobile users never download the full 8MB original. Page load time drops from 8 seconds to 1.5 seconds.
Learning Path
flowchart LR A["Upload Providers"] --> B["Image Optimization
-- You are here"]:::current B --> C["File Management"] C --> D["File Security"] classDef current fill:#4945ff,color:#fff,stroke-width:2px
How Strapi Processes Images
When you upload an image, Strapi automatically generates multiple responsive versions based on configured breakpoints.
// Default breakpoints:
// thumbnail: 245px width
// small: 500px width
// medium: 750px width
// large: 1000px width
// API response for an uploaded image:
{
"id": 1,
"name": "pizza.jpg",
"width": 4000,
"height": 3000,
"size": 2048576, // 2MB original
"formats": {
"thumbnail": { "url": "/uploads/thumbnail_pizza_abc123.jpg", "width": 245, "height": 184, "size": 15360 },
"small": { "url": "/uploads/small_pizza_abc123.jpg", "width": 500, "height": 375, "size": 51200 },
"medium": { "url": "/uploads/medium_pizza_abc123.jpg", "width": 750, "height": 563, "size": 112640 },
"large": { "url": "/uploads/large_pizza_abc123.jpg", "width": 1000, "height": 750, "size": 204800 }
},
"url": "/uploads/pizza_abc123.jpg", // Original
"provider": "local"
}
Your frontend can select the most appropriate format based on the display size and device.
Configuring Breakpoints
Customize breakpoints in the upload plugin configuration:
// config/plugins.js
module.exports = {
upload: {
config: {
breakpoints: {
xlarge: 1920,
large: 1000,
medium: 750,
small: 500,
thumbnail: 245,
// You can add custom breakpoints
"card-image": 400,
"banner": 1600,
},
},
},
};
Consider your frontend design when configuring breakpoints. There is no need to generate sizes you never display. Each breakpoint creates a separate file in storage.
Selecting Formats in the Frontend
// React component that selects the right image size
function RecipeImage({ image, sizes }) {
// image is the Strapi media object from the API
const formats = image.attributes.formats;
// Choose the best format based on display size
function getBestImage(containerWidth) {
if (containerWidth <= 245 && formats?.thumbnail) return formats.thumbnail;
if (containerWidth <= 500 && formats?.small) return formats.small;
if (containerWidth <= 750 && formats?.medium) return formats.medium;
if (formats?.large) return formats.large;
return { url: image.attributes.url }; // Fallback to original
}
const bestImage = getBestImage(sizes?.width || 750);
return (
<img
src={bestImage.url}
alt={image.attributes.alternativeText || ""}
loading="lazy"
width={bestImage.width}
height={bestImage.height}
// Use srcSet for browser-driven selection
srcSet={`
${formats?.thumbnail?.url || ""} 245w,
${formats?.small?.url || ""} 500w,
${formats?.medium?.url || ""} 750w,
${formats?.large?.url || ""} 1000w,
${image.attributes.url} ${image.attributes.width}w
`}
sizes="(max-width: 768px) 100vw, 50vw"
/>
);
}
Using srcSet and sizes lets the browser choose the optimal image size based on the device and viewport.
WebP and AVIF Format Conversion
Strapi does not convert images to WebP or AVIF by default. You need additional tools or providers.
// Option 1: Cloudinary (automatic format conversion)
// Cloudinary supports f_auto which serves WebP when the browser supports it
"url": "https://res.cloudinary.com/mycloud/image/upload/f_auto,q_auto/v1/pizza.jpg"
// The f_auto parameter automatically converts to WebP or AVIF
// Option 2: Server-side conversion middleware
// Install sharp and create a middleware
// src/middlewares/image-optimizer.js
const sharp = require("sharp");
module.exports = (config, { strapi }) => {
return async (ctx, next) => {
await next();
// Check if response is an image
if (ctx.response.type?.startsWith("image/") && ctx.url.startsWith("/uploads/")) {
const accept = ctx.request.headers.accept || "";
// If browser supports WebP, convert
if (accept.includes("image/webp")) {
const buffer = ctx.body;
const webpBuffer = await sharp(buffer).webp({ quality: 80 }).toBuffer();
ctx.body = webpBuffer;
ctx.set("Content-Type", "image/webp");
ctx.set("Content-Length", webpBuffer.length.toString());
}
}
};
};
// Option 3: Use a CDN with automatic optimization
// Cloudflare, Imgix, and ImageKit all provide automatic image optimization
// when images are served through their CDN
Compression Settings
Control image quality and compression:
// config/plugins.js
module.exports = {
upload: {
config: {
breakpoints: {
large: 1000,
medium: 750,
small: 500,
},
// Sharp options (Strapi 5)
sharp: {
jpeg: { quality: 80, progressive: true },
png: { quality: 80, compressionLevel: 9 },
webp: { quality: 75, lossless: false },
tiff: { quality: 80 },
},
},
},
};
Quality settings are a trade-off between file size and visual fidelity. 80% quality is a good starting point for photographs. For PNG graphics, higher compression levels reduce file size without quality loss.
Lazy Loading and Progressive Images
Implement lazy loading in your frontend for additional performance:
// Native lazy loading (browser support: modern browsers)
<img src="image.jpg" loading="lazy" alt="..." />
// Blur-up placeholder technique
// 1. Strapi generates a tiny thumbnail (10px wide)
// 2. Frontend shows the blurry thumbnail immediately
// 3. Full image loads in the background
// Example with blur-up:
async function getBlurPlaceholder(image) {
// Generate a tiny base64 placeholder
const thumbnailUrl = image.formats?.thumbnail?.url || image.url;
const response = await fetch(thumbnailUrl);
const blob = await response.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
CDN Integration for Images
Serve images through a CDN for global performance:
// With Cloudinary or S3 + CloudFront, images are automatically served from CDN
// With local provider, use a CDN that caches your uploads directory:
// nginx configuration for caching:
// location /uploads/ {
// proxy_pass http://localhost:1337;
// proxy_cache STATIC;
// proxy_cache_valid 200 30d;
// add_header Cache-Control "public, max-age=2592000, immutable";
// }
// Frontend: prepend CDN URL to image paths
const CDN_URL = process.env.NEXT_PUBLIC_CDN_URL || "";
const imageUrl = `${CDN_URL}${image.attributes.url}`;
Common Mistakes
Using original images everywhere. Serving the 4000px original image when the display size is 300px wastes bandwidth and slows pages. Always use responsive formats.
Generating unnecessary breakpoints. Each breakpoint creates a file and consumes storage. Only generate sizes your frontend actually uses. If you never display images at 1920px, do not generate that breakpoint.
Not setting quality limits. Default compression may not be optimal. Configure quality settings in the upload plugin for your specific use case.
Forgetting alt text for Accessibility. Every image needs descriptive alternative text. Make alt text a required field in your media content type.
Not using modern image formats. JPEG and PNG are outdated for web use. WebP provides 25-35% better compression. AVIF provides 50% better compression than JPEG.
Practice Questions
What responsive image formats does Strapi generate by default? Answer: Thumbnail (245px), small (500px), medium (750px), and large (1000px). These are generated automatically for image uploads.
How do you select the best image format in the frontend? Answer: Use the
formatsobject in the media API response. Check available formats and select the one closest to but not exceeding the display width. UsesrcSetfor browser-driven selection.Why is WebP better than JPEG for web images? Answer: WebP provides 25-35% smaller file sizes than JPEG at the same quality level. It also supports transparency (unlike JPEG) and animation.
Challenge: Implement a complete image optimization pipeline: (1) Configure custom breakpoints that match your site's design (e.g., card: 400px, banner: 1600px), (2) Set quality settings for JPEG at 75% and WebP at 70%, (3) Build a React component that selects the optimal image format using
srcSetandsizesattributes, (4) Implement lazy loading with a blur-up placeholder, (5) Test with various viewport sizes and network conditions to verify the correct image size is served.
FAQ
Mini Project
Your task: Build an image-optimized frontend gallery.
- Configure Strapi with custom breakpoints: thumbnail (150px), small (400px), medium (800px), large (1600px).
- Upload 10 high-resolution images (4000px+).
- Build a frontend gallery page that:
- Shows a grid of thumbnails using the 150px format
- Opens a lightbox with the 800px format on click
- Uses
srcSetandsizesfor responsive image selection - Implements lazy loading for images below the fold
- Shows a blur-up placeholder while the image loads
- Compare page load time with and without responsive images using browser DevTools.
What's Next
Now that you understand image optimization, proceed to File Management to learn about folder organization, file replacement, and the media library API. After that, secure your files with File Security.
Related lessons:
- Node.js Image Processing — How Sharp works
- REST API — Media library endpoints
- WordPress Image Optimization — Compare image handling
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro