Next.js Image Optimization Broke Our Client Build: The Sharp Fix cover image
Back to Blog
TechnologyPublished 29 June 2026· Updated 22 August 2026· 5 min read

Next.js Image Optimization Broke Our Client Build: The Sharp Fix

When our client's Next.js build passed but images stayed unoptimized in production, we traced it to a missing sharp binary in the standalone output. Here is the explicit declaration and file tracing fix we now bake into every project.

The Incident: Image Optimization Silently Failed in Production

Last month our team shipped a marketing site for a Pune-based SaaS startup using Next.js 15 with output: "standalone". The build passed in CI, the container started, and the homepage loaded. But every <Image> tag served the original .png and .jpg bytes instead of optimized .webp variants. No errors in the logs. No failed builds. Just silently bloated payloads.

The Client Setup and Build Pipeline

The project lives at /apps/marketing inside our monorepo. We build with:

cd /apps/marketing
NODE_OPTIONS=--max-old-space-size=4096 npm run build

Then we copy .next/standalone into a slim node:20-alpine Docker image and run node server.js. The Dockerfile uses multi-stage builds, so only the final stage ships to production.

The Silent Failure: No Errors, Just Unoptimized Images

Next.js logs showed nothing. Chrome DevTools Network tab showed content-type: image/png for assets that should have been image/webp. The /_next/image endpoint returned 200 but with full-size bytes. This is the exact behavior described in the Next.js docs under "Sharp Missing In Production" Sharp Missing In Production.

What We Tried and What Failed

First Attempt: Installing sharp as a Dev Dependency

We added sharp to devDependencies thinking it would be picked up during build:

"devDependencies": {
  "sharp": "^0.34.2"
}

It did not work. The --omit=dev flag in our Docker production stage stripped it out, and even locally the standalone output did not include the native binary.

Second Attempt: Relying on Next.js Optional Dependency Resolution

Next.js 14.2+ pulls sharp in as an optional dependency automatically Fix: Next.js Image Optimization Errors. We assumed this would just work. It did not. Our CI runner uses --omit=optional to trim install size, which stripped sharp before the build even started.

Third Attempt: Copying node_modules Between Architectures

We tried copying node_modules from our macOS build machine into the Linux container. Sharp installed fine locally but failed at runtime with:

Error: Could not load the 'sharp' module using the linux-x64 runtime

The native binary was compiled for darwin-x64, not linux-x64 Next.js Image Optimization in CI.

The Working Approach: Explicit sharp Declaration and File Tracing

Adding sharp to dependencies in package.json

We moved sharp from devDependencies to dependencies so it survives production installs:

"dependencies": {
  "next": "^15.3.4",
  "react": "^19.0.0",
  "react-dom": "^19.0.0",
  "sharp": "^0.34.2"
}

Configuring outputFileTracingIncludes in next.config.js

The standalone output does not automatically trace native binaries like sharp. We added explicit includes:

// next.config.js
const nextConfig = {
  output: "standalone",
  outputFileTracingIncludes: {
    "**/*": [
      "node_modules/sharp/**/*",
      "node_modules/@img/**/*"
    ]
  }
};

module.exports = nextConfig;

This ensures the prebuilt platform binaries ship with the standalone server Fix image optimization in NextJS standalone.

Verifying sharp Loads at Runtime with require('sharp')

Before deploying, we run a quick check inside the container:

docker run --rm our-marketing-app node -e "require('sharp'); console.log('sharp OK')"

If this throws Cannot find module 'sharp', the binary is missing and we rebuild.

Pitfalls We Would Warn an Intern About

The --omit=optional Trap in Docker and CI

Any CI pipeline or Docker stage using --omit=optional will strip sharp. Check your install commands:

# Bad
npm ci --omit=optional

# Good
npm ci

Architecture Mismatches Between Build and Runtime

Building on macOS and deploying to Linux requires platform-specific binaries. Always install sharp on the target platform:

npm install --cpu=x64 --os=linux sharp

Silent Failures vs. Loud Errors in Image Optimization

Next.js does not throw when sharp is missing in production. It silently falls back to serving original images. This makes the bug hard to catch in staging. We now add a runtime verification step to every deployment.

What We Would Do Differently Next Time

Pin sharp Version and Test on Target Platform Early

We will pin sharp to a specific version and test the build on the target platform before merging. Version mismatches between 0.33.x and 0.34.x have caused build failures for other teams GitHub Discussion #66303.

Add Runtime Verification to Deployment Checks

Every deploy now runs:

node -e "const s = require('sharp'); console.log('sharp version:', s.versions.sharp)"

If this fails, the deployment aborts.

Consider a Custom Loader for Static Exports

For projects using output: "export", we skip the built-in optimizer entirely and use a custom loader pointing to Cloudflare Image Resizing or imgix. This avoids the sharp dependency altogether FixDevs.

The Bottom Line

Image optimization failures in Next.js are silent, sneaky, and easy to miss. The fix is simple: declare sharp as a direct dependency, configure outputFileTracingIncludes, and verify at runtime. We have added this checklist to our standard deployment playbook at Agentic Academy Labs.

Next time you see unoptimized images in production, check sharp first. It is almost always the culprit.

Sources you may cite:

Enjoyed this article?

Back to Blog