Breno Baptista

Fixing Blurry Images in "next/image"

Table of Contents

Problem, Investigation and Solution

I noticed that my profile picture appeared blurry when I accessed my blog on my iPad. After searching for a solution, I found that forum users typically recommend using quality={100} or unoptimized={true}, but these options didn't fix my problem.

After an analysis of the DOM elements generated by next/image, I realized that the issue was due to it expecting an image with double the render size. This is to improve quality on high-density displays (like Retina Display for Apple devices).

If you open DevTools and inspect the document, you'll see that the generated <img> tag looks something like this:

<img
  {...}
  srcset="
    {...}/profile.jpg&amp;w=128 1x,
    {...}/profile.jpg&amp;w=256 2x
  "
  src="{...}/profile.jpg&amp;w=256"
>

Notice that it not only uses src but also srcset. The srcset property is used to provide additional sources based on some conditions. In this example, if the display's pixel density is 1x, the image used is 128x128 (my original image). If the pixel density is 2x (as on the iPad), it uses a 256x256 image.

So the problem was actually that I was storing the picture with resolution 128x128 (because that was the resolution to be rendered on my component) but for high-density displays it actually needs to use 256x256. So, I simply had to double the resolution of my image and next/image would handle the rest.

Justification

Vercel mentions this in the documentation, although the explanation is near the end.

Thinking back now, the solution to the problem may seem pretty obvious. It's just that when you create a component and put it as 128x128 you probably won't realize that it needs to be filled with 256x256 because of high-density displays. It's a matter of remembering that these displays exist and need support.

My laptop doesn't have a HiDPI display, so I'm only aware of the problem because I recently bought an Apple product. This makes me wonder how many others are unaware that their images might actually appear blurry on blog posts, social media or portfolios.

What is HiDPI?

4K does not mean HiDPI. We need to consider the physical size of the display, otherwise it can be too dense, making the UI (fonts and icons) too small.

A 4K television is big because it uses 1x pixel density, while a 4K laptop uses 2x pixel density to occupy the same size as a 1080p laptop. In this example, the TV would be LoDPI and the laptop would be HiDPI. 1080p (1920x1080) doubled is 4K (3840x2160), so it makes sense for that physical size. The UI will be the same size, but it'll look crispy.

Example: if you used to watch videos on a monitor at 1080p and you replace it with a 2x pixel density monitor, it should be 4K so you have to start streaming/downloading 4K videos.

Reading recommendation to understand HiDPI

Back to Investigation

Lee Robinson is a well-known developer in the Next.js community that now works directly at Vercel. From his screenshots, he appears to use a MacBook.

While investigating this issue, I checked his blog for clues and noticed that when using next/image, he needed to divide the dimensions by 2 to ensure that images are appropriately sized for 1x and 2x displays.

Example from his repository

<Image
  alt={`Exporting data from Firebase`}
  src={`/images/mysql-planetscale/firebase.png`}
  width={2905 / 2}
  height={1959 / 2}
/>

I checked the image stored in /public and the resolution is 2905x1959.

Intrinsic Size

If you hover over the srcset, you'll see some useful information:

Details on hover

It indicates that the intrinsic size is double the original resolution on 2x pixel density displays. It downloads the double-sized image and renders it in the space intended for 128x128 because the screen has twice the normal pixel density.

Tip for Working With SVG

I noticed a few users on forums complaining about their SVG being blurred. To fix this, instead of using next/image, render the SVG directly in the browser: Working With SVGs in React


Profile picture

Breno Baptista is a software engineer who likes to explore new things every day. He is interested in Linux, open-source software, digital privacy and front-end development.