component-optimization

React performance optimization patterns - React.memo, useMemo, useCallback, code splitting, and preventing unnecessary re-renders

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "component-optimization" with this command: npx skills add code-visionary/react-router-skills/code-visionary-react-router-skills-component-optimization

Component Optimization

Master React performance optimization techniques. Learn how to identify and fix performance bottlenecks, prevent unnecessary re-renders, and create fast, responsive UIs.

Quick Reference

Prevent Re-renders with React.memo

const MemoizedComponent = React.memo(function Component({ data }) {
  return <div>{data}</div>;
});

Memoize Expensive Calculations

const result = useMemo(() => 
  expensiveCalculation(data), 
  [data]
);

Memoize Callback Functions

const handleClick = useCallback(() => {
  doSomething(value);
}, [value]);

Code Splitting

const LazyComponent = lazy(() => import('./Component'));

<Suspense fallback={<Spinner />}>
  <LazyComponent />
</Suspense>

When to Use This Skill

  • Component renders slowly or causes UI lag
  • Large lists or tables perform poorly
  • Callbacks cause child components to re-render
  • Initial page load is too slow
  • Bundle size is too large
  • Profiler shows unnecessary re-renders

Understanding Re-renders

When Does React Re-render?

React re-renders a component when:

  1. State changes - useState or useReducer updates
  2. Props change - Parent passes new props
  3. Parent re-renders - By default, all children re-render
  4. Context changes - Any context value updates

The Re-render Problem

// ❌ PROBLEM: Child re-renders unnecessarily
function Parent() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <ExpensiveChild /> {/* Re-renders every time! */}
    </div>
  );
}

Even though ExpensiveChild doesn't use count, it re-renders whenever the parent does.

Optimization Techniques

1. React.memo - Prevent Unnecessary Re-renders

React.memo skips re-rendering if props haven't changed.

// ✅ SOLUTION: Memoize the child
const ExpensiveChild = React.memo(function ExpensiveChild() {
  console.log("ExpensiveChild rendered");
  
  return (
    <div>
      {/* Expensive rendering logic */}
    </div>
  );
});

function Parent() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <ExpensiveChild /> {/* No longer re-renders! */}
    </div>
  );
}

When to use:

  • Component renders often but props rarely change
  • Component has expensive rendering logic
  • Component is large with many children

When NOT to use:

  • Props change frequently
  • Component is simple and fast
  • Premature optimization

2. useMemo - Memoize Expensive Calculations

useMemo caches calculation results between renders.

function ProductList({ products, filters }) {
  // ❌ Recalculates on every render
  const filtered = products.filter(p => 
    p.category === filters.category
  );
  
  // ✅ Only recalculates when dependencies change
  const filtered = useMemo(
    () => products.filter(p => p.category === filters.category),
    [products, filters.category]
  );
  
  return (
    <ul>
      {filtered.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}

When to use:

  • Expensive calculations (filtering, sorting, mapping large arrays)
  • Derived state from props or state
  • Creating objects/arrays passed as props to memoized children

When NOT to use:

  • Simple calculations (they're fast enough)
  • Values that change every render anyway
  • Premature optimization

3. useCallback - Memoize Functions

useCallback prevents creating new function instances on every render.

function TodoList({ todos }) {
  const [filter, setFilter] = useState("all");
  
  // ❌ New function every render, breaks React.memo
  const handleComplete = (id) => {
    completeTodo(id);
  };
  
  // ✅ Same function reference between renders
  const handleComplete = useCallback((id) => {
    completeTodo(id);
  }, []);
  
  return (
    <ul>
      {todos.map(todo => (
        <MemoizedTodoItem
          key={todo.id}
          todo={todo}
          onComplete={handleComplete}
        />
      ))}
    </ul>
  );
}

When to use:

  • Passing callbacks to memoized children
  • Functions used in dependency arrays
  • Functions passed to many children

When NOT to use:

  • Functions not passed as props
  • Parent component re-renders frequently anyway
  • Simple event handlers

4. Code Splitting with React.lazy

Split large components into separate bundles that load on demand.

import { lazy, Suspense } from "react";

// ✅ Only loads when needed
const AdminPanel = lazy(() => import("./AdminPanel"));
const Charts = lazy(() => import("./Charts"));

function Dashboard() {
  const [showAdmin, setShowAdmin] = useState(false);
  
  return (
    <div>
      <h1>Dashboard</h1>
      
      {showAdmin && (
        <Suspense fallback={<Spinner />}>
          <AdminPanel />
        </Suspense>
      )}
      
      <Suspense fallback={<ChartsSkeleton />}>
        <Charts />
      </Suspense>
    </div>
  );
}

When to use:

  • Large components not needed initially
  • Admin/authenticated sections
  • Modals and dialogs
  • Route-based code splitting

5. Virtualization for Long Lists

Render only visible items in long lists.

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

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

When to use:

  • Lists with 100+ items
  • Tables with many rows
  • Infinite scrolling
  • Chat messages or feeds

Advanced Patterns

1. Context Optimization

Split contexts to prevent unnecessary re-renders:

// ❌ Everything re-renders when anything changes
const AppContext = createContext({
  user: null,
  theme: "light",
  settings: {},
});

// ✅ Separate contexts for independent data
const UserContext = createContext(null);
const ThemeContext = createContext("light");
const SettingsContext = createContext({});

function App() {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState("light");
  const [settings, setSettings] = useState({});
  
  return (
    <UserContext.Provider value={user}>
      <ThemeContext.Provider value={theme}>
        <SettingsContext.Provider value={settings}>
          <AppContent />
        </SettingsContext.Provider>
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
}

2. Component Composition vs Props

Use children instead of passing components as props:

// ❌ Slower prop creates new component
function Parent({ content }) {
  const [state, setState] = useState(0);
  return <div>{content}</div>;
}

<Parent content={<ExpensiveComponent />} />

// ✅ Faster - children don't re-render
function Parent({ children }) {
  const [state, setState] = useState(0);
  return <div>{children}</div>;
}

<Parent>
  <ExpensiveComponent />
</Parent>

3. State Colocation

Move state closer to where it's used:

// ❌ Top-level state causes full tree re-render
function App() {
  const [color, setColor] = useState("blue");
  
  return (
    <div>
      <ColorPicker color={color} onChange={setColor} />
      <ExpensiveComponent />
      <AnotherExpensiveComponent />
    </div>
  );
}

// ✅ Isolated state only affects relevant components
function App() {
  return (
    <div>
      <ColorPickerSection />
      <ExpensiveComponent />
      <AnotherExpensiveComponent />
    </div>
  );
}

function ColorPickerSection() {
  const [color, setColor] = useState("blue");
  return <ColorPicker color={color} onChange={setColor} />;
}

Profiling Performance

1. React DevTools Profiler

// Add Profiler to measure render times
import { Profiler } from "react";

function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <AppContent />
    </Profiler>
  );
}

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime
) {
  console.log(`${id} (${phase}) took ${actualDuration}ms`);
}

2. Performance Timeline

// Mark performance in browser DevTools
function expensiveOperation() {
  performance.mark("expensive-start");
  
  // ... expensive work ...
  
  performance.mark("expensive-end");
  performance.measure(
    "expensive-operation",
    "expensive-start",
    "expensive-end"
  );
}

Common Issues

Issue 1: React.memo Not Working

Symptoms: Memoized component still re-renders Cause: Props include objects/arrays/functions that change reference Solution: Memoize prop values

// ❌ New object every render
<MemoizedChild config={{ theme: "dark" }} />

// ✅ Stable reference
const config = useMemo(() => ({ theme: "dark" }), []);
<MemoizedChild config={config} />

// ✅ Or use individual props
<MemoizedChild theme="dark" />

Issue 2: Over-Optimization

Symptoms: Code is complex but not faster Cause: Memoization overhead exceeds benefit Solution: Remove unnecessary optimization

// ❌ Overkill - simple operations are fast
const result = useMemo(() => a + b, [a, b]);

// ✅ Just calculate it
const result = a + b;

Issue 3: Stale Closures

Symptoms: useCallback uses old values Cause: Missing dependencies Solution: Add all dependencies

// ❌ Uses stale 'count'
const handleClick = useCallback(() => {
  console.log(count);
}, []);

// ✅ Always current
const handleClick = useCallback(() => {
  console.log(count);
}, [count]);

Best Practices

  • Profile before optimizing (use React DevTools)
  • Start with proper state colocation
  • Use React.memo for expensive components with stable props
  • Use useMemo for expensive calculations, not simple ones
  • Use useCallback for functions passed to memoized children
  • Split large components into smaller ones
  • Use code splitting for large, conditionally rendered components
  • Virtualize long lists (100+ items)
  • Split contexts to reduce re-render scope
  • Measure impact - ensure optimization actually helps

Anti-Patterns

Things to avoid:

  • ❌ Memoizing everything by default
  • ❌ Premature optimization without measuring
  • ❌ Using useMemo for simple calculations
  • ❌ Forgetting dependencies in hooks
  • ❌ Creating new objects/arrays in render
  • ❌ Deeply nested component trees
  • ❌ Massive components with too many responsibilities
  • ❌ Global state for local concerns

Performance Checklist

When debugging slow components:

  1. Identify - Use Profiler to find slow components
  2. Measure - Record baseline performance
  3. Analyze - Check why component renders
  4. Optimize - Apply appropriate technique
  5. Verify - Measure improvement
  6. Document - Note why optimization was needed

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

Performance Engineering System

Complete performance engineering system — profiling, optimization, load testing, capacity planning, and performance culture. Use when diagnosing slow applica...

Registry SourceRecently Updated
2500Profile unavailable
Coding

Performance Profiler

Analyze code performance to identify bottlenecks, detect redundant computations and blocking operations, and provide optimization recommendations.

Registry SourceRecently Updated
7410Profile unavailable
Coding

typescript-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

loader-action-optimizer

No summary provided by upstream source.

Repository SourceNeeds Review