My React App Was Slow Until I Did This: Performance Tips for MERN Stack Developers
Prajesh Prajapati

Prajesh Prajapati @prajesh_16

About: Full-stack developer passionate about building modern web apps with React, Node.js, and TypeScript. Always learning and exploring new tech.

Location:
Vadodara, Gujarat
Joined:
Jun 14, 2025

My React App Was Slow Until I Did This: Performance Tips for MERN Stack Developers

Publish Date: Jun 22
3 0

🐢 My React App Was Slow Until I Did This…

Performance Optimization Tips for MERN Stack Developers

“Why is my React app so slow?”

That was me — frustrated with lag, excessive renders, and sluggish performance. As a MERN stack developer, I was so focused on features that I overlooked performance. Let me walk you through 8 concrete steps I took to speed up my app — from React tweaks to MongoDB query optimizations.


🚀 My Stack Setup

I built a full-stack task management app using:

  • 🧠 MongoDB + Mongoose (Database)
  • 🚪 Express.js (REST API)
  • ⚛️ React + Redux (Frontend)
  • ⚙️ Node.js (Server runtime)

Once the project grew to multiple components, filters, and interactions — it became noticeably slower. That’s when I dug into performance optimization.


🛠️ Step 1: Identify Rendering Bottlenecks

The first step is always profiling your app.

✅ What I Did:

I opened Chrome DevTools → Performance Tab and recorded interactions like page load, filtering, and typing in a search box.

❌ What I Found:

  • Components were re-rendering excessively, even when props didn’t change.
  • Expensive calculations were running on every render.

💡 The Fix: Use React.memo and useMemo

// ✅ Prevents re-render if props are unchanged
const TaskItem = React.memo(({ task }) => {
  return <li>{task.title}</li>;
});

// ✅ Memoize derived values (expensive calculations)
const highPriorityTasks = useMemo(() => {
  return tasks.filter(task => task.priority === "high");
}, [tasks]);
Enter fullscreen mode Exit fullscreen mode

🔄 This drastically reduced re-renders and improved interaction speed.


🧠 Step 2: Avoid Anonymous Functions in JSX

Every render creates a new function instance, which triggers re-renders in child components.

❌ Problem:

<button onClick={() => handleClick(task.id)}>Delete</button>
Enter fullscreen mode Exit fullscreen mode

✅ Solution:

Use useCallback to memoize functions:

const handleDelete = useCallback((id) => {
  // delete logic
}, []);

<button onClick={() => handleDelete(task.id)}>Delete</button>
Enter fullscreen mode Exit fullscreen mode

🔁 Now handleDelete has a stable reference and won't trigger unnecessary re-renders.


🐢 Step 3: Lazy Load Heavy Components

Don't load everything at once — lazy load pages and heavy components.

import { lazy, Suspense } from "react";

const TaskDetails = lazy(() => import("./TaskDetails"));

<Suspense fallback={<div>Loading...</div>}>
  <TaskDetails />
</Suspense>
Enter fullscreen mode Exit fullscreen mode

This reduced my initial bundle size significantly.


🧭 Step 4: Code Splitting with React Router

If you're using react-router-dom, code-split your routes:

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

<Routes>
  <Route path="/dashboard" element={<Dashboard />} />
  <Route path="/settings" element={<Settings />} />
</Routes>
Enter fullscreen mode Exit fullscreen mode

🧳 This defers route components until needed, speeding up your homepage.


🧱 Step 5: Normalize Redux/Context State

I initially had deeply nested objects, which caused re-renders and slow updates.

❌ Anti-pattern:

{
  users: [
    {
      id: 1,
      name: "Alice",
      tasks: [{ id: 1, title: "Fix bug" }]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

✅ Best practice:

{
  users: {
    1: { id: 1, name: "Alice" }
  },
  tasks: {
    1: { id: 1, title: "Fix bug", userId: 1 }
  }
}
Enter fullscreen mode Exit fullscreen mode

📦 Use Redux Toolkit to manage normalized state.


🔑 Step 6: Use Stable Keys in Lists

Always provide unique and stable keys when rendering lists.

{tasks.map(task => (
  <TaskItem key={task._id} task={task} />
))}
Enter fullscreen mode Exit fullscreen mode

🔄 This helps React efficiently update only the changed DOM nodes.


🔍 Step 7: Debounce Input/API Calls

When I implemented search, I was firing API requests on every keystroke.

❌ Bad:

<input onChange={(e) => searchTasks(e.target.value)} />
Enter fullscreen mode Exit fullscreen mode

✅ Good: Debounce the input

import { debounce } from 'lodash';

const debouncedSearch = useMemo(() =>
  debounce((query) => searchTasks(query), 500), []);

<input onChange={(e) => debouncedSearch(e.target.value)} />
Enter fullscreen mode Exit fullscreen mode

🔄 This reduced unnecessary API calls, improving both frontend and backend performance.


📡 Step 8: Optimize MongoDB Queries

Turns out, not all slowness is caused by React — sometimes your API is slow.

✅ What I Fixed:

  • Added indexes to MongoDB collections
  • Used .lean() for read queries to skip Mongoose overhead
  • Avoided large $or and $in queries without indexes
const tasks = await Task.find({ status: 'active' }).lean();
Enter fullscreen mode Exit fullscreen mode

🎯 Queries became 2–3x faster, which made frontend responses instant.


📊 Results: Before vs After

Metric Before Optimization After Optimization
Initial Load Time ~6.5 sec ~2.4 sec
Average Component Renders ~500+ <100
Search API Calls per second 15–20 ~2
React Memory Usage High Normal

🧠 Final Takeaways

Performance in a MERN app isn't magic — it's a series of small, intentional improvements:

  • 🚫 Avoid unnecessary re-renders with React.memo, useCallback, and useMemo
  • 🧠 Normalize global state
  • 🐢 Lazy load pages and debounce inputs
  • ⚙️ Optimize backend queries

🙋 Have You Faced This Too?

Let me know in the comments if your React app is slow and you're stuck. I'd love to help you debug or even collaborate on performance tips.


📬 Follow for More

I’ll be sharing more about MERN stack development, real-world problems, and practical solutions. Hit follow if you found this post helpful!

Thanks for reading ❤️


Comments 0 total

    Add comment