Next.js ships two ways to display images: the <Image> component from next/image and the plain HTML <img> tag. Choosing the wrong one costs you either performance or flexibility. Here's when to use each.
What next/image does
The <Image> component is a wrapper around <img> that adds:
- Automatic WebP/AVIF conversion: Converts JPEGs and PNGs to WebP or AVIF based on browser support.
- Automatic srcset generation: Creates multiple size variants using the
deviceSizesandimageSizesconfig. - Lazy loading: Below-fold images load only when they enter the viewport (using native
loading="lazy"). - Layout shift prevention: Reserves space via
widthandheightprops, preventing CLS. - Blur placeholder: Shows a low-quality placeholder while the full image loads.
- Priority loading:
priorityprop adds<link rel='preload'>for LCP images.
The trade-offs of next/image
The automation comes with constraints:
- Server-side processing: Images are converted on the server at request time (or cached after first request). This adds cold-start latency and server CPU cost.
- Required dimensions: You must provide
widthandheightprops (or usefillmode). This can be awkward for user-uploaded content with unknown dimensions. - Remote domains must be allowlisted: External image domains require configuration in
next.config.js. - Overhead for static images: Pre-converted WebP images go through the optimization pipeline even when they don't need to be.
When to use next/image
- Dynamic images from a CMS or database (unknown dimensions, various formats)
- User-uploaded images where you can't pre-process
- When you want automatic srcset without manual file generation
- When you need blur placeholders or priority loading with minimal code
When to use a plain img tag
- Pre-optimized images: Images already converted to WebP or AVIF at build time don't benefit from next/image processing.
- Known, fixed dimensions: Static assets where you control the source and have already sized them correctly.
- Avoiding server-side costs: High-traffic pages where the conversion overhead matters and you can pre-process images instead.
- Full HTML control: Complex
<picture>elements with multiple sources, art direction, or custom loading behavior.
Performance comparison
| Scenario | next/image | img tag |
|---|---|---|
| First request (cold) | Slow (server converts) | Fast (pre-converted) |
| Subsequent requests | Fast (edge cache) | Fast (CDN/browser cache) |
| Build time | Fast | Slow (if pre-converting) |
| Server CPU | Non-zero | Zero |
| srcset | Automatic | Manual |
The hybrid approach
Many production Next.js apps use both. Use <Image> for dynamic content (blog post images, product photos from a CMS) and plain <img> for static assets (hero images, icons, illustrations) that you've pre-optimized.
For pre-converting static images to WebP, use Picovert's converter or integrate Sharp into your build pipeline. See our full comparison in the next/image vs pre-convert article.
Common mistakes
- Missing sizes prop: Without
sizes, Next.js defaults to100vwand loads unnecessarily large images on desktop. Always setsizesto match the actual rendered size. - Not setting priority on LCP images: The above-fold hero image should have
priorityset to avoid lazy-loading the most important image on the page. - Using fill mode without a positioned parent:
fillrequires the parent to haveposition: relativeand explicit dimensions.