Fix Next.js Image Optimization with Strapi CMS: Configuration and Common Errors

Topic: nextjs-image-optimization-lcp-improvementUpdated 7/17/2026

Quick Answer

  • Conclusion: Use Next.js next/image component with Strapi CMS to automatically optimize images (WebP/AVIF conversion, responsive srcSet, lazy loading) and improve LCP/CLS Core Web Vitals without manual optimization scripts.
  • First checks: Ensure next.config.js has images.remotePatterns configured with your Strapi hostname, and that every <Image> component includes required src, alt, width, and height props.
  • Minimal fix for remote images: Add your Strapi domain to remotePatterns in next.config.js and restart the dev server. Example: { protocol: 'https', hostname: 'your-strapi-instance.com', pathname: '/uploads/**' }.
  • Version boundary: Works with Next.js 13+ (App Router) and Next.js 12+ (Pages Router). Requires next build before next start in production.

What Problem It Solves

When using Strapi CMS, images uploaded by editors are often large, unoptimized files (JPEG/PNG at full resolution). Serving these directly causes:

  • High Largest Contentful Paint (LCP) – large image files take too long to download
  • Cumulative Layout Shift (CLS) – images push content down as they load
  • Wasted bandwidth – mobile users download desktop-sized images

Next.js next/image solves all three by automatically converting formats, resizing to device-specific dimensions, lazy-loading offscreen images, and reserving space via explicit width/height props.

Minimal Working Configuration

1. Configure next.config.js

JS
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'your-strapi-instance.com',
        port: '',
        pathname: '/uploads/**',
      },
    ],
  },
};

module.exports = nextConfig;

Replace your-strapi-instance.com with your actual Strapi domain. For local development with Strapi on localhost:1337, use:

JS
{
  protocol: 'http',
  hostname: 'localhost',
  port: '1337',
  pathname: '/uploads/**',
}

2. Use the Component

JSX
import Image from 'next/image';

export default function BlogPost({ post }) {
  return (
    <Image
      src={post.coverImage.url}   // e.g. "https://your-strapi-instance.com/uploads/hero-123.jpg"
      alt={post.coverImage.alternativeText || 'Blog cover'}
      width={1200}
      height={630}
      priority                    // Add for above-the-fold images
      placeholder="blur"          // Optional: requires blurDataURL for remote images
      quality={85}                // Optional: default is 75
    />
  );
}

Parameters and Environment Variables

ParameterRequiredDefaultDescription
srcYesImage source URL. For remote images, must be absolute (http/https). For local images in public/, use relative path.
altYesAlternative text for accessibility and SEO.
widthYesExplicit width in pixels. Prevents layout shift (CLS).
heightYesExplicit height in pixels. Prevents layout shift (CLS).
placeholderNo'empty''blur' shows a blurred placeholder until the image loads. Requires blurDataURL for remote images.
qualityNo75Image compression quality (1–100). Lower values reduce file size.
priorityNofalseWhen true, the image is preloaded (no lazy loading). Essential for LCP images.

Root Cause Analysis

The most common failure point is the remotePatterns configuration. Next.js blocks optimization for any external image host not explicitly whitelisted. This is a security measure to prevent SSRF attacks where an attacker could trick your server into processing arbitrary external images.

When remotePatterns is missing or incorrect, Next.js throws:

Error: Invalid src prop (https://example.com/image.jpg) on `next/image`,
hostname "example.com" is not configured under images in your `next.config.js`

The second major issue is missing width and height props. Unlike a standard <img> tag, next/image requires explicit dimensions to calculate aspect ratio and prevent CLS. If you omit them, the component will warn or error.

Common Errors and Fixes

Error: Hostname not configured

Error message: Invalid src prop ... hostname "..." is not configured under images in your next.config.js

Fix: Add the exact hostname to images.remotePatterns in next.config.js. Include the correct protocol, hostname, and optionally pathname and port.

JS
// Correct
remotePatterns: [{ protocol: 'https', hostname: 'cdn.strapi.io', pathname: '/uploads/**' }]

// Wrong – too broad, security risk
remotePatterns: [{ protocol: 'https', hostname: '**' }]

Error: Image Optimization API not available

Error message: Image Optimization API is not available in next start mode

Fix: Run next build before next start. The image optimization pipeline requires a production build. If you're using a custom server, ensure it calls next({ dev: false }).

Warning: Width or height modified but not the other

Warning: Image with src "..." has either width or height modified, but not the other

Fix: When applying CSS size changes, always set both width and height, or use object-fit. Alternatively, use the fill prop with a positioned parent container:

JSX
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image src={url} alt="Hero" fill style={{ objectFit: 'cover' }} />
</div>

Error: Invalid URL

Error message: The URL must be absolute and start with http:// or https://

Fix: Ensure src is a full absolute URL for remote images. For local images in public/, use a relative path like /images/logo.png (no leading http).

Production Notes and Security Checks

1. Lock down remotePatterns

Never use wildcard patterns like hostname: '**' in production. Always specify the exact hostname and path prefix. This prevents attackers from making your server optimize arbitrary external images (a form of SSRF).

2. Mitigate CPU spikes

Image optimization uses Sharp, which is CPU-intensive. On first request (cache miss), the server processes the image. To reduce impact:

  • Set images.minimumCacheTTL to a reasonable value (e.g., 3600 seconds)
  • Consider using an external image CDN (Cloudflare Images, Imgix) for high-traffic sites
  • Set CPU limits in containerized deployments (Kubernetes/Docker)

3. Cache concurrency

The default image cache is filesystem-based (.next/cache/images). Under high concurrency, multiple requests for the same uncached image may cause file locking. For serverful deployments, use a shared cache (Redis, S3) or a custom loader.

4. Access control

next/image does not enforce authentication. If images require access control (e.g., paid content), implement a proxy API route that validates permissions before serving the image.

5. Always use HTTPS in production

Set protocol: 'https' in remotePatterns. Using http in production triggers mixed-content warnings and exposes traffic to MITM attacks.

FAQ

Q: My LCP didn't improve after switching to next/image. What's wrong?

A: Check these in order:

  1. Did you add priority to the hero image? Without it, Next.js lazy-loads by default.
  2. Is the Strapi hostname correctly listed in remotePatterns? If not, Next.js serves the original unoptimized image.
  3. Are width and height set to the actual display size? Setting them too large generates oversized images.
  4. Is the image cache warm? First visit is slower; subsequent visits use cached optimized versions.

Q: How do I add blur placeholder for remote Strapi images?

A: Remote images require a manual blurDataURL. Generate it server-side:

JSX
// In getServerSideProps or API route
import sharp from 'sharp';

const response = await fetch(imageUrl);
const buffer = await response.buffer();
const { data } = await sharp(buffer)
  .resize(10)
  .jpeg({ quality: 30 })
  .toBuffer({ resolveWithObject: true });
const blurDataURL = `data:image/jpeg;base64,${data.toString('base64')}`;

// In component
<Image src={imageUrl} placeholder="blur" blurDataURL={blurDataURL} alt="..." />

This adds server load, so use it only for critical above-the-fold images.

Q: My Strapi images are on AWS S3. How do I configure next/image?

A: Add the S3 bucket domain to remotePatterns:

JS
remotePatterns: [
  {
    protocol: 'https',
    hostname: 'your-bucket.s3.us-east-1.amazonaws.com',
    pathname: '/uploads/**',
  },
],

For better performance, use a custom loader that points to a CDN (CloudFront + Lambda@Edge) or a third-party image service:

JS
// next.config.js
images: {
  loader: 'custom',
  loaderFile: './lib/imageLoader.js',
}

// ./lib/imageLoader.js
export default function myImageLoader({ src, width, quality }) {
  return `https://your-cdn-domain.com/${src}?w=${width}&q=${quality || 75}`;
}

Ensure the S3 bucket policy allows read access from your Next.js server or CDN.

Official References

Related Guides