Skip to content
Back to skills

Web Performance

ASecurity

Use this skill when a page or app feels slow, a bundle grew, Core Web Vitals regressed, or a build/deploy needs a performance budget - LCP, INP, CLS, TTFB, hydration cost, image and font strategy, cache headers, and render-blocking work. Covers measuring before changing, the fixes ordered by payoff, and proving the win with numbers.

  • 3 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 9, 2026
ai-agentsgoapifrontendbackendperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 9, 2026

npx -y skills add chadixearth/graphyloop --skill web-performance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Web Performance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Web Performance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/chadixearth-web-performance/badge)](https://www.skillsdirectory.com/skills/chadixearth-web-performance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: web-performance
description: Use this skill when a page or app feels slow, a bundle grew, Core Web Vitals regressed, or a build/deploy needs a performance budget - LCP, INP, CLS, TTFB, hydration cost, image and font strategy, cache headers, and render-blocking work. Covers measuring before changing, the fixes ordered by payoff, and proving the win with numbers.
---

# Web performance (front of the stack)

Frontend performance work goes wrong in one way: guessing. Someone memoizes a
component while a 900 KB font and a synchronous third-party script own the
critical path. Measure, fix the biggest item, measure again — and keep the number.

## When to activate

- "It's slow", "LCP regressed", "the bundle doubled", "the list janks", low
  Lighthouse/PageSpeed score, a CI budget failing.
- Before shipping a new page, dashboard, or heavy dependency.
- After adding an animation library, chart library, map, video, or analytics tag.

## Step 1 — baseline (never skip)

Record numbers before touching code; without a baseline there is no win to claim.

```
npx lighthouse http://127.0.0.1:PORT --preset=desktop --quiet --output=json --output-path=./.perf/base.json
npx lighthouse http://127.0.0.1:PORT --quiet   # default = mobile throttling, the honest one
ANALYZE=true npm run build        # next: @next/bundle-analyzer
npx vite-bundle-visualizer        # vite
npx source-map-explorer 'dist/assets/*.js'
```

Capture: LCP, INP (replaces FID), CLS, TTFB, total JS transferred, largest chunks,
request count, and the field data if you have RUM. Note whether the run is
throttled — an unthrottled localhost number is not a result.

Targets (75th percentile, mobile) — the three Core Web Vitals plus TTFB, which is a
diagnostic for the first of them rather than a vital itself: **LCP ≤ 2.5 s ·
INP ≤ 200 ms · CLS ≤ 0.1 · TTFB ≤ 0.8 s**, and a JS budget you write down (e.g.
≤ 170 KB gzipped for the initial route).

## Step 2 — fixes, ordered by payoff

**LCP (usually an image or a slow server)**
- Find the LCP element in the Lighthouse trace before optimizing anything.
- Hero image: correct format (AVIF/WebP), correct dimensions, `priority` /
  `fetchpriority="high"`, `preload` the actual file; every other image `loading="lazy"`
  + `decoding="async"`.
- Kill render-blocking: no `@import` chains, inline critical CSS, `defer` scripts,
  `preconnect` to the font/API origin, self-host fonts with `font-display: swap`
  and subset them (`unicode-range`). One variable font beats four weights.
- Server side: cache the data the first paint needs, stream (RSC/Suspense) instead
  of awaiting everything, and check TTFB separately — a 1.5 s TTFB is a backend bug
  (query plan, N+1, cold start), not a CSS problem.

**INP / main-thread**
- Split the bundle: dynamic `import()` for routes and anything below the fold
  (editors, charts, maps, date pickers, emoji sets). Never import a whole icon or
  lodash package for one symbol.
- Client JS is a budget: keep components server-rendered where the framework allows
  it; a `"use client"` at the top of a layout ships the entire tree.
- Break up long tasks (> 50 ms): `scheduler.yield()`/`setTimeout` chunks,
  `startTransition` for non-urgent updates, debounce input handlers, move parsing
  or crypto to a Web Worker.
- Lists: virtualize past a few hundred rows, stable `key`s, avoid new object/array
  literals in props on every render. Memoize only where a profile shows the cost.
- Animate `transform`/`opacity` only; anything touching layout thrashes. Read then
  write — never `getBoundingClientRect` inside a write loop.

**CLS**
- Explicit `width`/`height` or `aspect-ratio` on every image, video, iframe, ad slot.
- Reserve space for banners, skeletons, and async content; never insert above
  existing content after paint.
- Fonts: matched fallback metrics (`size-adjust`, `ascent-override`) or `next/font`
  to remove the swap shift.

**Transfer and caching**
- Immutable hashed assets: `cache-control: public, max-age=31536000, immutable`;
  HTML `no-cache` (or a short s-maxage + `stale-while-revalidate`) at the CDN.
- Compression on (brotli) — verify with `curl -sI -H 'accept-encoding: br'`.
- Drop the dependency you added for one helper; prefer the platform (`Intl`,
  `URL`, `structuredClone`, CSS `:has`).

## Step 3 — prove it and keep it

- Re-run the exact same command as the baseline, same throttling, and report
  before → after per metric. A change with no measurement is not done.
- Land a budget so the win survives: `lighthouse-ci` assertions, a
  `performance.budget.json`, `size-limit`, or a bundle-size check in CI.
- Add RUM (`web-vitals` → your analytics endpoint) if the app has real users —
  lab numbers do not catch a slow third-party tag on a cold cache.

## Anti-patterns

- Optimizing what the profile did not blame.
- Wrapping everything in `memo`/`useMemo`/`useCallback` as policy — cost, no win,
  and it hides the real regression.
- `loading="lazy"` on the hero image (delays LCP).
- Preloading everything — preload competes with the critical request.
- Removing a feature to hit a number without saying so.

## Reporting

A table of metric · before · after · how measured, then the file-level changes that
produced it, then the budget added. Name what regressed elsewhere (e.g. bundle down
but TTFB up) rather than reporting only the improved metric. If a number could not
be measured, say which and why.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.