
Building Performant React Applications: A Practical Guide
Learn the essential techniques for building fast, responsive React applications — from code splitting and lazy loading to memoization and state management optimization.
Why Performance Matters
In today's web landscape, performance isn't just a nice-to-have — it's a critical factor in user retention, conversion rates, and SEO rankings. Studies show that a 1-second delay in page load time can result in a 7% reduction in conversions.
The Core Web Vitals
Google's Core Web Vitals have become the standard for measuring web performance:
- Largest Contentful Paint (LCP): Should occur within 2.5 seconds
- First Input Delay (FID): Should be less than 100 milliseconds
- Cumulative Layout Shift (CLS): Should be less than 0.1
Code Splitting: The Low-Hanging Fruit
One of the easiest ways to improve performance is through code splitting. Instead of loading your entire application at once, you load only what's needed for the current view.
React.lazy and Suspense
import { lazy, Suspense } from "react";
const Dashboard = lazy(() => import("./Dashboard"));
const Analytics = lazy(() => import("./Analytics"));
export default function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
);
}
Route-Based Splitting in Next.js
Next.js makes this even easier with file-system based routing and automatic code splitting:
// app/dashboard/page.tsx — automatically code-split
export default function DashboardPage() {
return <Dashboard />;
}
Memoization: Avoiding Unnecessary Re-renders
React re-renders components when their state or props change. But sometimes, components re-render when they don't need to. That's where memoization comes in.
React.memo
import { memo } from "react";
const ExpensiveComponent = memo(function ExpensiveComponent({
data,
}: {
data: ComplexData;
}) {
// This only re-renders when `data` changes
return <div>{/* expensive rendering */}</div>;
});
useMemo and useCallback
import { useMemo, useCallback } from "react";
function SearchResults({ query, items }: Props) {
// Only recompute when query or items change
const filtered = useMemo(
() => items.filter((item) => item.name.includes(query)),
[query, items]
);
// Stable callback reference
const handleClick = useCallback(
(id: string) => {
analytics.track("item_clicked", { id, query });
},
[query]
);
return (
<ul>
{filtered.map((item) => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
Image Optimization
Images are often the largest assets on a page. Optimizing them can dramatically improve load times.
Next.js Image Component
import Image from "next/image";
export default function Hero() {
return (
<Image
src="/hero.webp"
alt="Hero image"
width={1200}
height={600}
priority // Load above-the-fold images eagerly
placeholder="blur" // Show blur-up while loading
blurDataURL="data:image/webp;base64,..." // Tiny placeholder
/>
);
}
Responsive Images with Art Direction
<picture>
<source media="(min-width: 1024px)" srcSet="/hero-desktop.webp" />
<source media="(min-width: 640px)" srcSet="/hero-tablet.webp" />
<img src="/hero-mobile.webp" alt="Hero" loading="lazy" />
</picture>
State Management Best Practices
Poor state management is a common source of performance issues. Here are some guidelines:
Keep State Close to Where It's Used
// ❌ Bad: State lifted too high
function App() {
const [search, setSearch] = useState("");
return (
<div>
<Header search={search} onSearchChange={setSearch} />
<Sidebar />
<MainContent search={search} />
<Footer />
</div>
);
}
// ✅ Good: State in the component that needs it
function SearchPage() {
const [search, setSearch] = useState("");
return (
<div>
<SearchBar value={search} onChange={setSearch} />
<SearchResults query={search} />
</div>
);
}
Use Context Wisely
// Split contexts to prevent unnecessary re-renders
const ThemeContext = createContext<Theme>("light");
const UserContext = createContext<User | null>(null);
function App() {
return (
<ThemeContext.Provider value="dark">
<UserContext.Provider value={user}>
<Dashboard />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
Measuring Performance
You can't improve what you don't measure. Here are the tools I use:
- Lighthouse — Built into Chrome DevTools
- Web Vitals Library — Real user monitoring
- React DevTools Profiler — Component-level profiling
- Bundle Analyzer — Visualize bundle size
# Add bundle analyzer to Next.js
pnpm add @next/bundle-analyzer
# In next.config.ts
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
module.exports = withBundleAnalyzer({});
Real-World Results
After applying these techniques to a client's e-commerce site, we achieved:
| Metric | Before | After | Improvement |
|---|---|---|---|
| LCP | 4.2s | 1.8s | 57% faster |
| TTI | 5.1s | 2.3s | 55% faster |
| Bundle Size | 420KB | 180KB | 57% smaller |
| Lighthouse Score | 62 | 96 | +34 points |
Key Takeaways
- Start with measurements — know your baseline before optimizing
- Code split aggressively — load only what's needed
- Optimize images — they're usually the biggest bottleneck
- Memoize strategically — don't over-optimize, but target expensive computations
- Keep state local — avoid lifting state higher than necessary
Performance optimization is an ongoing process, not a one-time task. Make it part of your development workflow, and your users will thank you.
Want to discuss performance optimization for your project? I'm always open to new challenges. Get in touch and let's make your app blazing fast.
Related Articles

Getting Started with Next.js 16: What's New and Why You Should Care
Next.js 16 brings groundbreaking features including the Turbopack stable release, React 19 support, and enhanced server actions. Here's everything you need to know.

Understanding TypeScript Generics: From Basics to Advanced Patterns
A comprehensive guide to TypeScript generics — covering everything from simple type parameters to advanced patterns like conditional types, mapped types, and template literals.
Why Every Small and Medium Business Needs a Professional Website in 2026
Discover how a modern website helps small and medium businesses attract more customers, build trust, and increase sales