Images & Icons Best Practices
A condensed summary of the 25 most important best practices drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important best practices drawn from every page in this section.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
next/image so Next.js automatically serves WebP/AVIF, lazy-loads offscreen images, and generates responsive sizes. The raw <img> tag ships no optimization and hurts LCP.fill without a sizes prop makes the browser download the largest variant for every device. Provide a breakpoint-aware sizes string that matches your actual layout columns.fill image's parent must have position: relative (or absolute/fixed) plus defined dimensions, or the image collapses to zero height. Use an aspect-ratio wrapper to lock the layout.priority to above-the-fold hero images (typically one or two per page). Overusing it cancels the lazy-loading benefit of next/image.images.unsplash.com, images.pexels.com, cdn.pixabay.com) to images.remotePatterns in next.config.ts and restart the dev server. Missing entries cause 400 errors at runtime, not at build time.StaticImageData with width, height, and an auto-generated blurDataURL, eliminating manual boilerplate. They also enable placeholder="blur" without extra work.width and height props define the requested size and aspect ratio - they do not resize or crop the rendered element. Control visual sizing with CSS classes..env.local. Pixabay is especially risky because the key is a query parameter.next: { revalidate: 3600 } so you stay inside hourly rate limits (Unsplash 50/5000, Pexels 200, Pixabay 100/min) and keep pages fast. This also avoids hammering third-party APIs on every request.Authorization: Client-ID <key>, Pexels requires the raw key with no Bearer prefix, and Pixabay uses a key query parameter. Mixing these up returns a 401 and wastes debugging time.regular/webformatURL/large for display and reserve raw/original/fullHDURL for downloads, since originals can exceed 10MB. Serving oversized images undoes the point of an optimization pipeline.download_location whenever a photo is used. Pexels and Pixabay strongly encourage attribution even when not strictly required.alt or Unsplash alt_description can be empty, generic, or null. Provide a meaningful fallback to keep images accessible.react-icons from a family path like react-icons/fa - importing from the package root can defeat tree-shaking and bundle thousands of unused icons. The same discipline applies to any barrel-style icon import.size and color props; set dimensions and color via Tailwind utilities such as h-6 w-6 text-blue-500. Alias imports when using both outline and solid variants of the same icon in one file.LucideIcon, IconType, or React.ComponentType<SVGProps<SVGSVGElement>> to type icon props so you can render them with <Icon size={…} />. Passing a rendered element instead of a component type breaks reusable button wrappers.IconContext.Provider from react-icons uses React Context and must live in a Client Component in the App Router. Place it high enough in the tree to cover the subtree you want to style.stroke="currentColor" (or fill="currentColor") on inline SVG icons so they inherit color via parent text-* classes. This makes a hand-rolled icon set behave like Lucide or Heroicons.aria-hidden="true" and no role; informative icons need role="img" plus an aria-label. Also write SVG attributes in camelCase (viewBox, strokeWidth) and keep viewBox even after SVGO optimization.fill and stroke to black, so stroke-based icons need fill="none" and filled icons need stroke="none" to avoid surprise rendering. Lucide's stroke-only behavior is a concrete example - fill has no effect there.blurDataURLs (around 8x8 pixels) ahead of time and cache them with your image metadata. Generating them per request with sharp adds latency and can't run on the Edge Runtime.blur() only to tiny placeholders, never to full-resolution images.favicon.ico in app/ root, and drop icon.*, apple-icon.*, opengraph-image.*, and twitter-image.* into the route segments where they should apply. Next.js wires them into <head> automatically and scopes OG images per route.ImageResponse from next/og uses Satori, which supports only flexbox (no grid), no background-image: url(), and requires fonts loaded as ArrayBuffer. Target 1200x630, export an alt, and prefer runtime = "edge" for fast, cacheable per-page images.Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥