Advanced (Updated: 6/2/2026)

Claude Code Performance Optimization: From Measurement to Core Web Vitals

Measure and improve LCP, INP, API latency, bundles, and caching with Claude Code and runnable examples.

Claude Code Performance Optimization: From Measurement to Core Web Vitals

Performance optimization is not a vague attempt to make a web app feel faster. It is the discipline of measuring what users wait for, what blocks interaction, what shifts on screen, and what wastes server resources, then reducing those costs with evidence.

Claude Code is useful here because it can inspect code, trace likely bottlenecks, propose focused patches, and help write verification steps. The important part is the workflow: measure first, form a hypothesis, make a small change, and measure again. If you only ask Claude Code to “make this fast,” you will often get plausible but unproven changes.

Use the official references as your baseline: web.dev Core Web Vitals, Lighthouse docs, and the MDN Performance API.

Define Fast Before Editing

Web performance has several dimensions. LCP, or Largest Contentful Paint, tracks when the main visible content appears. INP, or Interaction to Next Paint, tracks how heavy interactions feel after a click, tap, or key press. CLS, or Cumulative Layout Shift, tracks unexpected movement while the page loads.

For application code, you also need API p75 latency, database query count, JavaScript bundle size, image transfer size, and CPU-heavy work in the browser. Average latency hides unhappy users, so p75 is often more practical: it shows the slower end of normal traffic without being dominated by a single outlier.

flowchart LR
  Measure["Measure"] --> Hypothesis["Form a hypothesis"]
  Hypothesis --> Patch["Patch one thing"]
  Patch --> Verify["Measure again"]
  Verify --> Keep["Keep only proven wins"]
MetricMeaningFirst target
LCPMain content appearsAim for 2.5s or less
INPInteraction responsivenessAim for 200ms or less
CLSUnexpected layout movementAim for 0.1 or less
API p75Latency most users feelDefine per screen
JS transferJavaScript loaded up frontCompare per route

Give Claude Code Better Inputs

The best prompt includes the symptom, reproduction steps, current numbers, and constraints.

The /products page in this repository is slow.
The goal is to reduce mobile LCP and API p75 latency.

Current measurements:
- Lighthouse mobile Performance: 58
- LCP: 4.2s
- INP: 180ms
- CLS: 0.04
- /api/products p75: 920ms

Please:
1. Inspect images, bundle size, API code, and database access.
2. Do not change code based on guesses only.
3. Prefer low-risk patches with measurable impact.
4. Include the commands I should run after the change.

For related local reading, see the Complete Debugging Guide, How to Supercharge React Development, and the Claude Code Image Optimization Guide.

Runnable Measurement Script

This Node.js 18+ script measures a page or API endpoint and prints average and p75 latency.

// measure-url.mjs
import { performance } from "node:perf_hooks";

const url = process.argv[2] ?? "https://example.com/";
const runs = Number(process.argv[3] ?? 5);

async function measureOnce() {
  const start = performance.now();
  const res = await fetch(url, { cache: "no-store" });
  await res.arrayBuffer();
  return {
    status: res.status,
    ms: performance.now() - start,
  };
}

const samples = [];
for (let i = 0; i < runs; i += 1) {
  samples.push(await measureOnce());
}

samples.sort((a, b) => a.ms - b.ms);
const avg = samples.reduce((sum, s) => sum + s.ms, 0) / samples.length;
const p75Index = Math.floor((samples.length - 1) * 0.75);
const p75 = samples[p75Index].ms;

console.table(
  samples.map((sample, index) => ({
    run: index + 1,
    status: sample.status,
    ms: sample.ms.toFixed(1),
  }))
);
console.log(`avg=${avg.toFixed(1)}ms p75=${p75.toFixed(1)}ms`);
node measure-url.mjs http://localhost:3000/api/products 7

Give this output to Claude Code before and after the patch. It turns performance work from opinion into a repeatable comparison.

Realistic Use Cases

Use caseCommon causeWhat to ask Claude Code
SaaS dashboard loads slowlyEvery widget fetches on first paint; chart bundle is hugeDefer non-critical widgets and split the chart code
Ecommerce listing is slowOversized images and review/inventory N+1 queriesFix image sizes and database include/select usage
Media article has poor LCPHero image loads late; ad tags run too earlyPrioritize the main image and delay third-party scripts
Admin search freezesLarge array filtering on the main threadMove heavy work, reduce complexity, or paginate

N+1 means the app fetches a list once, then runs another query for each row. It can look fine with 5 records and fail badly with 100. Caching means reusing a result for a short period, but user-specific or permission-sensitive data must not be placed into a shared cache.

Runnable Cache Example

This Express example compares a deliberately slow endpoint with a cached endpoint. Production systems often use Redis or a CDN, but an in-memory demo makes the behavior easy to see.

npm init -y
npm install express
node cached-api.mjs
// cached-api.mjs
import express from "express";
import { performance } from "node:perf_hooks";

const app = express();
const cache = new Map();
const ttlMs = 30_000;

async function loadProducts() {
  await new Promise((resolve) => setTimeout(resolve, 800));
  return [
    { id: 1, name: "Starter Plan", price: 1200 },
    { id: 2, name: "Pro Plan", price: 4800 },
  ];
}

async function cached(key, loader) {
  const now = Date.now();
  const hit = cache.get(key);
  if (hit && hit.expiresAt > now) return { data: hit.data, cache: "hit" };

  const data = await loader();
  cache.set(key, { data, expiresAt: now + ttlMs });
  return { data, cache: "miss" };
}

app.get("/api/products/raw", async (_req, res) => {
  const start = performance.now();
  const data = await loadProducts();
  res.json({ cache: "none", ms: performance.now() - start, data });
});

app.get("/api/products/cached", async (_req, res) => {
  const start = performance.now();
  const result = await cached("products", loadProducts);
  res.json({ ...result, ms: performance.now() - start });
});

app.listen(3000, () => {
  console.log("Open http://localhost:3000/api/products/raw");
});

Measure it from another terminal:

node measure-url.mjs http://localhost:3000/api/products/raw 3
node measure-url.mjs http://localhost:3000/api/products/cached 3

When asking Claude Code to add caching, specify the scope: “cache popular products for 30 seconds,” “do not cache inventory,” or “include the user ID in the cache key.”

Runnable Algorithm Example

Network is not always the bottleneck. Expensive synchronous JavaScript can hurt INP. This example replaces repeated includes scans with a Set.

// compare-lookup.mjs
import { performance } from "node:perf_hooks";

const a = Array.from({ length: 40_000 }, (_, i) => i);
const b = Array.from({ length: 40_000 }, (_, i) => i * 2);

function slowIntersection(left, right) {
  return left.filter((item) => right.includes(item));
}

function fastIntersection(left, right) {
  const rightSet = new Set(right);
  return left.filter((item) => rightSet.has(item));
}

function time(label, fn) {
  const start = performance.now();
  const result = fn();
  const ms = performance.now() - start;
  console.log(`${label}: ${ms.toFixed(1)}ms (${result.length} hits)`);
}

time("slow", () => slowIntersection(a, b));
time("fast", () => fastIntersection(a, b));
node compare-lookup.mjs

Common Failure Modes

Do not optimize only for a Lighthouse score. Lighthouse is lab data, while real users have different devices, networks, extensions, and cache states. Pair it with Search Console, real-user monitoring, and server logs.

Do not add cache without correctness rules. Prices, inventory, permissions, and personal data need clear cache keys, TTLs, and invalidation.

Do not add useMemo everywhere. React memoization should protect measured hot paths; otherwise it adds complexity and dependency-array bugs.

Do not prioritize every image. Above-the-fold images need stable dimensions and sometimes priority loading, but below-the-fold images should usually stay lazy.

Do not split bundles blindly. Use the ideas in Claude Code Bundle Analysis and Claude Code Code Splitting to compare route-level impact.

Add Rules to CLAUDE.md

## Performance rules
- Before optimizing, record the current metric and target metric.
- Prefer small, measurable changes over broad rewrites.
- Do not introduce shared cache for user-specific data.
- Avoid N+1 queries; use select/include or batching.
- Keep above-the-fold images sized and stable.
- After changes, report commands used for verification.

These rules keep Claude Code focused on measurable work instead of cosmetic changes.

Consulting CTA

ClaudeCodeLab can help audit existing web apps with Claude Code, improve Core Web Vitals, isolate API and database latency, and produce a practical performance runbook. For a useful first review, prepare the target URL, key screens, Lighthouse output, slow API logs, and the deadline for improvement.

Hands-On Result

Running the sample locally, the intentionally delayed /api/products/raw endpoint stays around the 800ms range, while /api/products/cached drops to a few milliseconds after the first request. The compare-lookup.mjs script also shows a large reduction when using Set. Real projects are messier, but the pattern holds: measure p75 before, change one thing, and measure again with the same tool.

#Claude Code #performance #optimization #Core Web Vitals #speed
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.