DEV Community

Cover image for Why Your React App Feels Slow (Even When It Looks Fine) And How React Doctor Fixes It in Seconds 🧑‍💻⌨️
Krish Carter
Krish Carter

Posted on

Why Your React App Feels Slow (Even When It Looks Fine) And How React Doctor Fixes It in Seconds 🧑‍💻⌨️

You've been there: your React app feels sluggish, re-renders are happening more than you'd like, and debugging sessions stretch into the night. You profile with React DevTools, chase down memoization misses, and still wonder — "Why is this component re-rendering on every keystroke?"

The culprit is often not one big mistake, but dozens of small anti-patterns that accumulate over time. Unnecessary useEffect hooks computing derived state. Prop drilling through five levels instead of composition. Missing key props that silently break list reconciliation. Accessibility oversights that hurt real users.

These issues don't scream during development they whisper performance regressions, bugs during refactors, and frustrated users. In large codebases, they compound until your app feels "heavy" for no obvious reason.

Enter React Doctor
A free, open-source CLI tool that acts like a code physician. Run one command, and it scans your entire React project (Next.js, Vite, Remix, whatever), detects framework setup, React version, and compiler config, then flags issues with file-by-file precision. It outputs a 0-100 health score and prioritizes fixes that deliver the biggest wins.

In this post, you'll learn exactly how to use React Doctor, why its warnings matter, and how to fix the most common issues it surfaces. By the end, you'll have a repeatable workflow to keep your React code lean, accessible, and maintainable.

Table of Contents

  • What React Doctor Actually Does
  • Getting Started: One Command to Rule Them All
  • The Most Common Warnings (And Why They Hurt)
  • Fixing Unnecessary useEffect: The Silent Performance Killer
  • Beyond Hooks: Accessibility, Keys, and Architecture Smells
  • Advanced: Integrating React Doctor into Your Workflow
  • Common Gotchas When Interpreting Results
  • Wrapping Up: Make Code Health a Habi

What React Doctor Actually Does

React Doctor isn't a runtime profiler like React Scan — it's a static analyzer powered by fast Rust based tooling (Oxlint under the hood). It parses your code without running it, applies rules tuned for modern React (hooks, Server Components, server actions), and catches patterns that lead to bugs or slowness.

Getting Started: One Command to Rule Them All

Installation? None. Just npx it:

Bash

npx -y react-doctor@latest .
Enter fullscreen mode Exit fullscreen mode

The . tells it to scan the current directory. In seconds (yes, even on big monorepos), you'll see output like:

Text

React Doctor vX.Y.Z
Scanning project...
Framework: Next.js 15
React: 19.0.0-rc
Compiler: Turbopack

Health Score: 82/100

Critical (1):
- src/app/actions/deleteUser.ts:18 Missing authentication check in server action

Warnings (42):
- src/components/UserProfile.tsx:45 Derived state in useEffect — compute in render
- src/features/TodoList.tsx:22 Using index as key in mapped list
...

Accessibility (8):
- src/components/AnimatedModal.tsx:67 Missing prefers-reduced-motion check
Enter fullscreen mode Exit fullscreen mode

Gotcha!
If you're in a monorepo, cd into the app folder first or use --dir ./apps/web (check docs for flags).
Accessibility Note
Many warnings highlight a11y gaps, like missing reduced-motion handling that improve UX for users with vestibular disorders.

The Most Common Warnings (And Why They Hurt)

React Doctor surfaces patterns you've probably written without thinking:

  1. Derived state in useEffect— recomputes on every render cycle instead of during render.
  2. Uncleaned async in effects → race conditions on unmount.
  3. Index as key → broken animations/reordering in lists.
  4. Prop drilling → brittle components, hard refactors.
  5. Server action security → missing auth checks → vulnerability.
  6. No reduced-motion→ jarring experiences for sensitive users.

Fixing Unnecessary useEffect: The Silent Performance Kille

This is the #1 warning in most codebases. Classic anti-pattern:

tsx

function ExpensiveList({ items }) {
  const [sortedItems, setSortedItems] = useState([]);

  useEffect(() => {
    setSortedItems([...items].sort((a, b) => a.name.localeCompare(b.name)));
  }, [items]);

  return <ul>{sortedItems.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
Enter fullscreen mode Exit fullscreen mode

Why it's bad: Every time items changes → effect runs → state update → re-render → potential loop if parent re-renders often.

Fix —** compute during render:**

tsx

function ExpensiveList({ items }) {
  const sortedItems = useMemo(() => {
    return [...items].sort((a, b) => a.name.localeCompare(b.name));
  }, [items]);

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



**Tip**
`useMemo `is your friend here, but don't overuse it, only when the sort is expensive. React Doctor helps you spot when you need it.

## Beyond Hooks: Accessibility, Keys, and Architecture Smells

For keys in lists:
Bad:


Enter fullscreen mode Exit fullscreen mode

{items.map((item, index) => )}




Good:



Enter fullscreen mode Exit fullscreen mode

{items.map(item => )}



React Doctor flags index-as-key because reordering items causes unnecessary DOM mutations and lost state.

For accessibility animations without reduced-motion checks:

tsx


Enter fullscreen mode Exit fullscreen mode

// Add this wrapper
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');

if (prefersReducedMotion) {
// no animation
} else {
// animate
}



**Wrapping Up: Make Code Health a Habit**

Mastering tools like React Doctor shifts you from reactive firefighting to proactive quality. Your apps become faster, more accessible, and easier to maintain especially as teams grow.

Next steps:

Run `npx react-doctor@latest .` on your current project today.
Tackle the top 5 warnings.
Add it to CI for ongoing health checks.

Enter fullscreen mode Exit fullscreen mode

Top comments (0)