Journal/Next.js Images for Portfolios: Visual Stability Without Killing Art Direction

Next.js Images for Portfolios: Visual Stability Without Killing Art Direction

Front-End CraftPerformanceWeb Design

Next.js image handling is built for generic web apps. Portfolio sites need different tradeoffs. Here is how to configure it without losing visual control.

Next.js Images for Portfolios: Visual Stability Without Killing Art Direction illustration

The Default Does Not Fit

Next.js ships with an image optimization pipeline. In its default configuration, the next/image component handles lazy loading, responsive sizing, format conversion, and quality adjustment automatically. For a SaaS landing page or a content-heavy blog, this default behavior is genuinely useful.

For portfolio sites, it creates problems. The automatic quality reduction can blur photographic detail that is essential to showcasing work. The built-in responsive breakpoints may not match the art-directed crops a portfolio layout requires. And the layout shift prevention mechanism, while good in principle, can fight against the flexible aspect ratios that portfolio grids demand.

This article is about making Next.js images work for portfolio sites specifically, where visual quality and art direction take priority over generic optimization.

Static Export Reality

Many portfolio sites (including this one) use Next.js with output: 'export' for static deployment. In this mode, the built-in image optimization server is not available because there is no server. The next/image component still works, but only with unoptimized: true in the config, which disables automatic format conversion and resizing at request time.

This means image optimization for static exports happens at build time, not at serve time. You preprocess your images into the formats and sizes you need, put them in the public/ directory, and reference them directly. The next/image component still provides lazy loading and layout shift prevention via its sizing props, but the actual image files are your responsibility.

This is not a limitation. For portfolio sites, it is an advantage. You have full control over compression quality, format selection, and cropping for every image. No runtime process is making quality decisions for you.

Layout Shift Prevention

The primary value of next/image even in static export mode is CLS prevention. When you provide width and height props, the component renders a container with the correct aspect ratio before the image loads. This prevents layout shifts that would occur if the browser had to discover the image dimensions from the file headers.

For portfolio sites, this works well when images have consistent aspect ratios. A grid of project thumbnails at 16:9 is straightforward. But portfolio layouts often mix aspect ratios: a 16:9 hero, a 4:3 detail shot, and a 1:1 avatar. Each needs its correct dimensions specified.

If you are using plain tags instead of next/image (which is common on static export sites), add width and height attributes to every image. The behavior is identical for CLS purposes. We discussed this in the CLS article.

Art Direction: The Picture Element

When the same image needs different crops at different viewport sizes, neither next/image nor a plain with srcset is sufficient. You need the element.

A common portfolio pattern: the hero image shows a full-width product shot on desktop, but on mobile the same image is too wide to read the important details. You need a tighter crop for narrow viewports.

```

Project hero

```

This gives you different crops for different breakpoints and different formats for different browsers, which is exactly what portfolio sites need and what automatic optimization cannot provide.

Quality Control

On a portfolio site, image quality is not a slider you crank down until the file is small enough. It is a design decision that varies per image.

A high-contrast product photograph with sharp edges compresses efficiently. You can use aggressive AVIF settings (quality 50 to 55) and the result looks excellent because AVIF handles edges well.

A soft, gradient-heavy brand photograph is harder. AVIF at the same quality setting may introduce banding in the gradients. You need to bump the quality to 65 or 70, which produces a larger file but preserves the visual intent.

A screenshot or mockup with text and UI elements needs yet another approach. Text rendering artifacts are more noticeable than photographic artifacts because our eyes are trained to detect text imperfections. Use higher quality settings or lossless WebP for images that contain readable text.

The lesson: per-image quality settings are not optional on portfolio sites. Batch processing with a single quality level produces inconsistent results. Either tune each image individually or establish quality presets for different image categories (photography, mockups, screenshots, abstract textures).

Responsive Images Without a Server

On static export sites, responsive images require pre-generating every size variant at build time.

The practical approach:

  1. Define the breakpoints your layout uses (e.g., 400w, 800w, 1200w, 1600w)
  2. Process each source image into those widths during the build step
  3. Reference them with srcset and sizes attributes

```

src="/images/work/project-1600x900.jpg"

srcset="

/images/work/project-400x225.jpg 400w,

/images/work/project-800x450.jpg 800w,

/images/work/project-1200x675.jpg 1200w,

/images/work/project-1600x900.jpg 1600w

"

sizes="(max-width: 768px) 100vw, 50vw"

width="1600"

height="900"

alt="Project detail"

loading="lazy"

/>

```

This is more manual than the built-in Next.js image optimization, but it gives you exact control over which files are generated and served. For a portfolio site with 50 to 100 images, the build-time cost is manageable.

The Unoptimized Trade-off

Setting images: { unoptimized: true } in next.config means the framework trusts you to handle optimization. This is the right choice for static exports because the alternative (a runtime optimization server) does not exist in that deployment model.

But it means every image optimization decision is explicit. If you add a new image to the site and forget to compress it, the raw file ships to production. There is no safety net.

The mitigation is validation. A build step that checks every image in public/images/ against size budgets and format expectations catches mistakes before deployment. This is more robust than relying on a runtime service because it fails loudly during the build rather than silently serving suboptimal images.

When next/image Is Worth Using

Even in static export mode, next/image provides value for:

  • Lazy loading. The component adds loading="lazy" by default and can be configured with priority for above-the-fold images.
  • Blur placeholder. If you generate blurDataURL values at build time, the component shows a blurred preview while the full image loads, which feels more polished than an empty space.
  • Sizing props. The fill prop with sizes gives you responsive container behavior without manual CSS.

For portfolio grid pages with many images, these features reduce boilerplate. For individual hero images where you want full art direction control, a plain element is often simpler.

FAQ

Should I use next/image or plain img tags on a static export site?

Use next/image where its lazy loading and sizing features are helpful (grids, listing pages). Use and where you need art direction (hero images, featured project shots). Mixing both on the same site is fine.

Can I use Cloudflare Images or Imgix with a static Next.js site?

Yes. You can use an external image service as the src for your images. The service handles format negotiation and resizing at the edge. This works well for sites with many images where build-time processing is too slow.

Does the next/image quality prop do anything in static export mode?

No. With unoptimized: true, the quality prop is ignored because no runtime processing occurs. Quality is determined by the source file you provide.

The Practical Summary

For portfolio sites on Next.js static export:

  1. Set images: { unoptimized: true } in the config
  2. Pre-process images at build time using Sharp or equivalent
  3. Use for art-directed hero images
  4. Use next/image for grids and listing pages where lazy loading matters
  5. Validate image sizes and formats as part of the build pipeline
  6. Accept that this is more work than the automatic optimization server, but it gives you the visual control a portfolio demands

Continue Reading

All journal entries