Accelerate React: 6 Proven Tips for Optimising Core Web Vitals

Michal MatuškaMichal MatuškaUpdated 1/21/20265 minutes reading

In this text, we'll introduce several optimisation techniques to improve Core Web Vitals for sites built on React. Our focus will primarily be on Interaction to Next Paint (INP), the speed of response to interactions.

Optimising the speed of React-based sites involves enhancing long JavaScript tasks (measurable by the JS Long Tasks (JSLT) metric), which are closely tied to React's internal workings and are visible in the TBT metric.

Is a React Site Automatically Fast? Think Again

React internally uses several clever techniques to aid the speed of websites or web applications.

It efficiently updates and renders only the components where data has changed. The hooks system somewhat shields the web from unwanted layout recalculations and the notorious layout thrashing.

Does this mean a site built on React will automatically be fast? The answer, simply put, is NO.

Declarative components do make code more predictable and easier to understand, but careless manipulation of state or numerous 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 now explore our recommended tips for optimising React based on our work with clients. How can you keep your React code under control?

1) Reduce DOM Size

The size and optimisation of the DOM is fundamental. What isn’t in the DOM doesn’t need to be rendered, allowing the browser to rest.

In React, this truth is doubly important. Fewer elements mean fewer components, which means less JavaScript to download and process.

In the case of SSR (server-side rendering), it also optimises the time needed to assemble the first HTML response, which is smaller in data size. A win-win situation all around.

Remove components that aren't crucial for SEO entirely from the DOM 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 must be in its full visual glory at the first render – especially if the component isn’t visible in the first viewport.

Optimisation by splitting into simple and rich components is especially effective for elements that repeat frequently on a page. These typically include navigation pages, product listings, or other offerings, as shown in the image:

Example of replacing a simple component with a rich one An example of a component that provides only SEO-important data upon page load. This state is visually hidden from the user. The "rich variant" activates when the component comes into the viewport.

3) Use <Suspense>

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

Few realise, however, that its hidden superpower is enabling concurrent rendering. This allows the page and its components to be divided into more and less important parts.

Visualisation of a divided React component tree Component tree divided using the <Suspense> tag. The red components are marked by this tag as less important.

In the case of SSR (server-side rendering), the effect of the <Suspense> tag is absolutely crucial.

Hydration, or reviving the page, is when server-side generated code is brought to life on the client side in the browser. It’s almost always a lengthy JavaScript task, so leveraging <Suspense> helps.

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

4) Be Wary of Hydration Errors

At the end of hydration, verification occurs to ensure the resulting element tree (DOM) matches the server state. If not, an error and a message occur:

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

At that moment, React invalidates all the components on the page, leading to their update by client-side JavaScript, degrading performance and potentially delaying the LCP metric.

5) Be Cautious with useEffect()

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

If the hook is used to defer a task to the next rendering cycle, it’s necessary 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 only available on the server, not in client-side JavaScript.

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

Conclusion

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

Explore more methods for optimising INP.