Skip to content

Strapi Image Optimization — Formats, Compression, and Responsive Images

DodaTech Updated 2026-06-28 8 min read

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

  1. Using original images everywhere. Serving the 4000px original image when the display size is 300px wastes bandwidth and slows pages. Always use responsive formats.

  2. 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.

  3. Not setting quality limits. Default compression may not be optimal. Configure quality settings in the upload plugin for your specific use case.

  4. Forgetting alt text for Accessibility. Every image needs descriptive alternative text. Make alt text a required field in your media content type.

  5. 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

  1. 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.

  2. How do you select the best image format in the frontend? Answer: Use the formats object in the media API response. Check available formats and select the one closest to but not exceeding the display width. Use srcSet for browser-driven selection.

  3. 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.

  4. 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 srcSet and sizes attributes, (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

Does Strapi convert images to WebP automatically?

No, Strapi does not convert images to WebP during upload. You need Cloudinary (which offers f_auto for automatic format selection), a custom Sharp middleware, or CDN-level optimization (Cloudflare, Imgix).

How do breakpoints work in Strapi?

Breakpoints define the widths at which Strapi generates resized copies of uploaded images. When you upload a 4000px image, Strapi creates copies at each breakpoint width (e.g., 1000px, 750px, 500px, 245px).

Can I add custom breakpoints without restarting Strapi?

No, breakpoint configuration is loaded at startup. Changes to config/plugins.js require a server restart. Existing uploads are not affected by breakpoint changes.

How does Cloudinary's f_auto parameter work?

f_auto checks the browser's Accept header and serves the best supported format. Chrome and Firefox get WebP. Safari gets JPEG. This happens automatically without any frontend changes.

Should I store original high-resolution images?

Yes. Always store the original full-resolution image. Responsive formats are derived from it. If you delete the original, you cannot regenerate responsive formats at different breakpoints later.

Mini Project

Your task: Build an image-optimized frontend gallery.

  1. Configure Strapi with custom breakpoints: thumbnail (150px), small (400px), medium (800px), large (1600px).
  2. Upload 10 high-resolution images (4000px+).
  3. 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 srcSet and sizes for responsive image selection
    • Implements lazy loading for images below the fold
    • Shows a blur-up placeholder while the image loads
  4. 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:

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro