| name | Performance Budgets |
| description | ENFORCE strict performance budgets and Core Web Vitals (CWV) targets. Prevent unoptimized JS bundles, neglected image optimization, layout shift, and main thread blocking. LCP ≤ 2.5s, CLS ≤ 0.1, INP ≤ 200ms, JS ≤ 100KB gzipped per route. Trigger: "make it fast", "optimize performance", "improve core web vitals", "fix (the|a) lighthouse score".
|
| category | quality |
| version | 3.0.0 |
| last_updated | 2026-06-28T00:00:00.000Z |
| stacks | ["Next.js 16","React 19.2","Nuxt","Vue","Astro"] |
| related_skills | ["responsive-layout-math","no-slop-landing-page-architecture","component-architecture-patterns","react-best-practices"] |
Performance Budgets and Core Web Vitals
IDENTIFY: When to Activate
Activate when:
- Deciding frontend architecture or adding heavy third-party libraries
- Auditing existing codebase for speed
- User reports slow load times, layout shifts, or unresponsive UI
- Setting up a new project, set budgets before coding
CORE WEB VITALS TARGETS (Unchanged in 2026)
| Metric | Target | What It Measures |
|---|
| LCP (Largest Contentful Paint) | ≤ 2.5 seconds | Main content loading speed |
| INP (Interaction to Next Paint) | ≤ 200 milliseconds | Click/tap responsiveness |
| CLS (Cumulative Layout Shift) | ≤ 0.1 | Visual stability during load |
JAVASCRIPT BUDGET
TARGET: ≤ 100 KB gzipped per route (initial load).
Every KB of JavaScript costs: download + parse + execute. On slow mobile devices, parsing 1MB of JS takes >1 second.
BUDGET ENFORCEMENT:
Server Components by default → zero client JS for static content
Dynamic imports for heavy components → const Chart = dynamic(() => import('./Chart'))
Native APIs over libraries → Intl.DateTimeFormat, not moment.js (saves ~50KB)
Tree-shake imports → import { debounce } from 'lodash-es', not import _ from 'lodash'
EXECUTE: Instructions
1. LCP Optimization: Hero Image ALWAYS Gets Priority
LCP element is typically the hero image. Treat it as special.
CHECKLIST:
[ ] Identify LCP element (usually hero image or heading)
[ ] Preload it: <link rel="preload" as="image" href="/hero.webp" />
[ ] Use modern format: WebP or AVIF (30-50% smaller than JPEG/PNG)
[ ] Set explicit width + height (prevents CLS)
[ ] Use priority in Next.js: <Image priority ... /> (LCP image ONLY)
[ ] NEVER lazy-load above-the-fold images
[ ] Server-render above-the-fold content (don't wait for JS)
<Image src="/hero.webp" width={1200} height={600} priority alt="Product screenshot" />
<Image src="/hero.webp" loading="lazy" ... />
2. CLS Prevention: ALWAYS Reserve Space
| Cause | Fix |
|---|
| Image loads without reserved space | ALWAYS set width + height on <img> |
| Web font causes text reflow | Use next/font or font-display: swap + size-adjust |
| Ad/embed pushes content down | Reserve space: container with fixed dimensions |
| Dynamic content injected above existing | Insert below fold or use skeleton matching final size |
3. INP Optimization: Keep Interactions Fast
Break long synchronous tasks → setTimeout or scheduler.yield()
Offload heavy computation → Web Workers
Avoid deep useEffect chains → blocks main thread
Use requestAnimationFrame for visual updates only (not data processing)
4. Third-Party Scripts: ALWAYS Defer
Third-party scripts are the #1 cause of performance regressions:
<script defer src="https://analytics.example.com/script.js"></script>
<Script strategy="lazyOnload" src="https://chat.example.com/widget.js" />
5. Soft Navigation Performance (SPA Transitions)
For SPA client-side routing, track LCP and INP during virtual page transitions, not just initial load. These "soft navigations" are measured by the browser and contribute to CWV scores.
Stack-Specific Optimization (2026)
Next.js 16 + Turbopack (default bundler)
Server Components (default) → zero client JS for static content
next/image → auto-WebP/AVIF, srcset, lazy-load by default
next/font → eliminates web font CLS, self-hosts, no external requests
Route-level loading.tsx → instant loading states for server pages
Dynamic imports: dynamic(() => import('./HeavyComponent'), { ssr: false })
use cache directive → explicit opt-in caching for data
Turbopack → significantly faster builds and refresh
Astro
Zero JS by default, all components render to static HTML
client:visible for below-fold components, hydrate on scroll
Islands Architecture, each interactive component is independent island
VALIDATE: Quality Gates
ANTI-PATTERNS: ALWAYS Avoid
| Anti-Pattern | Why Wrong | Fix |
|---|
loading="lazy" on hero image | Delays LCP, the image you need fastest loads last | priority on LCP image ONLY |
useEffect + fetch in every component | Waterfall, each component waits for last | Server Components + parallel data fetching |
import moment from 'moment' | ~70KB for date formatting | Native Intl.DateTimeFormat |
'use client' on entire page | Ships massive JS bundle for static content | Move to specific interactive component |
No width/height on images | Content jumps on load, CLS penalty | ALWAYS set explicit dimensions |