Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
openaec-foundation avatar

React Impl Performance

  • 12 installs
  • 6 repo stars
  • Updated July 8, 2026
  • openaec-foundation/react-claude-skill-package

Helps with frontend development tasks.

About

react-impl-performance is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted development.

  • react-impl-performance
  • Frontend Development
  • AI-coding skill

React Impl Performance by the numbers

  • 12 all-time installs (skills.sh)
  • Ranked #1,643 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/react-claude-skill-package --skill react-impl-performance

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs12
repo stars6
Last updatedJuly 8, 2026
Repositoryopenaec-foundation/react-claude-skill-package

What it does

Helps with frontend development tasks.

Files

SKILL.mdMarkdownGitHub ↗

react-impl-performance

Quick Reference

Performance Optimization Tools

ToolPurposeReact VersionWhen to Use
React.memoSkip re-renders when props unchanged18 + 19Expensive component, same props frequently
useMemoCache expensive computation results18 + 19Calculation >1ms on target hardware
useCallbackStable function reference for children18 + 19Function prop to memo-wrapped child
React CompilerAutomatic memoization at build time19+Replaces manual memo/useMemo/useCallback
<Profiler>Measure render durations18 + 19Identify slow components
React DevToolsVisual profiling (flamegraph, ranked)18 + 19Interactive performance investigation
React.lazyCode splitting per route/component18 + 19Reduce initial bundle size
@tanstack/virtualVirtualize long lists18 + 19Lists with 1000+ items

Critical Warnings

NEVER optimize before measuring. ALWAYS use the Profiler component or React DevTools to identify the actual bottleneck first. Premature optimization adds complexity without measurable benefit.

NEVER wrap every component in React.memo. The shallow comparison itself has a cost. ONLY use memo when a component re-renders frequently with the same props AND rendering is noticeably slow.

NEVER use JSON.stringify in a custom arePropsEqual function for React.memo. This is slower than just re-rendering the component.

NEVER rely on useMemo as a semantic guarantee. React MAY discard cached values (on suspend, during development). Use useRef if you need a persistent reference.

---

Decision Tree: When to Optimize

Component renders slowly?
├── NO → STOP. Do not optimize.
└── YES → Measure with Profiler/DevTools
    ├── Re-renders with same props?
    │   ├── Props are primitives → Use React.memo
    │   ├── Props include objects → useMemo the object, then React.memo
    │   └── Props include functions → useCallback the function, then React.memo
    ├── Expensive computation during render?
    │   └── Use useMemo with dependency array
    ├── Large bundle size / slow initial load?
    │   ├── Route-based → React.lazy + Suspense
    │   └── Feature-based → Dynamic import + React.lazy
    ├── Long list (1000+ items)?
    │   └── Use @tanstack/virtual
    └── Using React 19?
        └── Enable React Compiler → removes need for manual memo/useMemo/useCallback

---

React.memo

Wraps a component to skip re-rendering when props have not changed (shallow Object.is comparison per prop).

import { memo } from 'react';

interface ExpensiveListProps {
  items: readonly string[];
  onSelect: (item: string) => void;
}

const ExpensiveList = memo<ExpensiveListProps>(function ExpensiveList({
  items,
  onSelect,
}) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item} onClick={() => onSelect(item)}>{item}</li>
      ))}
    </ul>
  );
});

ALWAYS ensure props passed to a memo component are referentially stable. Passing a new object or function literal on every render defeats memo entirely.

memo does NOT prevent re-renders caused by:

  • Internal state changes (useState, useReducer)
  • Context value changes (useContext)

---

useMemo

Caches the result of an expensive calculation between re-renders.

import { useMemo } from 'react';

function FilteredList({ items, query }: { items: Item[]; query: string }) {
  const filtered = useMemo(
    () => items.filter((item) => item.name.includes(query)),
    [items, query]
  );

  return <ul>{filtered.map((item) => <li key={item.id}>{item.name}</li>)}</ul>;
}

ALWAYS include every reactive value used inside the calculation in the dependency array. NEVER omit the dependency array — this recalculates every render, defeating the purpose.

---

useCallback

Returns a stable function reference between re-renders. Equivalent to useMemo(() => fn, deps).

import { useCallback } from 'react';

function Parent({ items }: { items: Item[] }) {
  const handleSelect = useCallback((id: string) => {
    console.log('Selected:', id);
  }, []);

  return <MemoizedChild items={items} onSelect={handleSelect} />;
}

ALWAYS use updater functions to remove state from the dependency array:

// WRONG: todos changes every update, useCallback is useless
const handleAdd = useCallback((text: string) => {
  setTodos([...todos, { id: nextId++, text }]);
}, [todos]);

// CORRECT: updater removes todos dependency
const handleAdd = useCallback((text: string) => {
  setTodos((prev) => [...prev, { id: nextId++, text }]);
}, []);

---

React Compiler (React 19+)

The React Compiler automatically applies memoization at build time, replacing manual memo, useMemo, and useCallback. When enabled, you do NOT need to write these manually.

Setup (Vite)

npm install -D babel-plugin-react-compiler@latest
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: ['babel-plugin-react-compiler'],
      },
    }),
  ],
});

The babel plugin MUST run first in the pipeline. See references/patterns.md for Next.js and Webpack setup.

Opt-Out Directive

function ProblematicComponent() {
  "use no memo";
  // Compiler skips this component
  return <div>Not compiled</div>;
}

Verification

In React DevTools, compiled components show a "Memo" badge with a sparkle icon.

ALWAYS install eslint-plugin-react-hooks@latest — it identifies Rules of React violations that prevent the compiler from optimizing a component.

---

Profiler Component

Measures render performance programmatically. ALWAYS use this to identify bottlenecks before optimizing.

import { Profiler } from 'react';

function onRender(
  id: string,
  phase: 'mount' | 'update' | 'nested-update',
  actualDuration: number,
  baseDuration: number,
  startTime: number,
  commitTime: number,
): void {
  console.log(`${id} [${phase}]: ${actualDuration.toFixed(2)}ms (base: ${baseDuration.toFixed(2)}ms)`);
}

function App() {
  return (
    <Profiler id="Dashboard" onRender={onRender}>
      <Dashboard />
    </Profiler>
  );
}
Callback ParameterMeaning
actualDurationTime spent rendering this commit (memoization benefit visible here)
baseDurationTime to render without any memoization (worst case)
phase'mount' = first render, 'update' = re-render

Compare actualDuration vs baseDuration to measure memoization effectiveness.

Caveats: Disabled in production builds by default. Use a profiling build for production measurement.

---

React DevTools Profiler

Use the browser extension Profiler tab for interactive investigation:

1. Flamegraph — visual tree of render durations. Gray = did not render (memoized) 2. Ranked chart — components sorted by render time (longest first) 3. "Why did this render?" — enable in Profiler settings to see the exact cause

ALWAYS check "Why did this render?" before adding memoization. Common causes:

  • Parent re-rendered (fix with memo)
  • Props changed (check referential equality)
  • Context changed (split contexts or use selectors)
  • Hook state changed (expected behavior)

---

Code Splitting with React.lazy

Split code by route or feature to reduce initial bundle size.

import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

ALWAYS use route-based splitting as the primary strategy — it provides the biggest impact with the least effort.

NEVER place lazy() calls inside a component. ALWAYS declare them at module level to prevent recreating the lazy component on every render.

---

Bundle Analysis

Use rollup-plugin-visualizer (Vite) to identify large chunks:

npm install -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    react(),
    visualizer({ open: true, filename: 'stats.html' }),
  ],
});

Run npm run build and inspect stats.html to find oversized dependencies.

---

Virtualization for Long Lists

NEVER render 1000+ DOM nodes. Use @tanstack/react-virtual to render only visible items.

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

function VirtualList({ items }: { items: string[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 35,
  });

  return (
    <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
        {virtualizer.getVirtualItems().map((virtualRow) => (
          <div
            key={virtualRow.key}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualRow.size}px`,
              transform: `translateY(${virtualRow.start}px)`,
            }}
          >
            {items[virtualRow.index]}
          </div>
        ))}
      </div>
    </div>
  );
}

---

Reference Links

  • references/examples.md — Complete optimization examples with before/after code
  • references/patterns.md — Performance patterns: React Compiler setup, context optimization, lazy loading strategies
  • references/anti-patterns.md — Common performance mistakes and premature optimization traps

Official Sources

  • https://react.dev/reference/react/memo
  • https://react.dev/reference/react/useMemo
  • https://react.dev/reference/react/useCallback
  • https://react.dev/reference/react/Profiler
  • https://react.dev/reference/react/lazy
  • https://react.dev/learn/react-compiler

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.