Introduction
There were times when the site felt painfully slow and completely unresponsive moments that left me frustrated and tempted to just give up that's why the Performance optimization is a critical aspect of developing web applications. Users expect applications to load quickly and respond to their interactions smoothly.
When a site takes over 1 second to become interactive, users often lose focus, and their experience feels interrupted.
No lengthy talk here’s the result that speaks for itself.
Back then, performance rate was crawling at 47
Voila 🎉 near-perfect 96 in performance.
Want to check your site’s performance? Just head over to Google’s performance tool pagespeed.web.dev drop site URL, and hit Analyze. You can also use Chrome DevTools open Inspect, navigate to the Lighthouse tab, choose your desired device, mode, and categories, then click Analyze page state to see how your site stacks up.
Optimizing performance in the React ecosystem especially when using frameworks like Next.js, offers many tools to improve mobile and web performance, which is key to delivering fast, responsive applications that keep users engaged and satisfied.
First need to understand the Core Web Vitals. The Foundation of Web Performance.
Core Web Vitals are a set of metrics defined by Google to measure the quality of a user's experience on a website. These metrics focus on three key aspects: loading, interactivity, and visual stability.
Few Core Web Vitals
-
First Contentful Paint (FCP)
- What it measures: Loading performance (time to render the largest visible element)
- Good threshold: Less than 2.5 seconds
-
Largest Contentful Paint (LCP)
- What it measured: Time from first user interaction to browser response
- Good threshold: Less than 2.5 seconds
-
Cumulative Layout Shift (CLS)
- What it measures: Visual stability (unexpected layout shifts)
- Good threshold: Less than 0.1 seconds
-
Total Blocking Time (TBT)
- What it measured: Total amount of time a webpage is blocked from responding
- Good threshold: Less than 200 milliseconds
Here is the Diagnostics, Inspect each section individually and focus on improving the essentials.
let's break down the techniques for optimizing the performance of our React/Next.js application.
-
Optimize the CSS
- In this project, there were some CSS files used in a few components, each containing around 4,500 lines of code. We removed unused styles, optimized, and minified the CSS, reducing the total lines by 50%.
- Since CSS is render-blocking, it loads before the page is rendered, which can negatively impact performance if not optimized. This improves the FCP
- Add
<link>
tag in the<head>
to Prefetch and preload<link rel="preload">
or<link rel="prefetch">
this really helps to improve my performance.
<link rel="preload" as="image" href="/herobg.webp" fetchPriority="high" />
<link
rel="preload"
href="/styles/globals.css"
as="style"
onLoad="this.onload=null;this.rel='stylesheet'"
/>
<noscript>
<link rel="stylesheet" href="/styles/globals.css" />
</noscript>
-
Reduce JavaScript Bundle Size
- Mobile devices struggle with parsing and executing large JS bundles. This improves the LCP
- Use dynamic imports with SSR disabled for non-critical components.
-
Reduce Initial Load
- Lazy-load non-critical components.
- Use dynamic imports
(next/dynamic)
or load below-the-fold sections conditionally.
const NonCriticalComponent = dynamic(() => import('../components/NonCriticalComponent'), {
ssr: false,
});
-
Optimize Images
- Mobile devices have smaller screens and limited bandwidth.
- Use Next.js
next/image
which automatically serves optimized images. - Also, prefetch the image in the
<head>
tag withfetchPriority = "high"
to reduce initial rendering time. - Serve WebP or AVIF formats for better compression, avoid heavy images.
- Set proper image sizes via sizes attribute for responsive behavior.
<Image
src="/herobg.webp"
alt="hero-bg"
fill
priority // to load on priority
sizes="100vw"
className="object-cover"
/>
-
Optimize Fonts
- Use
font-display: swap, preload: true
to prevent blocking rendering. - Prefer system fonts or serve self-hosted fonts with proper subsets.
- Initially, our project used multiple fonts like Helvetica and Inter. To optimize performance, We minimized font usage and switched to using only Poppins.
- Use
import { Poppins} from 'next/font/google';
const poppins = Poppins({ subsets: ['latin'], display: 'swap', preload: true, });
-
Leverage
next/script
for control- Lazy load scripts in the
<head>
tag to improve initial rendering performance. - Using attributes like
async
ordefer
for non-critical scripts in the<head>
ensures they don’t block the DOM rendering process. - To optimize script loading, import also add the strategy to "lazyOnload"
- Lazy load scripts in the
<Script src="https://example.com/script.js" strategy="lazyOnload" />
-
Memoizationl
- In our project there is few components dealing with computationally intensive or frequently called functions with the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application.
- memoization:
React.memo()
,useMemo()
, anduseCallback()
.
const memoizedValue = useMemo(() => expensiveComputation(count), [count]);
const memoizedIncrement = useCallback(requiredFunction, [count]);
-
Code Splitting
- Code splitting in React is a powerful technique that breaks down large components into smaller, reusable pieces helping to reduce code redundancy and avoid duplication for better performance and maintainability.
There are countless ways to boost performance in a Next.js app, but these are the techniques I personally implemented and they made a measurable impact on improving web vitals and overall user experience.
Conclusion
I’ve implemented all the above techniques in my own Next.js project and honestly, it wasn’t all smooth sailing. There were moments of frustration, especially when I applied the wrong optimizations or misunderstood how certain features worked. But through trial and error, learning from performance bottlenecks, and diving deeper into how React really works under the hood, we started to see real improvements.
What I’ve shared above isn’t just theory these are practical, concise strategies that helped me boost performance in a meaningful way. If you’re going through a similar journey, I hope these insights save you some time (and debugging headaches).
So that’s it, folks! I’d love to hear your thoughts or experiences with performance optimization techniques.
Top comments (2)
Love how practical this is, especially breaking down what's actually worth focusing on. Did you hit any surprises with dynamic imports or image optimization that tripped you up at first?
Yes, Dynamic imports really do work wonders being able to load components only when needed made a noticeable difference. And image optimization seriously blew my mind too just tweaking formats like converting from JPG/PNG to WebP and AVIF and using next/image gave huge performance boosts without compromising quality.