Skip to content
Back to skills

React Js Performance

ASecurity

Optimizes JavaScript execution performance in React applications through efficient event handling, debouncing/throttling, Web Worker offloading, and DOM operation reduction.

  • 4 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added June 12, 2026
developmentjavascripttypescriptgojavareactdebugginggitapiperformancedocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned June 12, 2026

npx -y skills add paulpas/agent-skill-router --skill react-js-performance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Js Performance?

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

Security grade badge for React Js Performance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/paulpas-react-js-performance/badge)](https://www.skillsdirectory.com/skills/paulpas-react-js-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: react-js-performance
description: Optimizes JavaScript execution performance in React applications through efficient event handling, debouncing/throttling, Web Worker offloading, and DOM operation reduction.
license: MIT
compatibility: opencode
archetypes:
  - tactical
  - generation
anti_triggers:
  - brainstorming
  - vague ideation
  - long-form architecture
response_profile:
  verbosity: low
  directive_strength: high
  abstraction_level: operational
metadata:
  version: "1.0.0"
  domain: coding
  triggers: react performance, javascript optimization, debounce, throttle, web worker, event delegation, main thread
  role: implementation
  scope: implementation
  output-format: code
  content-types: [code, guidance, do-dont, examples]
  related-skills: react-rerender-optimization, react-server-performance, react-bundle-size
  author: https://github.com/vercel-labs
  source: https://github.com/vercel-labs/agent-skills
---

# React JavaScript Performance Optimization

Optimizes JavaScript execution in React by profiling main thread activity, debouncing high-frequency events, offloading CPU-heavy work to Web Workers, and minimizing DOM operations. Keeps the UI responsive under the RAIL model (50ms task budget).

## TL;DR Checklist

- [ ] Profile with performance.now() and Chrome DevTools Performance tab before optimizing
- [ ] Debounce scroll/resize/input handlers with 250-400ms delay
- [ ] Throttle requestAnimationFrame-bound animations
- [ ] Offload parsing, crypto, and data transforms to Web Workers
- [ ] Use passive event listeners for scroll/touch/wheel events
- [ ] Batch DOM reads before writes using requestAnimationFrame
- [ ] Verify no main thread task exceeds 50ms

---

## When to Use

Use this skill when:

- Implementing scroll, resize, or touch event handlers with high fire rates
- Building components that parse large data sets on the main thread
- Identifying janky UI (dropped frames, delayed input response)
- Profiling and optimizing React render performance
- Adding animation-heavy interactions that compete with JS execution
- Debugging main thread tasks exceeding the 50ms RAIL budget

---

## When NOT to Use

Avoid this skill for:

- Network optimization (API call reduction, caching) — use `react-server-performance` instead
- Bundle size or code splitting concerns — use `react-bundle-size` instead
- React re-render optimization (useMemo, useCallback, React.memo) — use `react-rerender-optimization` instead
- CSS-level animations that don't involve JS — CSS handles these without JS intervention

---

## Core Workflow

1. **Profile JS Execution** — Open Chrome DevTools Performance tab or instrument with `performance.now()` to capture task durations.
   **Checkpoint:** Identify all long tasks (>50ms) and high-frequency event handlers.

2. **Identify Optimization Targets** — Look for:
   - Event handlers firing >30 times/second (scroll, resize, mousemove)
   - Synchronous CPU work >50ms (parsing, computation, DOM manipulation)
   - Forced synchronous layouts (DOM read after write within same frame)

3. **Apply Debouncing/Throttling** — Debounce input and resize handlers; throttle scroll and animation handlers.

4. **Offload to Web Workers** — Move parsing, crypto, data transforms, and any task >50ms to a dedicated Worker thread.

5. **Optimize Event Handling** — Use passive listeners and event delegation to reduce handler overhead.

6. **Verify** — Re-profile to confirm all main thread tasks stay under 50ms and frame rate is stable at 60fps.

---

## Implementation Patterns

### Pattern 1: Debouncing High-Frequency Events

```typescript
// ✅ GOOD: Debounced input handler with configurable delay
function useDebouncedCallback<T extends (...args: unknown[]) => void>(
  callback: T,
  delay: number = 300
): T {
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    return () => {
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
    };
  }, []);

  return useCallback(
    ((...args: unknown[]) => {
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
      timeoutRef.current = setTimeout(() => callback(...args), delay);
    }) as T,
    [callback, delay]
  );
}

// Usage in a search component
function SearchBox() {
  const [query, setQuery] = useState('');
  const debouncedSearch = useDebouncedCallback(
    (value: string) => performSearch(value),
    350
  );

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setQuery(e.target.value);
    debouncedSearch(e.target.value);
  };

  return <input type="text" value={query} onChange={handleChange} />;
}
```

### Pattern 2: Throttling vs. Debouncing (BAD vs. GOOD)

```typescript
// ❌ BAD: No throttling — fires hundreds of times during scroll
function ScrollSpy() {
  const [scrollY, setScrollY] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      setScrollY(window.scrollY);
      // Expensive layout calculation on every frame
      updateActiveSection();
    };
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  return <div>{/* ... */}</div>;
}

// ✅ GOOD: Throttled scroll handler with RAF synchronization
function ScrollSpy() {
  const [scrollY, setScrollY] = useState(0);
  const rafRef = useRef<number | null>(null);

  useEffect(() => {
    const handleScroll = () => {
      if (rafRef.current) return; // Skip if RAF already queued
      rafRef.current = requestAnimationFrame(() => {
        setScrollY(window.scrollY);
        updateActiveSection();
        rafRef.current = null;
      });
    };

    window.addEventListener('scroll', handleScroll, { passive: true });
    return () => {
      window.removeEventListener('scroll', handleScroll);
      if (rafRef.current) cancelAnimationFrame(rafRef.current);
    };
  }, []);

  return <div>{/* ... */}</div>;
}
```

### Pattern 3: Web Worker Offloading

```typescript
// ✅ GOOD: Offload expensive computation to a Web Worker
function useWorker<TInput, TOutput>(
  workerFactory: () => Worker,
  input: TInput | null
): { result: TOutput | null; error: Error | null; isProcessing: boolean } {
  const [result, setResult] = useState<TOutput | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const [isProcessing, setIsProcessing] = useState(false);
  const workerRef = useRef<Worker | null>(null);

  useEffect(() => {
    workerRef.current = workerFactory();

    workerRef.current.onmessage = (e: MessageEvent<TOutput>) => {
      setResult(e.data);
      setIsProcessing(false);
    };

    workerRef.current.onerror = (e: ErrorEvent) => {
      setError(new Error(e.message));
      setIsProcessing(false);
    };

    return () => {
      workerRef.current?.terminate();
    };
  }, [workerFactory]);

  useEffect(() => {
    if (input === null) return;
    setIsProcessing(true);
    workerRef.current?.postMessage(input);
  }, [input]);

  return { result, error, isProcessing };
}

// worker.ts — runs off the main thread
self.onmessage = (e: MessageEvent<ArrayBuffer>) => {
  const parsed = parseComplexData(e.data); // CPU-heavy work
  self.postMessage(parsed);
};
```

### Pattern 4: Event Delegation (BAD vs. GOOD)

```typescript
// ❌ BAD: Attaching listener to every list item — O(n) handlers
function ItemList({ items }: { items: Item[] }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id} onClick={() => handleItemClick(item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

// ✅ GOOD: Single delegated listener on the parent — O(1) handler
function ItemList({ items }: { items: Item[] }) {
  const handleClick = useCallback(
    (e: React.MouseEvent<HTMLUListElement>) => {
      const li = (e.target as HTMLElement).closest('li');
      if (!li?.dataset?.id) return;
      handleItemClick(li.dataset.id);
    },
    []
  );

  return (
    <ul onClick={handleClick}>
      {items.map((item) => (
        <li key={item.id} data-id={item.id}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}
```

---

## Constraints

### MUST DO
- Use `{ passive: true }` for scroll, touch, and wheel event listeners
- Debounce input handlers with 250-400ms delay
- Keep main thread tasks under 50ms (RAIL model)
- Use Web Workers for CPU-heavy operations (parsing, crypto, data transforms)
- Prefer CSS animations (transform, opacity) over JS-driven animation
- Use `will-change` sparingly on elements that actually animate
- Batch DOM reads before writes using requestAnimationFrame

### MUST NOT DO
- Block the main thread for more than 50ms at a time
- Use JavaScript for animations that CSS can handle (transforms, opacity)
- Attach individual event listeners to every list item — use event delegation
- Add `will-change` to too many elements (consumes GPU memory)
- Create new function or object references in event handler closures
- Read then write DOM properties in the same frame (forces synchronous layout)

---

## Related Skills

| Skill | Purpose |
|---|---|
| `react-rerender-optimization` | Reduce unnecessary re-renders with memo, useMemo, useCallback |
| `react-bundle-size` | Code splitting, tree shaking, and bundle analysis |
| `react-server-performance` | Server-side rendering and streaming optimization |
| `react-composition-patterns` | Component composition patterns for efficient rendering |

---

## Live References

> Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.

- [React Performance Optimization Guide](https://react.dev/learn/render-and-commit)
- [MDN: Event dispatch and passive listeners](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners)
- [Web Workers API (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
- [RAIL Model — Google Web Fundamentals](https://web.dev/articles/rail)
- [MDN: requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame)
- [Chrome DevTools Performance Reference](https://developer.chrome.com/docs/devtools/performance/reference)
- [CSS will-change Property (MDN)](https://developer.mozilla.org/en-US/docs/Web/CSS/will-change)

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.