# How to Improve Core Web Vitals in Next.js for Beginners

> How to improve Core Web Vitals in Next.js for beginners: measure LCP, CLS and INP, tune next/image priority and sizes, use next/font, fix common errors.

URL: https://www.matthewswong.com/en/blog/cara-meningkatkan-core-web-vitals-nextjs/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2026-10-17
- Updated: 2026-10-17
- Category: Web Dev

> **Key takeaway:** To improve Core Web Vitals in Next.js: measure first with PageSpeed Insights, give the hero image priority plus width and height or fill, write sizes for responsive images, use fixed width and height for small images, and load fonts through next/font. web.dev's good targets are LCP 2.5 seconds, INP 200 milliseconds, CLS 0.1.

You run PageSpeed Insights for the first time and LCP is red, even though the page is one big image and a little text. You add the Image component the way the docs show, and the score does not move. Or the text above the fold jumps a moment after the font arrives. Beginners get stuck here because they cannot tell which part is slow, so every fix feels like a guess.

This guide fixes the three most common causes: the hero image, responsive image sizing, and fonts. Every prop name and behaviour was checked against the Next.js 15 docs and web.dev. The examples follow what this site does: the blog hero uses fill, sizes and priority, small logos use fixed width and height, and the font is self-hosted through next/font/local. I do not promise how many milliseconds you will save; you have to measure that on your own pages.

Want the concepts from scratch first? There is a

free lesson on Core Web Vitals, next/image and next/font

with an interactive diagram and a quiz. This article is the hands-on, step by step version.

What you need

A Next.js 15 project using the App Router that builds (npm run build).

One page with a large image at the top (the hero) and a few small images such as logos.

Chrome with DevTools, and access to pagespeed.web.dev for a site that is online.

The discipline to measure before and after each change, one change at a time.

For a wider view of Next.js performance, also read the

Next.js performance optimization guide

 once you finish here.

## Step 1: Measure first, and tell field data from lab data

Before changing code, know your starting numbers. PageSpeed Insights shows two kinds of data. Field data comes from the Chrome User Experience Report (CrUX), real visits over the previous 28 days, shown at the 75th percentile. Lab data comes from Lighthouse running in a simulated environment. If your page lacks enough samples, PageSpeed Insights falls back to origin-level data, and if that is also thin, no field data is shown.

The three metrics and web.dev's good thresholds, all judged at the 75th percentile: LCP (Largest Contentful Paint, how fast the largest element appears) 2.5 seconds or less, INP (Interaction to Next Paint, how fast the page responds to clicks and taps) 200 milliseconds or less, and CLS (Cumulative Layout Shift, how much the layout moves) 0.1 or less. What to expect from this step: three starting numbers written down, and you know which one is red.

Lighthouse cannot measure INP because there is no user input; it uses Total Blocking Time as a lab stand-in. Lab and field data can disagree because they measure different conditions. Use lab to find causes and field to judge the result.

## Step 2: Fix the hero image with priority

LCP is usually the largest image or text block visible when the page opens, and on many pages that is the hero image. The Image component from next/image lazy loads by default. web.dev says never to lazy-load the LCP image, because the download only starts once the browser confirms the image is in the viewport. On the hero, add priority.

For a hero that spans the screen, use fill inside a wrapper with a fixed aspect ratio. The docs require the parent of a fill image to be positioned relative, fixed or absolute. This is how this site's blog hero is built.

What to expect: the hero image is fetched early and its space is already reserved, so nothing jumps when it appears. Remember that width and height are the intrinsic size used to compute the aspect ratio; the displayed size is still set by CSS. You must set both unless the image is statically imported or uses fill.

## Step 3: Write sizes for responsive images

The browser does not know how wide an image will be shown when it reads the HTML, so sizes tells it. Per the Next.js docs, if sizes is missing the browser assumes the image is as wide as the viewport, which can download an unnecessarily large image. Use sizes whenever an image uses fill or is made responsive with CSS.

sizes also changes the markup: without it, Next.js generates a limited srcset (for example 1x and 2x) that suits fixed-size images; with it, Next.js generates a full srcset of widths such as 640w and 750w. The browser downloads only the one candidate it picks. Check the built HTML to confirm.

## Step 4: Use fixed width and height for small images

The reverse matters too. The certification marquee on this site shows 44 pixel logos. A comment in its code records that fill with a pixel-based sizes lists every configured width in srcset, about 1 KB of HTML per tile. The fix was fixed dimensions.

The simple rule: fluid images such as heroes and card covers use fill and sizes; fixed-size images such as logos and avatars use width and height. If you are curious how srcset and the image cache work behind the scenes, read

the internals of next/image optimization

.

## Step 5: Load fonts through next/font

next/font self-hosts font files with the rest of your static assets, so the browser sends no requests to a font provider. For Google fonts, the CSS and files are downloaded at build time. According to the docs, this lets you load web fonts with no layout shift. The default display is swap and the default preload is true. The docs recommend variable fonts.

If your CI build must not depend on the network to a font provider, keep the file in the repo and use next/font/local. This site switched to that after the Docker build in CI failed to download the font from Google and the whole deploy failed with it. One variable font file now covers weights 400 to 800.

Per the docs, next/font has an adjustFontFallback option (on by default for Google fonts) that adjusts the fallback font to reduce CLS. For local fonts the value is Arial, Times New Roman or false. Do not turn it off unless you know why.

## Step 6: Verify the result

Build for production and read the HTML that is really shipped, not the source. What to expect: a preload link for the hero image, responsive images carrying the sizes string you wrote, and no request to a font provider.

Then run PageSpeed Insights again and compare with the numbers from Step 1. Lab data changes immediately, but field data uses a 28 day window, so an improvement only shows fully after a few weeks.

What about INP? next/image and next/font target loading and stability. INP is about how fast the page responds after a click or tap, and that is usually heavy work in an event handler, not images. Measure real interactions before guessing.

Common errors and how to fix them

Seven of these eight problems are easy-to-spot implementation mistakes; the eighth is a misreading of the numbers.

Symptom

Cause

Fix

LCP is red and the hero appears late

The hero uses the next/image default, lazy loading, so the download waits for layout

Add priority to the hero image

LCP did not improve after adding priority

priority is on many images, and web.dev says more than one or two stops it helping

Set it only on the image that is the LCP element

Next.js asks for width and height, or the layout jumps when the image appears

The aspect ratio is unknown, so no space is reserved

Set width and height, import the image statically, or use fill inside a positioned wrapper

A fill image does not show or has a strange size

The parent element is not positioned relative, fixed or absolute

Give the parent position relative and a clear height or aspect ratio

Phones download an image that is too large

sizes is missing, so the browser assumes the image is as wide as the viewport

Write sizes to match the real displayed width at each breakpoint

HTML bloats with a long srcset for small logos or icons

fill with a pixel sizes lists every configured width

Use fixed width and height so srcset is only 1x and 2x

An image from another domain fails with 400 Bad Request

The image host is not registered in remotePatterns in the Next.js config

Add the hostname and path to images.remotePatterns, then restart

Lab is green but field data in PageSpeed Insights is still red

Lab data is a simulation; field data is real users over 28 days at the 75th percentile

Judge results by field data, use lab for debugging, and wait out the 28 day window

Checklist before you deploy

You wrote down the starting LCP, INP and CLS from PageSpeed Insights.

The hero image has priority, and only one or two images do.

Every image has width and height, or fill inside a positioned wrapper.

Fluid images have sizes; small fixed-size images do not use fill.

Fonts load through next/font, once in one module, with subsets or a local file.

You checked the built HTML with curl, then re-ran PageSpeed Insights.

Go deeper

This article focuses on the practical steps. The concepts, including the four LCP subparts and a diagram of one image from HTML to paint, are in the free lesson below.

Lesson: Core Web Vitals, next/image and next/font basics

The rule to carry: measure first, fix the LCP element with priority, describe image size through width, height or sizes, and let next/font host your fonts. One change, one measurement.

## Sources and official reading

- web.dev: Web Vitals
- web.dev: Optimize Largest Contentful Paint
- Google: about PageSpeed Insights
- Next.js 15 docs: Image component
- Next.js 15 docs: Font module

## Source links

- https://web.dev/articles/vitals
- https://web.dev/articles/optimize-lcp
- https://developers.google.com/speed/docs/insights/v5/about
- https://nextjs.org/docs/15/app/api-reference/components/image
- https://nextjs.org/docs/15/app/api-reference/components/font

## FAQ

### How do I improve LCP in a Next.js app?

Find the largest element, usually a hero image, and add priority to its next/image component so it is preloaded and not lazy loaded. web.dev says never to lazy-load the LCP image. Set priority on only one or two images, because more than that stops it helping.

### Why is my Next.js layout shifting when images load?

The browser did not know the image's aspect ratio, so it reserved no space. Set width and height, import the image statically, or use fill inside a wrapper that is positioned and has a fixed aspect ratio. The same goes for fonts: next/font avoids layout shift by self-hosting and adjusting the fallback font.

### Do I need the sizes prop on next/image?

Yes, whenever the image uses fill or CSS makes it responsive. Without sizes the browser assumes the image is as wide as the viewport and may download an unnecessarily large file. With sizes, Next.js also generates a full srcset of widths.

### Why is my Lighthouse score good but PageSpeed Insights field data is bad?

Lighthouse is lab data from a simulated environment, while field data comes from real Chrome users over the previous 28 days at the 75th percentile. They measure different conditions, so they can disagree. Use lab data to debug and field data to judge whether users really improved.

### Does next/image or next/font fix INP?

Not directly. They target loading and visual stability, which means LCP and CLS. INP measures how quickly the page responds to clicks, taps and key presses, so you need to look at what your event handlers do and measure real interactions.

---

- HTML version: https://www.matthewswong.com/en/blog/cara-meningkatkan-core-web-vitals-nextjs/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/cara-meningkatkan-core-web-vitals-nextjs/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
