Back to Blog
react
performance
optimization
web-vitals

React Performance Optimization: From 8s to 3.1s Load Time

Practical techniques for optimizing React applications, including code splitting, memoization, and reducing render counts

March 10, 20243 min readBy Amaresh

React Performance Optimization: From 8s to 3.1s Load Time

Performance optimization is crucial for user experience. In this post, I'll share how we reduced our app's load time from 8 seconds to 3.1 seconds at Flipkart.

The Problem

Our warehouse management application was suffering from:

  • Long initial load times (8+ seconds)
  • Excessive re-renders
  • Large bundle sizes
  • Poor Core Web Vitals scores

Optimization Strategies

1. Code Splitting

We implemented route-based code splitting using React.lazy:

import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Reports = lazy(() => import('./pages/Reports'));

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/reports" element={<Reports />} />
      </Routes>
    </Suspense>
  );
}

Result: Reduced initial bundle from 2.5MB to 800KB

2. Reducing Re-renders

We used React.memo and useMemo strategically:

// Before: Re-rendered on every parent update
function ExpensiveComponent({ data }) {
  return <div>{/* Complex rendering */}</div>;
}

// After: Only re-renders when data changes
const ExpensiveComponent = React.memo(({ data }) => {
  const processedData = useMemo(() => {
    return expensiveCalculation(data);
  }, [data]);
  
  return <div>{/* Complex rendering */}</div>;
});

Result: Reduced render count by 50%, improved render duration by 50%

3. Debouncing API Calls

We implemented debouncing for search inputs:

import { useMemo } from 'react';
import debounce from 'lodash/debounce';

function SearchComponent() {
  const debouncedSearch = useMemo(
    () => debounce((query) => {
      fetchResults(query);
    }, 300),
    []
  );

  return (
    <input
      onChange={(e) => debouncedSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}

Result: Reduced API calls by 40%

4. Virtual Scrolling

For long lists, we implemented virtual scrolling:

import { FixedSizeList } from 'react-window';

function VirtualList({ items }) {
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>
          {items[index].name}
        </div>
      )}
    </FixedSizeList>
  );
}

Result: Improved rendering performance for 1000+ item lists

5. Image Optimization

We used Next.js Image component with proper sizing:

import Image from 'next/image';

function ProductCard({ product }) {
  return (
    <Image
      src={product.image}
      alt={product.name}
      width={300}
      height={300}
      loading="lazy"
      placeholder="blur"
    />
  );
}

Key Takeaways

  1. Measure First: Use React DevTools Profiler to identify bottlenecks
  2. Code Split: Don't load everything upfront
  3. Memoize Wisely: Not everything needs memoization
  4. Debounce User Input: Reduce unnecessary API calls
  5. Optimize Images: Use modern formats and lazy loading

Performance optimization is an ongoing process. Regular monitoring and incremental improvements lead to the best results.