Accelerate React: 6 Proven Tips for Optimizing Core Web Vitals

In this text, we introduce several optimization techniques to improve the Core Web Vitals metrics of websites built on React. We will focus primarily on Interaction to Next Paint (INP), which measures the speed of response to interactions.

Optimizing the speed of websites built on React involves optimizing long JavaScript tasks (measurable by the JS Long Tasks (JSLT) metric), which are closely tied to how React functions internally and are visible, among other things, in the TBT metric.

Is a React Website Automatically Fast? Not Quite

Internally, React employs several clever techniques that help enhance the speed of websites or web applications.

It efficiently updates and renders only components where data has changed. The hooks system partially protects the web from unwanted layout recalculations and so-called layout thrashing.

So, does this mean a website built on React will automatically be fast? The answer is simple: NO.

While declarative components make the code more predictable and easier to understand, careless manipulation with state or a multitude 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 React optimization tips that we recommend based on our work for clients. How do you keep your React code under control?

1) Reduce DOM Size

DOM size and its optimization are fundamental. What's not in the DOM doesn't need to be rendered, allowing the browser to relax.

In React, this truth holds twice as much weight. Fewer elements mean fewer components, which means less JavaScript to download and process.

In the case of SSR (server-side rendering), it also optimizes the time required to assemble the first HTML response. Plus, it has a smaller data size. Simply a win-win on all fronts.

Remove components that aren't crucial for SEO from the DOM entirely and load them via lazy loading:

import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

2) Create Two Versions of Components: Simple and Rich

Even if a component or its content is important for SEO or accessibility, it doesn't mean it has to be in full visual quality at the initial render. Especially if the component is not visible in the first viewport.

Optimization by splitting into a simple and a rich component is particularly effective for elements that repeat multiple times on a page. These are typically landing pages, product listings, or other offerings, as you see in the image:

Example of replacing a simple component with a rich one Example of a component that, at page load, provides only SEO-important data. This state is visually hidden from the user. The "rich variant" is activated when the component enters the viewport.

3) Use <Suspense>

The <Suspense> tag in React is primarily used for displaying placeholder content while lazy components are loading.

However, its hidden superpower is that <Suspense> enables concurrent rendering. This allows you to divide the page and its components into important and less important parts.

Visualization of a split react component tree Component tree split using the <Suspense> tag. The 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, the moment when server-side generated code is brought to life on the client side in the browser, is almost always a lengthy JavaScript task, so using <Suspense> helps.

But beware! Never wrap a large block with this tag, only smaller parts. Also, use <Suspense> sparingly for elements visible in the first viewport. Otherwise, it might be counterproductive.

4) Watch Out for Hydration Errors

At the end of hydration, a check is performed to see if the resulting element tree (DOM) corresponds to the server state. If not, an error occurs and a message is displayed:

Example of a hydration error in the console Display of a hydration error in the browser console.

At that moment, React invalidates all components on the page, and their update is carried out by client-side JavaScript, which degrades performance and can unpleasantly delay the LCP metric.

5) Be Cautious with useEffect()

useEffect isn't always asynchronous. If a user triggers an input (for example, a click), all React code is executed synchronously, including "effect hooks."

If the hook is used to defer a task to the next rendering 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 writing components that aren't available in client-side JavaScript but only on the server.

This means the client receives already rendered content and doesn't need to run JavaScript again to display or revitalize the content.

Conclusion

React is just a tool. The key is to understand it well. In this regard, we highly recommend the series of articles React Internals Deep Dive.

Check out other methods for optimizing INP.