Most React apps start fast and slow down over time. Extra re-renders pile up, bundles grow, and large lists block the main thread. Here are the techniques that actually move the needle — from render control and virtualization to code splitting and SSR.

1. React Rendering Mechanism Optimization

React uses a virtual DOM to efficiently update the user interface. However, unnecessary re-renders can impact performance. Here’s how to optimize rendering:

1.1 Using React.memo and PureComponent

  • React.memo: Prevents re-renders for function components when props remain unchanged.
  • PureComponent: Optimizes class components by shallowly comparing props and state.

Example: React.memo

jsx
const ChildComponent = React.memo(({ value }) => {
  console.log('ChildComponent rendered');
  return <div>{value}</div>;
});

const ParentComponent = () => {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
      <ChildComponent value={count} />
    </div>
  );
};

In this example, ChildComponent only re-renders when its value prop changes.

1.2 Using useMemo and useCallback

  • useMemo: Caches expensive computations.
  • useCallback: Memoizes function references to avoid unnecessary re-creation.

Example: useMemo

jsx
const ExpensiveCalculation = ({ items }) => {
  const total = React.useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);

  return <div>Total: {total}</div>;
};

Example: useCallback

jsx
const IncrementButton = () => {
  const [count, setCount] = React.useState(0);

  const increment = React.useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  return <button onClick={increment}>Increment</button>;
};

2. Virtualization for Large Data Sets

Rendering large lists or tables can degrade performance. Virtualization ensures only visible elements are rendered.

2.1 Using react-window

Install the library:

npm install react-window

Example: Virtualized List

jsx
import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => <div style={style}>Item {index}</div>;

const VirtualizedList = () => (
  <List height={400} itemCount={1000} itemSize={35} width={300}>
    {Row}
  </List>
);

This renders only items visible within the viewport, reducing DOM nodes.

3. Lazy Loading and On-Demand Loading

3.1 Lazy Loading with React.lazy and Suspense

Lazy loading improves load times by deferring component loading until needed.

Example: Component Lazy Loading

jsx
const LazyComponent = React.lazy(() => import('./LazyComponent'));

const App = () => (
  <Suspense fallback={<div>Loading...</div>}>
    <LazyComponent />
  </Suspense>
);

3.2 Route-Based Lazy Loading with React Router

jsx
import React, { Suspense } from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';

const HomePage = React.lazy(() => import('./HomePage'));
const AboutPage = React.lazy(() => import('./AboutPage'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route path='/' exact component={HomePage} />
        <Route path='/about' component={AboutPage} />
      </Switch>
    </Suspense>
  </Router>
);

4. Build Optimization Techniques

4.1 Enable React Production Mode

Ensure production builds by running:

npm run build

4.2 Code Splitting with Webpack

Split bundles to load only necessary code:

json
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
};

4.3 Tree Shaking

Remove unused code:

json
module.exports = {
  mode: 'production',
};

4.4 Analyze Bundle Size

Install webpack-bundle-analyzer:

npm install —save-dev webpack-bundle-analyzer

Add to Webpack config:

json
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [new BundleAnalyzerPlugin()],
};

5. Server-Side Rendering (SSR) and Static Site Generation (SSG)

5.1 Server-Side Rendering with Next.js

Example: SSR with getServerSideProps

jsx
export async function getServerSideProps() {
  const data = await fetchData();
  return { props: { data } };
}

const Page = ({ data }) => <div>{data}</div>;
export default Page;

5.2 Static Site Generation with Next.js

Example: SSG with getStaticProps

jsx
export async function getStaticProps() {
  const data = await fetchData();
  return { props: { data } };
}

const Page = ({ data }) => <div>{data}</div>;
export default Page;

6. Image Optimization

6.1 Using next/image

jsx
import Image from 'next/image';

import Image from 'next/image';

const OptimizedImage = () => (
  <Image
    src='/path/to/image.jpg'
    alt='Description'
    width={500}
    height={300}
    priority
  />
);

6.2 Lazy Loading with Placeholder Images

Install react-lazy-load-image-component:

npm install react-lazy-load-image-component

Example:

jsx
import { LazyLoadImage } from 'react-lazy-load-image-component';

const LazyImage = () => (
  <LazyLoadImage
    alt='Image'
    src='/path/to/image.jpg'
    height='auto'
    width='100%'
  />
);

7. Conclusion

Start with the React DevTools profiler — find the components that re-render most, then apply React.memo, useMemo, or virtualization where the profiler shows the biggest wins. Code splitting and SSR are next once the render path is clean.