DEV Community

Sajeed Syed
Sajeed Syed

Posted on

How I Optimized a React App from Slow to Lightning Fast (Real Performance Guide)

Most React tutorials focus on building apps. Very few teach you how to fix performance issues which is exactly what companies care about.

πŸ‘¨β€πŸ’» Introduction

With around 3 years of experience building frontend applications using React, I’ve worked on developing scalable, user-friendly interfaces and solving real-world performance challenges.

In many projects, I noticed a common patternβ€”applications worked fine initially but started slowing down as features grew. This led me to explore performance optimization techniques and better state management approaches.

In this article, I’ll share how I improved a React application’s performance using techniques like memoization, code splitting, and modern server-state management with tools like TanStack Query.

The goal is simple: not just to build applications that work, but to build applications that are fast, efficient, and production-ready.

🐒 The Problem

The app had:

  • Slow initial load
  • Unnecessary re-renders
  • Large bundle size
  • Poor user experience on low-end devices

βœ… Step 1: Identifying the Bottleneck

Before optimizing, I used:

  • React DevTools Profiler
  • Chrome Performance tab

I discovered:

  • Components re-rendering unnecessarily
  • Large third-party libraries bloating bundle size

βœ… Step 2: Prevent Unnecessary Re-renders

❌ Before

function UserList({ users }) {
  return users.map(user => <UserCard user={user} />);
}
Enter fullscreen mode Exit fullscreen mode

Every parent render caused all UserCard components to re-render.

βœ… After (Using memo)

function UserList({ users }) {
  return users.map(user => <UserCard user={user} />);
}
Enter fullscreen mode Exit fullscreen mode
import React, { memo } from "react";
const UserCard = memo(({ user }) => {
  return <div>{user.name}</div>;
});
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Result: Reduced unnecessary renders significantly.

βœ… Step 3: Use useCallback & useMemo Wisely

❌ Problem

Functions recreated on every render:

const handleClick = () => {
  console.log("Clicked");
};
Enter fullscreen mode Exit fullscreen mode

βœ… Fix

import { useCallback } from "react";

const handleClick = useCallback(() => {
  console.log("Clicked");
}, []);
Enter fullscreen mode Exit fullscreen mode

βœ… Step 4: Code Splitting (Huge Impact)

Instead of loading everything upfront:

import Dashboard from "./Dashboard";
Enter fullscreen mode Exit fullscreen mode

βœ… Use Lazy Loading

import { lazy, Suspense } from "react";

const Dashboard = lazy(() => import("./Dashboard"));
Enter fullscreen mode Exit fullscreen mode
<Suspense fallback={<p>Loading...</p>}>
  <Dashboard />
</Suspense>
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Result: Reduced initial bundle size drastically.

βœ… Step 5: Remove Unused Dependencies

I found heavy libraries that were barely used.

βœ” Replaced large libraries with lighter alternatives
βœ” Removed dead code

βœ… Step 6: API Optimization

  • Implemented caching
  • Reduced redundant API calls
  • Used pagination instead of loading everything

βœ… Step 7: Smarter Data Fetching with React Query (TanStack Query)

Managing API state manually with useEffect quickly becomes messy:

  • Re-fetching logic
  • Caching
  • Loading states
  • Error handling

Instead of reinventing the wheel, I used TanStack Query.
❌ Before (Manual Fetching)

useEffect(() => {
  fetch("/api/users")
    .then(res => res.json())
    .then(setData);
}, []);
Enter fullscreen mode Exit fullscreen mode

βœ… After (React Query)

import { useQuery } from "@tanstack/react-query";

function Users() {
  const { data, isLoading, error } = useQuery({
    queryKey: ["users"],
    queryFn: () =>
      fetch("/api/users").then(res => res.json()),
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error...</p>;

  return data.map(user => <div key={user.id}>{user.name}</div>);
}
Enter fullscreen mode Exit fullscreen mode

Why This Is Better ???

  • βœ… Automatic caching
  • βœ… Background refetching
  • βœ… Deduplication of requests
  • βœ… Built-in loading & error states

πŸ‘‰ Result: Cleaner code + better performance without extra effort.

πŸ“Š Final Results

  • ⚑ Faster initial load time
  • πŸ”„ Fewer re-renders
  • πŸ“‰ Smaller bundle size
  • πŸ“± Better performance on mobile

🎯 Key Learnings

  • Optimization starts with measurement, not guessing
  • Small improvements compound into big gains
  • Clean architecture = better performance

πŸ’‘ Pro Tip

Don’t overuse optimization hooks (useMemo, useCallback). Use them only when needed.

🏁 Conclusion

Building a React app is one thing. Making it fast is what separates good developers from great ones.

If you can talk about performance with real examples, you’ll stand out instantly in interviews.

Top comments (0)