Core Web Vitals och Next.js
Vad Next.js gör för prestanda automatiskt och vad du fortfarande måste hantera själv: LCP, CLS, INP och praktiska snabbvinster.
Next.js gör en del av prestandaarbetet åt dig automatiskt. Men "automatiskt" gäller inte allt, och ett vanligt misstag är att anta att ramverket löser hela problemet. Den här guiden går igenom vad Core Web Vitals faktiskt mäter, var Next.js hjälper dig och var ansvaret fortfarande är ditt.
Vad är Core Web Vitals?
Core Web Vitals är tre mätvärden från Google som mäter upplevd laddningstid, visuell stabilitet och interaktivitet. Sedan 2021 ingår de i Googles rankningsalgoritm. En sida med svaga poäng rankar lägre än en jämförbar sida med starka poäng.
LCP (Largest Contentful Paint)
LCP mäter hur lång tid det tar innan det största synliga elementet ovanför folden har renderats. Det är vanligtvis en hjältebild eller en stor rubrik. Målvärde: under 2,5 sekunder.
Vanliga orsaker till dålig LCP: långsam server, render-blockerande resurser, stora okomprimerade bilder, och lazy-laddade bilder ovanför folden. Den sista orsaken är särskilt vanlig i Next.js-projekt eftersom lazy loading är på som standard.
CLS (Cumulative Layout Shift)
CLS mäter hur mycket layouten hoppar runt under laddning. Varje gång ett element förflyttar sig utan att användaren initierat det bidrar det till CLS-poängen. Målvärde: under 0,1.
Vanliga orsaker: bilder och iframes utan angivna dimensioner, innehåll som injiceras ovanför befintligt innehåll efter laddning, och webbtypsnitt som orsakar textomflöde när de laddas in.
INP (Interaction to Next Paint)
INP mäter hur snabbt sidan svarar på användarinteraktioner: klick, tangentbordstryckningar och touch-gester. Det ersatte FID (First Input Delay) i mars 2024 och är ett mer heltäckande mått eftersom det mäter alla interaktioner under ett sidbesök, inte bara den första. Målvärde: under 200 ms.
Vanliga orsaker: långa JavaScript-uppgifter som blockerar main thread, tunga event handlers och tredjepartsskript.
Vad Next.js gör åt dig automatiskt
Statisk generering
Sidor som byggs med generateStaticParams eller utan server-side-data pre-renderas till HTML vid byggtid. Servern skickar ett komplett HTML-dokument direkt, utan att JavaScript behöver köras innan innehållet är synligt. Det är den starkaste prestandaoptimeringen som finns, och Next.js hanterar den utan konfiguration.
Automatisk koddelning
Bara JavaScript för den aktuella sidan skickas till webbläsaren. Delad kod splittras i separata chunk-filer som laddas vid behov. Du slipper skicka hela applikationens bundle till varje besökare.
Streaming med Suspense
Sidskeletten renderas och streamas till webbläsaren omedelbart. Sektioner omslutna med <Suspense> laddas progressivt allt eftersom deras data resolvar. Webbläsaren kan börja rendera innan all data finns tillgänglig, vilket ger en upplevelse av snabbare laddning även när databasfrågor tar tid.
next/image
next/image konverterar automatiskt bilder till WebP och AVIF, aktiverar lazy loading som standard, och kräver att du anger dimensioner. Det obligatoriska width/height-kravet är inte godtyckligt: det förhindrar CLS eftersom webbläsaren kan reservera rätt utrymme innan bilden laddas.
next/font
Typsnitt laddas ner vid byggtid och self-hostas. Ingen extern fontbegäran blockerar renderingen. Next.js injicerar också CSS med size-adjust som justerar fallback-typsnittet så att det matchar dimensionerna på det inladdade typsnittet, vilket eliminerar textomflöde.
Server Components
Komponenter som inte behöver interaktivitet körs på servern och skickar noll JavaScript till webbläsaren. Mindre JavaScript på klienten innebär mindre att parsa, kompilera och köra, vilket direkt förbättrar INP.
Vad Next.js inte löser åt dig
Den viktigare delen för erfarna utvecklare är följande.
LCP-bilden kräver priority
Next.js vet inte vilken bild som är ditt LCP-element. Utan priority är alla bilder lazy-laddade. Det innebär att webbläsaren inte börjar hämta hjältebilden förrän efter att sidan har parsats och layout beräknats, vilket direkt slår mot LCP.
<Image
src="/hero.jpg"
alt="Beskrivning"
width={1200}
height={600}
priority
/>
Lägg till priority på den första synliga bilden ovanför folden. Det är en av de snabbaste LCP-förbättringar du kan göra.
CLS från dynamiskt innehåll
Innehåll som injiceras efter laddning, auth-tillstånd som förändrar layouten, eller client-side-datafetcher som ersätter platshållare kan alla ge CLS-poäng. Next.js kan inte förutse hur din applikation beter sig.
Lösningen beror på situationen: reservera utrymme med CSS, använd skeleton screens med fasta dimensioner, eller flytta renderingen till servern.
INP från JavaScript
Tunga event handlers, synkrona DOM-operationer och tredjepartsskript körs alla på main thread. Next.js kan inte optimera din egna JavaScript-logik.
Använd startTransition för icke-brådskande UI-uppdateringar:
import { startTransition } from "react";
function SokFalt() {
const [query, setQuery] = useState("");
const [resultat, setResultat] = useState([]);
function hanteraInput(e) {
const varde = e.target.value;
setQuery(varde);
startTransition(() => {
setResultat(sokILista(varde));
});
}
return <input value={query} onChange={hanteraInput} />;
}
För riktigt tunga operationer: scheduler.yield() är tillgängligt i moderna webbläsare och låter dig ge tillbaka kontrollen till webbläsaren mitt i en lång uppgift. Undersök ditt bundle med @next/bundle-analyzer för att hitta stora beroenden.
Tredjepartsskript
Analytics, chattwidgetar och samtyckesbanners är de vanligaste INP-bovarna i verkliga mätningar. Next.js tillhandahåller next/script för att styra laddningsordningen, men kan inte eliminera kostnaden för att köra dessa skript.
Font-FOUT utan next/font
Om typsnitt laddas via en vanlig <link>-tagg renderas text i fallback-typsnittet först, sedan flödar den om när webbtypsnittets laddats. Det ger CLS. next/font förhindrar det genom att preladda typsnittet och dimensionera fallback-versionen korrekt.
next/font
Ladda ett Google-typsnitt i applikationens layout:
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
})
export default function Layout({ children }) {
return (
<html lang="sv" className={inter.className}>
<body>{children}</body>
</html>
)
}
För ett lokalt typsnitt:
import localFont from 'next/font/local'
const satoshi = localFont({
src: '../public/fonts/Satoshi-Variable.woff2',
display: 'swap',
})
Next.js injicerar typsnitts-CSS direkt i <head> med font-display: swap och ett size-adjust-värde som gör fallback-typsnittet exakt lika stort som måltypsnittets, vilket eliminerar omflödet när typsnittet laddas. Ingen extern fontbegäran behöver göras av webbläsaren, och ingen render-blockerande resurs tillkommer.
next/script och tredjepartsskript
next/script ger dig tre strategier via strategy-propen:
import Script from 'next/script'
export default function Layout({ children }) {
return (
<>
{children}
{/* Laddas efter sidan är interaktiv, för analytics och taggar */}
<Script src="https://analytics.exempel.se/script.js" strategy="afterInteractive" />
{/* Laddas under idle time, för chattwidgetar och lågprioriterade skript */}
<Script src="https://chat.exempel.se/widget.js" strategy="lazyOnload" />
</>
)
}
beforeInteractive är för skript som måste köras innan sidan blir interaktiv, till exempel ett consent management-bibliotek som blockerar allt annat. Det är sällsynt. Lägg aldrig analytics i beforeInteractive, det blockerar hydreringen och sänker INP direkt.
afterInteractive är standardvalet för analytics. Skriptet laddas efter att React har hydrerat sidan.
lazyOnload är för allt icke-kritiskt: chattwidgetar, sociala knappar, lågprioritetade verktyg. Skriptet laddas under webbläsarens idle time.
Mäta och felsöka
Lighthouse
Öppna Chrome DevTools i ett inkognito-fönster och kör Lighthouse med mobilsimulering, throttlad CPU och throttlat nätverk. Inkognito-läget eliminerar extensions som annars kan påverka poängen. Kör tre gånger och ta snittet, poängen varierar mellan körningar.
Lighthouse är bra för att fånga uppenbara problem men är en simulering, inte ett verkligt mätvärde.
WebPageTest
webpagetest.org kör tester på riktiga enheter och riktiga nätverk. Waterfall-vyn visar exakt vilka resurser som blockerar laddningen och i vilken ordning. Mer tillförlitlig än Lighthouse för absoluta tal.
@next/bundle-analyzer
ANALYZE=true npm run build
Genererar en visuell treemap av dina JavaScript-bundles. Hitta stora beroenden och utvärdera om de kan lazy-laddas eller ersättas med något lättare.
Chrome DevTools Performance-fliken
Spela in en interaktion för att hitta långa uppgifter och identifiera vilket skript som orsakar hög INP. Filtrera på "Long Tasks" i tidslinjen. Varje uppgift över 50 ms bidrar till fördröjningen.
Snabbvinster: checklista
- Lägg till
prioritypå hjältebilden, LCP-elementet ovanför folden - Använd
next/fontistället för Google Fonts<link>-taggar - Sätt
strategy="afterInteractive"på analytics-skript ochstrategy="lazyOnload"på chattwidgetar och andra icke-kritiska skript - Lägg till
sizespå alla responsiva bilder medfill - Sätt explicit
widthochheightpå alla bilder som anges med URL (förhindrar CLS) - Wrappa tunga komponenter under folden med
dynamic(() => import(...))för lazy loading - Lägg till
<Suspense>-gränser runt sektioner som hämtar data (aktiverar streaming) - Kör
ANALYZE=true npm run buildoch undersök stora paket
