ScreenshotNeo

BlogHow-to

How to Add Images in Next.js

Learn how to add local and remote images with next/image, prevent layout shift, configure remote hosts, and choose responsive loading settings.

By the ScreenshotNeo team1 October 20269 min read

next/image is the built-in way to add optimized images in Next.js. Import it, provide meaningful alt text, and give remote images explicit dimensions or a correctly sized positioned container.

import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="/photo.jpg"
      alt="Description of the photo"
      width={800}
      height={600}
    />
  )
}

The example assumes photo.jpg is in your app’s public directory. The same component supports static imports and approved remote URLs. Its dimensions reserve the correct aspect ratio before the file arrives, while CSS controls the final rendered size.

1. Add a local image from public

Put an asset such as public/photo.jpg in the project. Reference it with a root-relative path; do not include /public in the URL.

import Image from 'next/image'

export default function Profile() {
  return (
    <main>
      <h1>Profile</h1>
      <Image
        src="/photo.jpg"
        alt="A mountain lake at sunrise"
        width={1200}
        height={800}
      />
    </main>
  )
}

Use an empty alt value for a decorative image when that matches your project’s accessibility convention. For meaningful content, write alternative text that can replace the image without changing the page’s meaning. The Next.js documentation explains that alt describes the image for screen readers and search engines.

2. Use a static import

A static import is useful when the file is part of your source tree. Next.js can read metadata from supported static image files and can provide blur data automatically for supported JPG, PNG, WebP, and AVIF imports.

import Image from 'next/image'
import hero from './hero.jpg'

export default function Home() {
  return (
    <Image
      src={hero}
      alt="People collaborating around a table"
    />
  )
}

You can still set CSS classes or an explicit display size. The intrinsic dimensions establish the aspect ratio; CSS determines how large the image appears in the layout.

3. Configure a remote image

Remote files are not available to Next.js at build time. Supply width and height yourself, then allow the exact source with images.remotePatterns in next.config.js.

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.example.com',
        port: '',
        pathname: '/photos/**',
        search: '',
      },
    ],
  },
}

module.exports = nextConfig
import Image from 'next/image'

export default function ArticleImage() {
  return (
    <Image
      src="https://images.example.com/photos/cover.jpg"
      alt="A red bicycle beside a brick wall"
      width={1200}
      height={800}
      sizes="(max-width: 768px) 100vw, 50vw"
    />
  )
}

Keep each pattern narrow. Omitted matching fields can act as broad wildcards, potentially allowing URLs you did not intend to fetch. Match the protocol, hostname, path, and query-string policy required by your application. The older domains setting is deprecated in favor of remotePatterns.

Remote URLs with query strings

If the image provider requires a query string, configure the expected search policy instead of leaving the pattern broader than necessary. A URL that differs in protocol, host, path, or permitted search parameters will be rejected by the image component.

4. Prevent layout shift with dimensions

width and height communicate the source aspect ratio. They do not force the browser to render the image at that exact CSS size.

<Image
  src="/wide-banner.jpg"
  alt="A wide banner over a city skyline"
  width={1600}
  height={500}
  className="banner"
/>
.banner {
  width: 100%;
  height: auto;
  display: block;
}

If you know only one dimension, calculate the other from the source aspect ratio. Supplying inaccurate dimensions reserves the wrong space and can distort the result or cause a visible correction when the image loads.

5. Make an image fill a responsive container

Use fill when the image should occupy a positioned parent. The parent needs a deliberate layout and height; otherwise the image has no useful box to fill.

import Image from 'next/image'

export default function Card() {
  return (
    <article className="card">
      <div className="media">
        <Image
          src="/card.jpg"
          alt="A notebook and coffee on a desk"
          fill
          sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
          style={{ objectFit: 'cover' }}
        />
      </div>
      <h2>Workspace</h2>
    </article>
  )
}
.media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

.card {
  max-width: 32rem;
}

For responsive CSS sizing or fill, add an accurate sizes value. It tells the browser the image’s expected rendered width at each breakpoint. Without it, the browser assumes 100vw and may download a larger source than necessary.

6. Choose responsive loading with sizes

A fixed-width image can omit sizes when its rendered width is fixed. Add it when the layout changes across breakpoints.

<Image
  src="https://images.example.com/photos/photo.jpg"
  alt="A person walking through a forest"
  width={1200}
  height={800}
  sizes="(max-width: 768px) 100vw, 50vw"
/>

The value should describe the CSS layout, not the source file’s dimensions. For example, a two-column desktop article image might use 50vw, while the same image becomes full width below 768 pixels.

7. Add a blur placeholder

Use placeholder="blur" with a small blurDataURL for remote or dynamic images. Supported static imports can receive blur data automatically.

import Image from 'next/image'

export default function RemoteHero() {
  return (
    <Image
      src="https://images.example.com/photos/hero.jpg"
      alt="A coastline under cloudy skies"
      width={1600}
      height={900}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."
      sizes="100vw"
    />
  )
}

Keep the blur data small. Do not set a blur placeholder when you do not have suitable blur data for the source.

8. Decide when to load the image

Images are lazy-loaded by default. Keep that behavior for below-the-fold content. An image that is likely to be the above-the-fold Largest Contentful Paint element may justify earlier loading.

<Image
  src="/home-hero.jpg"
  alt="A product dashboard on a laptop"
  width={1440}
  height={900}
  sizes="100vw"
  preload
/>

Check the API for the Next.js version installed in your project. Starting with Next.js 16, priority is deprecated in favor of preload. The Pages Router guidance also describes loading="eager" or fetchPriority="high" as alternatives that may fit many above-the-fold cases. Use early loading selectively; making every image eager increases contention for network and decoding resources.

9. Handle SVG, animated, and authenticated sources

SVGs and animated images may not benefit from optimization. The component supports unoptimized when you need the source served without the default optimization path.

<Image
  src="/diagram.svg"
  alt="Architecture diagram"
  width={900}
  height={500}
  unoptimized
/>

Enabling SVG optimization requires security precautions. An image source that requires authentication is another case to evaluate carefully because the default optimizer does not forward authentication headers. The official guidance suggests considering unoptimized for authenticated sources.

10. Complete App Router example

// app/gallery/page.tsx
import Image from 'next/image'
import localPhoto from './local-photo.jpg'

export default function GalleryPage() {
  return (
    <main>
      <h1>Gallery</h1>

      <Image
        src={localPhoto}
        alt="A local photograph of a forest path"
        sizes="(max-width: 768px) 100vw, 60vw"
      />

      <Image
        src="https://images.example.com/photos/remote.jpg"
        alt="A remote photograph of a coastal trail"
        width={1200}
        height={800}
        sizes="(max-width: 768px) 100vw, 40vw"
      />
    </main>
  )
}

11. Troubleshooting

Symptom Cause Fix
“Invalid src prop” or an unconfigured host error The remote hostname, protocol, path, or query policy is not allowed. Add a narrow matching entry to images.remotePatterns, then restart the development server.
The page jumps when the image appears Dimensions are missing or inaccurate. Provide the source aspect ratio with width/height, or give a fill parent a stable aspect ratio.
The image downloads too large a file A responsive image has no accurate sizes value, so the browser assumes 100vw. Describe the rendered width at each breakpoint.
A fill image has zero height or escapes its card The parent is not positioned or has no height. Set the parent to position: relative and define height or aspect-ratio.
Blur placeholder fails for a remote image Remote images do not receive automatic blur data. Supply a compact blurDataURL together with placeholder="blur".
An authenticated image returns an unauthorized response The default optimizer does not forward authentication headers. Use a public image URL, a controlled loader, or evaluate unoptimized for the authenticated source.
The image is missing from the page The public path includes /public or has the wrong case. Use /photo.jpg for public/photo.jpg and verify filename casing.
The expected loading prop behaves differently Image APIs vary by installed Next.js version. Check the version-specific Image reference; in Next.js 16 use preload instead of deprecated priority.

12. Performance, reliability, and cost considerations

  • Use local or static imports when the asset is shipped with the application and remote configuration is unnecessary.
  • Allowlist only the remote hosts and paths the application needs. Narrow patterns reduce accidental source access and configuration surprises.
  • Set sizes from the actual layout so responsive images do not fetch unnecessarily large candidates.
  • Reserve eager loading or preload for the image that benefits from arriving early. Keep other images lazy.
  • Use accurate dimensions or a stable fill container to avoid layout shift.
  • Do not assume optimization can access private origin headers. Plan authentication handling before choosing the default optimizer.
  • For SVG and animated files, compare the value of optimization with serving the original using unoptimized, while applying appropriate SVG security controls.

Next.js documentation describes a configurable default maximum response body size of 50 MB. Treat that as an implementation setting, not a general performance benchmark.

13. Verify an image implementation

  1. Open the page at mobile and desktop widths.
  2. Confirm the image keeps its intended aspect ratio before and after loading.
  3. Check that remote URLs match the narrow remotePatterns rule.
  4. Inspect the rendered width and confirm sizes describes it.
  5. Verify meaningful images have useful alt text and decorative images follow the project’s empty-alt convention.
  6. Confirm only the intended above-the-fold image uses early loading.
  7. Test slow or failed remote responses so the surrounding layout remains usable.

Or skip the browser setup

If you need a rendered screenshot of a Next.js page for documentation, previews, visual checks, or an AI workflow, ScreenshotNeo returns an image or PDF from one GET request. Its cleanup steps accept cookie and consent banners before capture and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled.

See the ScreenshotNeo API documentation for all options. This cURL request captures a page as WebP:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo reports whether a response was clean and whether it was billed through the X-Page-Verdict and X-Billed headers. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots each month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently asked questions

Do I need next/image for every image?

Use it when you want the built-in image component and its optimization behavior. Some assets, such as SVGs or animated files, may be better served with unoptimized after reviewing their security and delivery needs.

Should remote images use width and height?

Yes. Remote files are unavailable at build time, so provide their dimensions or use fill inside a stable, positioned container.

Why does my responsive image look blurry?

Check that the source dimensions are accurate and that sizes describes the displayed width. An incorrect value can make the browser choose an unsuitable source candidate.

When should I use a blur placeholder?

Use it when you have suitable blur data. Static imports can receive it automatically for supported formats; remote and dynamic images require your own compact blurDataURL.

Is priority still the right prop?

Check your installed version. Starting with Next.js 16, priority is deprecated in favor of preload; the Pages Router guidance also discusses loading="eager" and fetchPriority="high".

Official references