Accelerate React: 6 Proven Tips for Optimizing Core Web Vitals
In this article, we will introduce several optimization techniques to improve Core Web Vitals metrics on websites built with React. Our focus will be on Interaction to Next Paint (INP), which measures response speed to interactions.
Optimizing the speed of React-based websites involves improving long JavaScript tasks (measurable by the JS Long Tasks (JSLT) metric), which are closely tied to how React functions internally and are visible in metrics like TBT.
Is a React Website Automatically Fast? Not Quite
React employs several clever techniques that assist in speeding up websites or web applications.
It efficiently updates and renders only the components where data changes. The hooks system also partially protects the site from unwanted layout recalculations and the dreaded layout thrashing.
So, does this mean a website built with React is automatically fast? The answer is straightforward: NO.
While declarative components make code more predictable and easier to understand, careless manipulation of state or the number of components almost certainly leads to sluggish interactivity.
It's a tool like any other, and it always depends on how well the developer knows it.
Let's dive into the tips for optimizing React that we recommend based on our work with clients. How do you keep your React code in check?
1) Reduce DOM Size
The size of the DOM and its optimization is fundamental. What isn't in the DOM doesn't need rendering, allowing the browser to rest a bit.
In React, this truth is doubly so. Fewer elements mean fewer components, which means less JavaScript to download and process.
In the case of SSR (server-side rendering), there's also an optimization in the time required to assemble the first HTML response. This response is also smaller in data size. Simply a win-win all around.
Remove components that aren't essential for SEO entirely from the DOM and load them with lazy loading:
import { lazy } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
2) Create Two Versions of Components: Simple and Full
Even if a component or its content is important for SEO or accessibility, it doesn't mean it needs to be fully rendered on the first paint. Especially if the component isn't visible in the initial viewport.
Optimization by splitting into simple and rich components is particularly effective for elements that appear multiple times on a page. These are typically navigation pages, product listings, or other offers, as illustrated:
Example of a component that, when the page loads, provides only SEO-important data. This state is visually hidden from the user. The "rich variant" activates when the component enters the viewport.
3) Use <Suspense>
The <Suspense> tag is primarily used in React to display placeholder content while lazy components are loading.
However, its hidden superpower is enabling concurrent rendering. The page and its components can thus be divided into more and less critical parts.
Component tree split using the <Suspense> tag. Red components are marked as less important by this tag.
In the case of SSR (server-side rendering), the effect of the <Suspense> tag is absolutely crucial.
Hydration, which is the moment when server-side generated code comes alive on the client side in the browser, is almost always a long JavaScript task, so using <Suspense> helps.
But beware! Never wrap large blocks with this tag, only smaller parts. Also, use <Suspense> sparingly for elements visible in the initial viewport, as it may otherwise be counterproductive.
4) Watch Out for Hydration Errors
At the end of hydration, verification occurs to ensure the resulting element tree (DOM) matches the state on the server. If not, an error is reported:
Display of a hydration error in the browser console.
At that moment, React invalidates all components on the page, triggering their update with client-side JavaScript, worsening performance and potentially delaying the LCP metric.
5) Be Cautious with useEffect()
useEffect is not always asynchronous. If a user triggers an input (such as a click), all React code is executed synchronously, including "effect hooks".
If the hook is used to actually defer a task to the next render cycle, you need to use setTimeout or another method.
useEffect(() => {
// Defer work to a separate task:
setTimeout(() => {
sendAnalytics();
}, 0);
}, []);
6) Server Components
Server Components are a relatively new feature in React. They allow you to write components that are not available in client-side JavaScript but exist solely on the server.
This means the client receives already rendered content and doesn't need to run JavaScript again to display or activate the content.
Conclusion
React is just a tool. The key is to understand it well. In this regard, we highly recommend the article series React Internals Deep Dive.
Check out other methods for optimizing INP.
Tags:INPJavaScriptReact