Back to blog
ReactTypescript

⚡Practical Ways to Optimize Performance in Your React App

React makes it easy to build complex UIs — but that power can come at the cost of performance if you're not careful. In this post, I'll walk you through proven strategies I’ve used in real-world projects to keep things fast, smooth, and efficient.

June 23, 2025Marcus Nguyen
⚡Practical Ways to Optimize Performance in Your React App

1. Avoid Unnecessary Re-renders

React's render cycle is one of the biggest performance costs. Prevent wasteful re-renders by:

  • Wrapping components with React.memo
  • Using useCallback and useMemo to memoize props and computations
  • Keeping your props shallow and focused
typescript
const ExpensiveComponent = React.memo(({ data }) => {
  return <div>{data.title}</div>;
});

2. Split Your Bundles

Why load everything upfront?

  • Use dynamic imports with React.lazy and Suspense
  • Split vendors and shared chunks using Webpack
typescript
const LazySettings = React.lazy(() => import('./Settings'));

3. Co-locate and Scope State

Global state is powerful — but overuse can cause unwanted re-renders.

Tips:

  • Use local state (useState) when possible
  • Use context selectors (zustand, jotai, or use-context-selector)
  • Avoid passing global state deep into component trees

4. Optimize Images and Static Assets

  • Use modern formats like WebP, AVIF
  • Lazy-load large images
  • Compress SVGs
  • Use loading="lazy" for below-the-fold content
markup
<img src="/banner.webp" loading="lazy" alt="Banner" />

5. Use Throttling & Debouncing Wisely

Expensive operations like scroll, resize, or map updates should be throttled:

typescript
const throttled = useRef(_.throttle(handleScroll, 200));

6. Prefer For Loops Over Higher-Order Functions in Hot Paths

While JavaScript’s .map(), .filter(), and .reduce() methods offer clean and expressive syntax, they introduce overhead in performance-critical code — especially in rendering-heavy React apps or environments like Electron.

❌ Example (more expressive, but slower in large arrays):

typescript
const ids = logs.filter(log => log.active).map(log => log.id);

✅ Example (faster and more memory-efficient):

typescript
const ids = [];
for (const log of logs) {
  if (log.active) ids.push(log.id);
}
Why it matters:
  • Avoids extra array allocations
  • Reduces closure and function call overhead
  • Runs faster in tight loops or large datasets
  • Easier for V8 (Chrome’s JS engine) to optimize
🧪 Real-World Benefit

In one of my Electron apps processing 10k+ map features:

  • Using .map() + .filter() took ~15–20ms
  • Switching to for loops brought it down to ~4–6ms
  • Resulted in smoother user interactions and lower memory usage
When to use for loops:
  • Inside render-intensive or animation frames
  • During transformation of large data arrays (GeoJSON, logs, telemetry)
  • In memory-constrained apps or embedded devices

Conclusion

React performance isn't about one magic fix — it's a mindset. Start by measuring, then optimize carefully and iteratively. Many of these tips are simple yet effective, and they can dramatically improve the user experience.