DEV Community

Cover image for React.memo Is Not Enough — 4 Performance Fixes Senior Devs Actually Use
Harsh
Harsh

Posted on

React.memo Is Not Enough — 4 Performance Fixes Senior Devs Actually Use

Last month, I opened our React dashboard and watched it take 6 full seconds to load.

Six seconds. In 2026. Unacceptable.

Users were complaining. Bounce rate was climbing. And every time I clicked a button, the UI would freeze for half a second before responding.

I spent one day diagnosing and fixing it. The app now loads in under 1.5 seconds.

Here's exactly what I found — and how I fixed it. 🚀


Step 0: Never Optimize Without Measuring First

This is the mistake most developers make. They guess what's slow and start randomly adding useMemo and React.memo everywhere.

Don't do that.

Before touching a single line of code, open React DevTools Profiler:

  1. Install React DevTools in Chrome
  2. Open DevTools → Profiler tab
  3. Click Record
  4. Interact with your slow component
  5. Stop recording → Look at the Flame Chart

💡 What to look for: Any component taking more than 50ms is a red flag. In 2026, Google measures Interaction to Next Paint (INP) — anything blocking the main thread hurts your SEO and user experience.

In my case, the profiler revealed 3 major problems. Here's each one and how I fixed it.


Problem 1: Unnecessary Re-Renders Everywhere

This was the biggest culprit. Our parent component was re-rendering on every state change — and dragging every single child component with it.

The broken code:

// ❌ Every time `count` changes, ALL children re-render
function Dashboard() {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(count + 1)}>Update</button>
      <HeavyChart />        {/* Re-renders even though it doesn't use count */}
      <UserTable />         {/* Same problem */}
      <ActivityFeed />      {/* Same problem */}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Every click on that button was re-rendering HeavyChart, UserTable, and ActivityFeed — even though none of them used count.

The fix — React.memo:

// ✅ Now these components only re-render when their own props change
const HeavyChart = React.memo(function HeavyChart() {
  return <div>...chart...</div>;
});

const UserTable = React.memo(function UserTable({ users }) {
  return <table>...table...</table>;
});
Enter fullscreen mode Exit fullscreen mode

Result: Re-renders dropped by 70% for these components.

💡 2026 Update: If you're using the React Compiler (formerly React Forget), it handles memoization automatically. But if you haven't set it up yet, React.memo is still your best friend.


Problem 2: A 95KB JavaScript Bundle Loaded Upfront

Our app was shipping one massive JavaScript file containing every page, every component, and every library — even for pages the user might never visit.

I ran Webpack Bundle Analyzer to see what was inside:

npm install --save-dev webpack-bundle-analyzer
npx webpack-bundle-analyzer build/static/js/*.js
Enter fullscreen mode Exit fullscreen mode

What I found was shocking. A charting library we used on ONE page was taking up 38KB of our bundle — loading for every single user on every single page.

The fix — Lazy Loading with React.lazy:

// ❌ Before — everything loaded upfront
import HeavyChartPage from './pages/HeavyChartPage';
import AdminPanel from './pages/AdminPanel';
import ReportsPage from './pages/ReportsPage';

// ✅ After — only loaded when user visits that page
import { lazy, Suspense } from 'react';

const HeavyChartPage = lazy(() => import('./pages/HeavyChartPage'));
const AdminPanel = lazy(() => import('./pages/AdminPanel'));
const ReportsPage = lazy(() => import('./pages/ReportsPage'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/charts" element={<HeavyChartPage />} />
        <Route path="/admin" element={<AdminPanel />} />
        <Route path="/reports" element={<ReportsPage />} />
      </Routes>
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

Result: Initial bundle size dropped from 95KB to 31KB. Load time went from 6 seconds to 2.8 seconds — just from this one change.


Problem 3: Expensive Calculations Running on Every Render

Our dashboard had a filtering + sorting feature for a table with 2,000+ rows. The filter function was running on every single render — even when the data hadn't changed.

// ❌ This runs on every render — even unrelated ones
function UserTable({ users, searchQuery }) {
  // This heavy calculation runs EVERY time
  const filteredUsers = users
    .filter(user => user.name.includes(searchQuery))
    .sort((a, b) => a.name.localeCompare(b.name));

  return <table>...</table>;
}
Enter fullscreen mode Exit fullscreen mode

The fix — useMemo:

// ✅ Only recalculates when users or searchQuery actually changes
function UserTable({ users, searchQuery }) {
  const filteredUsers = useMemo(() => {
    return users
      .filter(user => user.name.includes(searchQuery))
      .sort((a, b) => a.name.localeCompare(b.name));
  }, [users, searchQuery]);

  return <table>...</table>;
}
Enter fullscreen mode Exit fullscreen mode

Result: The table interaction went from 340ms to 12ms. Instantly noticeable.


Bonus Fix: List Virtualization for Long Lists

Even after memoizing, rendering 2,000+ table rows in the DOM was still slow. The browser was creating 2,000 DOM nodes even though the user could only see 20 at a time.

The fix — react-window:

npm install react-window
Enter fullscreen mode Exit fullscreen mode
import { FixedSizeList } from 'react-window';

// ❌ Before — renders ALL 2000 rows in the DOM
function UserList({ users }) {
  return (
    <div>
      {users.map(user => <UserRow key={user.id} user={user} />)}
    </div>
  );
}

// ✅ After — only renders ~20 visible rows at any time
function UserList({ users }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      <UserRow user={users[index]} />
    </div>
  );

  return (
    <FixedSizeList
      height={600}
      itemCount={users.length}
      itemSize={50}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}
Enter fullscreen mode Exit fullscreen mode

Result: Scrolling through 2,000 rows became buttery smooth.


The Final Results

Here's what one day of focused performance work achieved:

Metric Before After
Initial Load Time 6.0s 1.4s
Bundle Size 95KB 31KB
Table Interaction 340ms 12ms
Re-renders per click ~47 ~8

Your React Performance Checklist

Before optimizing, always profile first. Then work through this list:

1. ✅ Run React DevTools Profiler — find what's actually slow
2. ✅ Add React.memo to components that re-render unnecessarily
3. ✅ Run Webpack Bundle Analyzer — find what's bloating your bundle
4. ✅ Lazy load routes and heavy components with React.lazy
5. ✅ Wrap expensive calculations in useMemo
6. ✅ Virtualize long lists with react-window
7. ✅ Enable React Compiler if on React 19+ — auto memoization!
Enter fullscreen mode Exit fullscreen mode

The Real Lesson

Performance issues don't announce themselves. They sneak in slowly — one re-render here, one large import there — until suddenly your app feels like it's running through mud.

The fix isn't always complicated. In my case, four targeted changes — memoization, code splitting, useMemo, and list virtualization — cut load time by 77%.

The key is to measure first, fix second. Never optimize blindly.


Have you had a React performance nightmare in your own project? What was the culprit? Drop it in the comments — I'd love to compare war stories! 👇


Heads up: AI helped me write this.But the ideas, code review, and learning are all mine — AI just helped me communicate them better. I believe in being transparent about my process! 😊

Top comments (4)

Collapse
 
prajwal_zore_lm10 profile image
Prajwal zore

hey! i'm a beginner web developer ofcourse still learning and exploring things, the unnecessary re-render issue it happens with me everytime i tried to built something the problem is i can't understand that i don't need to re-render these things always but the states trigger them , i used to create separate props and states to solve this but now i know that there's other simpler way thanks this post really gave me something valuable.

Collapse
 
harsh2644 profile image
Harsh

Thanks so much for sharing your experience!

You're absolutely right that "why is everything re-rendering?" moment happens to every React developer. It's one of those things they don't teach in tutorials, but you only really understand once you've struggled with it yourself.

The fact that you were already using separate props and states shows you're thinking in the right direction. Now you've got more tools in your toolbox!

Keep building, keep breaking things, and keep learning. That's the only way to get better.

Would love to see what you build next!

Collapse
 
ruth_sushils_281f325c6584 profile image
Ruth Sushils

Thanks so much for sharing your experience!

Collapse
 
harsh2644 profile image
Harsh

You're most welcome, Ruth! Glad you found it helpful.

Have you run into any of these performance issues in your own projects? Would love to hear about your experience always great to learn from fellow devs.