Next.js 16 teljesítmény csapdák, amibe minden Vercel projekt belesétál
5 csapda, kódszintű, nem konfig. Mind benne volt minden auditban az utóbbi 4 hónapban. Konkrét javítás kódban.
5 csapda, kódszintű, nem konfig. Mind benne volt minden auditban az utóbbi 4 hónapban. Konkrét javítás kódban.
Next.js 16 ban (2026 elején GA) sok új koncepció van, és az `useState` használat egy része ami működött 14 ben, 16 ban szétdől. Az utóbbi négy hónapban hat különböző Vercel projekt audit ját csináltuk meg, és mindenhol ott volt ugyanaz az 5 csapda. Ezek nem konfig dolgok, kódszintű hibák, és mind mérhető.
Mielőtt elkezdesz olvasni: a hivatalos `node_modules/next/dist/docs/` t mindig nézd át, mert a Next.js 16 ban a 14 es API ról a 15 ön át a 16 ig sok minden megváltozott. A példák itt 16 specifikusak.
A `use cache` direktíva 16 ban hivatalos. A csapda: ha egy `use cache` jelölt komponensen belül `Suspense` boundary van, a cache nem terjed le, és a stream két külön részre szakad. Ez fizikailag jó (a felső rész gyors), de az LCP elem gyakran a Suspense boundary mögött van · az eredmény: cached oldal, nem cached LCP.
// Rossz · 16 ban LCP nő, a hero kép a Suspense ben van
async function Page() {
"use cache";
return (
<main>
<Header />
<Suspense fallback={<HeroSkeleton />}>
<Hero /> {/* LCP elem · a use cache nem fedi le */}
</Suspense>
</main>
);
}
// Jó · az LCP elemet kihúzzuk a Suspense ből, a hosszú lekérdezést tesszük be
async function Page() {
"use cache";
const heroData = await fetchHero();
return (
<main>
<Header />
<Hero data={heroData} />
<Suspense fallback={<RecsSkeleton />}>
<Recommendations />
</Suspense>
</main>
);
}Egy webshop projekten LCP 2.8s ről 1.2s re esett a refaktorral. INP nem változott.
A 14 ben már volt ez a csapda, 16 ban rosszabb, mert a partial prerendering miatt a határok cseppfolyósabbak. Tipikus hiba: egy `'use client'` jelölt komponens importál egy server only modult (pl. egy `db.ts` t), ami megint importál szerver oldali utility ket. Ez a 16 ban nem dob fordítási hibát, hanem a szerver oldali kódot is bekapja a kliens bundle be.
// lib/db.ts · csak szerver, mert a `pg` import szerver only
import { Pool } from "pg";
export const pool = new Pool();
// components/cart-button.tsx · 'use client'
"use client";
import { pool } from "@/lib/db"; // csendes szivárgás 16 ban
// ...
// Javítás · markold a server only modult
// lib/db.ts
import "server-only"; // most már fordítási hibát dob, ha kliensből importálják
import { Pool } from "pg";
export const pool = new Pool();Egy SaaS dashboard nál a kliens JS bundle 940 kB ról 320 kB ra esett, miután mind a 6 szivárgást megtaláltuk. INP 380 ms ről 120 ms re.
A PPR (Partial Prerendering) 16 ban opt out, nem opt in. Ha bárhol az route segmentben van `export const dynamic = 'force-dynamic'`, a teljes route kibukik a PPR ből, és minden request szerver oldalon teljesen renderelődik. Sok régi kódbázisban ez ott maradt egy korábbi debug ülésről.
// Audit script · keresd meg az összes force-dynamic t
// terminálban: grep -r "force-dynamic" src/app | wc -l
// ha > 5, valószínűleg fél tucatban feleslegesEgy magyar webshop projekten 14 db `force-dynamic` exportot találtunk. Hét volt indokolt (admin, fiók, kosár), hét felesleges (egy-egy debug ülésen valaki bedobta). A felesleges hét eltávolítása a TTFB t 280 ms ről 90 ms re vitte le a publikus oldalakon, mert a HTML újra cache elhetővé vált a Vercel edge n.
A 16 ban a `next/image` beépített AVIF támogatást kapott (a 14 csak WebP volt default). A csapda: ha a `next.config.ts` ban a `formats: ['image/webp']` ott maradt egy régebbi beállításból, az AVIF nem generálódik. Az új böngészők (97% lefedettség 2026 ban) AVIF re mennének, és 30-40% kal kisebb képet kapnának.
// next.config.ts
import type { NextConfig } from "next";
const config: NextConfig = {
images: {
formats: ["image/avif", "image/webp"], // sorrend számít
minimumCacheTTL: 60 * 60 * 24 * 30, // 30 nap
qualities: [40, 60, 80], // 16 új feature
},
};
export default config;Egy hírportál projekten az LCP 1.8s ről 1.1s re esett a fenti egyetlen config változással + a `next/image` `quality={60}` használattal a hero képeken.
Ez a klasszikus, ami a 14 ben még meglepetést okozott, és a 16 ban is. Ha egy szülő layout ban `export const revalidate = 60`, és egy gyerek page ben `export const revalidate = 3600`, a gyerek értéke érvényesül · de csak a `page.tsx` re. A layout maga 60 másodpercen revalidálódik, és a gyerek `page.tsx` is, mert a layout cache invalidálása lehúzza a gyermeket is.
A javítás: ne tegyél `revalidate` t layoutba, ha nem akarod, hogy mindenre érvényes legyen. A pageonkénti finomhangolást a `page.tsx` ben tartsd.
Mind az 5 csapdát egy `next build && next start` futás közben fail2 grep el meg lehet találni. A bundle analyzer (`@next/bundle-analyzer`) plusz egy lighthouse audit elég. Ha a futási idő nem 2-3 nap, a saját kódbázisod nem ezekre szakad rá, hanem mélyebb dolgokra.

Alapító, DField Solutions
Pénzügyi cégeknél és kreátor-eszközöknél is építettem már olyan rendszereket, amik nap mint nap élesben futnak. Budapesttől San Franciscóig · startupoknak és nagyobb vállalatoknak egyaránt.
Beszéljünk a projektedről. 30 perc, nincs kötelezettség.