How to Fix: Image component shows compression artifacts, and increases the image’s size, on very small pngs

5 min read

Next.js Image optimization can make tiny PNGs look worse and weigh more, which feels backward until you look at how the optimization pipeline treats very small assets. In this case, the Image component is re-encoding a tiny source PNG into an output format and size profile that introduces visible compression artifacts while also producing a larger file than the original.

Understanding the Root Cause

The issue appears when a very small PNG is passed through the Next.js image optimization pipeline. For normal photos, optimization usually helps. For tiny graphics, icons, pixel art, badges, or other low-dimension PNGs, the pipeline can have the opposite effect.

Here is what is happening technically:

  • The original PNG is already extremely small and often highly efficient for its content.
  • The next/image optimizer may resize, recompress, or transcode the file depending on request parameters, browser support, and quality settings.
  • If the browser accepts a modern format such as WebP, Next.js may generate a WebP derivative. For tiny PNGs, that derivative can be larger than the source because encoder overhead becomes significant relative to the image data.
  • Lossy compression on small, high-contrast images can create visible artifacts, especially around sharp edges, transparency boundaries, or pixel-perfect artwork.
  • The cost of optimization metadata, format conversion, and resizing can outweigh any byte savings for very small assets.

This is why the optimized image can be both visibly worse and larger on disk or over the network.

In short, the bug is not that the browser suddenly renders PNG badly. The problem is that automatic optimization is not always appropriate for tiny raster assets.

Step-by-Step Solution

The most reliable fix is to skip optimization for tiny PNGs. If an image is already small and visually sensitive, serve it as-is.

1. Use the unoptimized prop for tiny PNGs

If you know a specific image should not go through the optimization pipeline, set unoptimized on that image.

import Image from 'next/image'

export default function TinyIcon() {
  return (
    <Image
      src="/tiny.png"
      alt="Tiny PNG"
      width={24}
      height={24}
      unoptimized
    />
  )
}

This tells Next.js to serve the original file directly, avoiding recompression and avoiding the artifact issue.

2. Use a plain <img> tag when optimization adds no value

For decorative micro-assets, logos, or UI sprites, a native image tag can be the simplest and most predictable choice.

export default function TinyIcon() {
  return (
    <img
      src="/tiny.png"
      alt="Tiny PNG"
      width="24"
      height="24"
    />
  )
}

This is especially useful when you do not need responsive resizing, lazy loading behavior from next/image, or automatic format negotiation.

3. Conditionally disable optimization for very small assets

If your application renders many images and only some are affected, add a small abstraction that disables optimization for tiny PNGs.

import Image from 'next/image'

function SmartImage({ src, alt, width, height, ...props }) {
  const isTinyPng =
    typeof src === 'string' &&
    src.endsWith('.png') &&
    width <= 48 &&
    height <= 48

  return (
    <Image
      src={src}
      alt={alt}
      width={width}
      height={height}
      unoptimized={isTinyPng}
      {...props}
    />
  )
}

export default SmartImage

This keeps optimization enabled for larger images while protecting the image classes most likely to degrade.

4. If needed, review global image configuration

In some setups, you may want to reduce surprising output by reviewing your next.config.js image settings. While this does not always fully solve the tiny-PNG problem, it helps control the broader optimization behavior.

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    formats: ['image/avif', 'image/webp'],
  },
}

module.exports = nextConfig

Be careful here: changing allowed formats alone does not guarantee better output for tiny PNGs. The more dependable fix remains opting out per image.

5. Verify the result correctly

After applying the fix, compare:

  • Visual sharpness at actual rendered size
  • Network response size in browser DevTools
  • Whether the response is the original PNG or a transformed asset

To inspect this, open your app in the browser, load the page, and compare the request for the original file against the optimized /_next/image request. If unoptimized is working, the transformed request should no longer be used for that image.

Common Edge Cases

  • Pixel art and crisp UI icons: These are especially sensitive to lossy recompression. Even mild optimization can blur sharp transitions.
  • Transparent PNGs: Compression artifacts often become more noticeable around alpha edges.
  • High-DPI rendering: A tiny image displayed larger via CSS can look even worse after recompression. Always compare at real rendered dimensions.
  • Remote images: If the PNG is fetched from a remote source and routed through Next.js optimization, the same issue can occur. Consider bypassing optimization for those assets too.
  • SVG alternatives: If the asset is really an icon or logo, converting it to SVG may eliminate the problem entirely, provided the artwork is vector-friendly.
  • Incorrect width and height props: If dimensions do not match the asset’s intended display size, resizing can introduce additional degradation beyond compression artifacts.

FAQ

Why does Next.js increase the file size of a tiny PNG?

Because optimization has overhead. For very small images, the container, metadata, encoding strategy, and format conversion can produce a file larger than the already-efficient original PNG.

Should I stop using next/image for all PNGs?

No. next/image is still valuable for many images, especially larger photos and responsive assets. The problem is most noticeable with very small PNGs, UI graphics, and pixel-sensitive artwork.

Is unoptimized the best fix?

For this specific issue, yes, it is usually the cleanest fix. It preserves the original file exactly and avoids both the size regression and the visual artifacting introduced by the optimization pipeline.

If you are reproducing the issue from the linked repository, the practical resolution is straightforward: identify the tiny PNGs that regress under next/image, and serve those assets without optimization. That preserves image quality and usually restores the smaller original file size.

Leave a Reply

Your email address will not be published. Required fields are marked *