Skip to content
Back to skills

Dev React Perf

ASecurity

React/Next.js performance optimization. Points to Vercel's react-best-practices (70 rules by impact - waterfalls, bundle, re-renders) and adds what it leaves out - list virtualization, state colocation, profiling tools, Core Web Vitals targets. Trigger when the user wants to optimize rendering, reduce re-renders, or improve Core Web Vitals.

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 28, 2026
ai-agentsgobashreactnextjsgitapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add christopherlouet/claude-base --skill dev-react-perf --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dev React Perf?

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

Security grade badge for Dev React Perf
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/christopherlouet-dev-react-perf/badge)](https://www.skillsdirectory.com/skills/christopherlouet-dev-react-perf)

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: dev-react-perf
description: React/Next.js performance optimization. Points to Vercel's react-best-practices (70 rules by impact - waterfalls, bundle, re-renders) and adds what it leaves out - list virtualization, state colocation, profiling tools, Core Web Vitals targets. Trigger when the user wants to optimize rendering, reduce re-renders, or improve Core Web Vitals.
---

# React Performance (pointer + gaps)

**If the `vercel-react-best-practices` skill is installed, invoke it now** (Skill tool) and apply its rules first. Either way, check these before anything else — the first two are the vendor's CRITICAL categories, the last three the re-render traps this foundation sees most:

1. **Request waterfalls** (vendor, CRITICAL) — independent requests awaited one after another: start them together (`Promise.all`), await late, stream with Suspense.
2. **Bundle size** (vendor, CRITICAL) — whole-library or barrel imports (`import { x } from 'lodash'`): import the path; load rarely used heavy UI (modals, charts, editors) with `lazy()` / `next/dynamic`.
3. **Derived state** (vendor, re-render category) — never copy props into state through `useEffect` + `setState`: compute during render, `useMemo` if expensive.
4. **Memo that cannot work** (foundation) — a `memo` child receiving inline arrows or inline objects re-renders anyway: pass stable callbacks and hoisted constants.
5. **Context values** (foundation) — a provider `value={{ ... }}` built on every render re-renders every consumer: memoize it or split the context.

With the React Compiler enabled, items 4 and 5 are handled for you: it memoizes components and values itself (the vendor skill says the same of `memo`/`useMemo`).

Vercel Engineering publishes the canonical rule set at [`vercel-labs/agent-skills/skills/react-best-practices`](https://github.com/vercel-labs/agent-skills/tree/main/skills/react-best-practices) (MIT): 70 rules in 8 categories ranked by impact, starting with the two CRITICAL ones — eliminating request waterfalls and bundle size — then server/client data fetching, re-renders, rendering and JS micro-optimizations. Its companion [`composition-patterns`](https://github.com/vercel-labs/agent-skills/tree/main/skills/composition-patterns) covers compound components and boolean-prop explosion.

Why a pointer: the vendor maintains its rules with each React and Next.js release; this skill's former 211-line version had gone stale (FID, retired in 2024; a react-window API removed in v2). Measurements behind this change: `eval/skill-triggering/FINDINGS.md`.

## Delegate to the vendor skill

```bash
git clone --depth 1 https://github.com/vercel-labs/agent-skills ~/dev/vendor-skills/vercel
ln -s ~/dev/vendor-skills/vercel/skills/react-best-practices ./.claude/skills/react-best-practices
ln -s ~/dev/vendor-skills/vercel/skills/composition-patterns ./.claude/skills/composition-patterns
```

Recipe entry: [`docs/recipes/recommended-vendor-skills.md`](../../../docs/recipes/recommended-vendor-skills.md) §"Vercel — `vercel-labs/agent-skills`".

## What the vendor skill leaves out

Virtualization, state colocation and the profiling tools below appear in none of its 72 files (it does cover `content-visibility` for long lists, a lighter alternative).

### Long lists: virtualize

Mounting thousands of rows costs more than any memoization saves. Render only the visible window:

```tsx
import { useRef } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';

export function VirtualList({ items }: { items: { id: number; name: string }[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const rows = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 48,
  });
  return (
    <div ref={parentRef} style={{ height: 400, overflow: 'auto' }}>
      <div style={{ height: rows.getTotalSize(), position: 'relative' }}>
        {rows.getVirtualItems().map((row) => (
          <div
            key={items[row.index].id}
            style={{ position: 'absolute', top: 0, width: '100%', height: row.size, transform: `translateY(${row.start}px)` }}
          >
            {items[row.index].name}
          </div>
        ))}
      </div>
    </div>
  );
}
```

Rows need a stable key (an id, never the index) and a known or estimated height. Under a few hundred rows, pagination or `content-visibility: auto` is often enough.

## State colocation (push state down)

```tsx
// BAD: state in the parent (re-renders everything)
function Page() {
  const [search, setSearch] = useState('');
  return (
    <div>
      <SearchBar value={search} onChange={setSearch} />
      <ExpensiveList /> {/* Unnecessary re-render! */}
      <Footer />       {/* Unnecessary re-render! */}
    </div>
  );
}

// GOOD: state in the component that uses it
function Page() {
  return (
    <div>
      <SearchSection />     {/* Internal state */}
      <ExpensiveList />     {/* Not affected */}
      <Footer />            {/* Not affected */}
    </div>
  );
}
```

Typical case: a clock, a timer or a search input whose state sits at the app root re-renders the whole tree on every tick or keystroke. Move that state into the small component that displays it.

When the state cannot move down (a provider, a layout that owns it), lift the expensive content up instead: pass it as `children`. Elements created by the parent are not re-created when the wrapper's state changes, so they do not re-render.

## Core Web Vitals targets

| Metric | Good | Needs work | Poor |
|--------|------|------------|------|
| LCP | < 2.5s | 2.5–4s | > 4s |
| INP | < 200ms | 200–500ms | > 500ms |
| CLS | < 0.1 | 0.1–0.25 | > 0.25 |

Measure before and after a change; the `qa-perf` skill holds the measurement workflow.

## Profiling tools

```bash
npx lighthouse https://example.com --view     # lab LCP / INP proxy (TBT) / CLS
ANALYZE=true npm run build                     # bundle composition (Next.js, with @next/bundle-analyzer wired in next.config)
```

- **React DevTools Profiler** — which components rendered, why, and for how long.
- **`why-did-you-render`** (`@welldone-software/why-did-you-render`) — logs avoidable re-renders in development.

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…