AccueilClientsExpertisesEstimateursOpen SourceBlogContact

24 mars 2025

How does the use API work with Next 15 and React 19?

7 minutes de lecture

How does the use API work with Next 15 and React 19?
🇫🇷 This post is also available in french

Data fetching and mutations are the cornerstones of many websites. For frontend developers, the goal is often to implement efficient interfaces while ensuring a smooth user experience. However, as there are increasingly numerous solutions for data management, how do you choose the right one?

In this article, we will explore the different solutions available with Next 15 and React 19. We will discover few new React 19 features like the use API, which was recently introduced and has now been stable for a few months.

Some reminders

Here, we will mainly focus on Client Components, Server Components, Server Actions, and how they work together to serve and manipulate data. If you are not familiar with these concepts, I recommend reading the articles Next 13.4: Overview of server actions and Focus on the new router app of Next 13. We should note that the data mutation aspect hasn't changed much since version 13, therefore we will focus on the part related to data fetching.

Key Concepts

Until version 15, there were four ways to fetch data:

Client Components

  • fetch: Retrieve data directly on the client side

This approach often involves a third-party library like React Query or use within an useEffect hook coupled with state management. This method allows the retrieval of data directly on the client side without going through the server. However, this method suffers from some limitations, especially in terms of performance since data will only be retrieved when the component mounts, which may give the user an impression of latency.

As a reminder, the fetch API is overloaded by NextJS and has additional features compared to the native fetch API, such as cache management and data revalidation.

  • server actions: Called from the client, executed on the server

Similar to a client-side fetch, this method offers a more streamlined developer experience. But it suffers from the same limitations as the client-side fetch. Moreover, server actions can open the door to security issues if one allows oneself to be lulled by its easy implementation and forgets to secure these functions - they are indeed endpoints, hence the need to protect them.

The use of server actions or the fetch API will be determined by the context of the application. If you are using an external API, you will need to use the fetch API on the client side. On the contrary, if you have an ORM like Prisma or Drizzle, you can use server actions which can use your data manipulation functions.

Server Components

  • fetch: Retrieve data on the server and then pass it to the UI
  • server function: Executed on the server and then passed to the UI

☝️ Here we specify that these are server functions because they are not necessarily server actions (without the use server directive), which avoids creating an endpoint that would not be used by a Client Component.

In both cases, we are dealing with asynchronous functions, which will be waited for by the server before being passed on to the UI. Thus, the page rendering will be blocked until the function is completed.

Assessment of traditional methods

Depending on the needs, data may need to be fetched client-side or server-side. If data fetching involves a prior user interaction, a Client Component will have to be used and thus a fetch or a server action, with the limitations this implies on performance. If no user interaction is required, a Server Component can be used and hence a server function or a server-side fetch, with the drawback of blocking the page rendering until the data is fetched.

For the part regarding dynamic data retrieval, little has changed since version 13. However, for the part involving static data retrieval, we now have greater flexibility, mainly around the user experience, which we will explore with the use of the use API.

The use API and the passage of Promises

As we have just seen, server-side data retrieval has limitations, especially in terms of performance, mainly due to the fact that page rendering is blocked until data is fetched because this data is fetched synchronously (using await). This can be problematic with a large amount of data or significant latency.

Since the introduction of Server Components, it has been possible to pass Promises to a Client Component, provided that the result of the Promise is a serializable value (so no functions). However, until now, it wasn't possible to block the rendering of the Client Component as long as the Promise is not resolved.

To address this issue, React 19 introduces a new API: use. We will see how this new API allows targeting the component that should wait for the Promise to be resolved, display a fallback until the Promise is resolved, or an error if the Promise is rejected.

Optimizing user experience

How does the transfer of Promises work with Next?

To understand the functionality of the use API, let's examine how Next 15 handles the transfer of Promises to a Client Component. For the example, we will retrieve data from all the planets in the solar system.

app/planets/page.tsx
import { getPlanets, type Planet } from '@/lib/planets'
import Planets from './Planets'

export default function PlanetsPage() {
  const planetsPromise: Promise<Planet[]> = getPlanets() // Asynchronous data retrieval

  return <Planets planetsPromise={planetsPromise} />
}

In this example, when the user navigates to the /planets page, the Next server will:

  1. Render the page, not blocked as the Promise is sent asynchronously to the client.

  2. Send the HTML to the client + the JavaScript bundle that will enable to build the page (the Client Component), without accounting for the Promise (in reality the resolution of the Promise has already started on the server side).

Server responses carry the header Transfer-Encoding: chunked to indicate that the response is divided into several parts. At this stage, the connection is kept open to receive future chunks.

  1. When the Promise is resolved on the server side, it sends a last chunk on the connection kept open until then.

  2. The client will receive the last chunk and will be able to build the page with the fetched data.

At this point, we can already see the major advantage of this approach which allows executing an asynchronous function on the server-side without blocking the page rendering and without using server actions implying a new HTTP call.

⚠️ Note that the asynchronous function will be executed even if it is not consumed by the Client Component. Make sure not to leave unnecessary Promises in the code, as this could impact performance.

How does the use API work?

Visually, there is not much change... for now, because we are not taking advantage of having non-blocking rendering, but this is where the use API comes into play. It will allow us to target the component that must wait for the Promise to be resolved and display a fallback until then or an error if the Promise is rejected.

If the PlanetsPage component contains metadata, such as a title or a description, these will be displayed before the Promise is resolved. The same applies to possible layouts or other elements other than the Planets component. This will make the page visually smoother for the user.

To take advantage of the benefits of the use API, we will therefore wrap the Planets component in a Suspense and/or an ErrorBoundary and use use within the client Planets component.

app/planets/page.tsx
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import Planets from './Planets'
import { getPlanets, type Planet } from '@/lib/planets'

export default function PlanetsPage() {
  const planetsPromise: Promise<Planet[]> = getPlanets() // Asynchronous data fetching

  return (
    <ErrorBoundary fallback={<div>Error</div>}>
      <Suspense fallback={<div>Loading...</div>}>
        <Planets planetsPromise={planetsPromise} />
      </Suspense>
    </ErrorBoundary>
  )
}
app/planets/Planets.tsx
'use client'

import { use } from 'react'

export default function Planets({ planetsPromise }) {
  const planets = use(planetsPromise)

  return (
    <ul>
      {planets.map(planet => (
        <li key={planet.id}>{planet.name}</li>
      ))}
    </ul>
  )
}

Here, React will detect that the Planets component uses the use API on a Promise, it will replace the Planets component with the Suspense fallback in the component tree and restore the Planets component when the Promise is resolved, or display the error if the Promise is rejected.

Passing Promises between Client Components

The primary objective of this new API is to be able to control loading and error states while retrieving data, but it can also be used to go further in the granularity of data fetching and drive our loading state closer to the concerned components. Sometimes, this might prompt us to reorganize our components to make them smaller and more independent, but in the end, it will help us improve readability, maintainability, and enhance user experience.

Other use cases

Among other use cases of the use API, we can mention:

  • Can be used in Server Components
  • Can be used conditionally
  • Can be used to fetch data from a Context

Downsides

Unfortunately, the use API requires implementing a Suspense and/or an ErrorBoundary for every component using it. This can quickly become tedious to manage, and may give a sense of boilerplate.

The loading.tsx and error.tsx files can be used to manage loading and error states globally or per page. They do not address the same level of granularity as a component-specific Suspense, but they can prevent potential oversights.

Conclusion

If you want to optimize the user experience of your pages, the use API is a solution not to be overlooked, provided your asynchronous functions aren't dependent on a user interaction. This will allow you to target the components affected by a loading or error state and avoid "flash" states that could occur with traditional loading state management.

Another significant advantage is that now your Server Components can be rendered immediately without waiting for the Promise to be resolved.

Sources

À découvrir également

Comment construire des applications IA avec Vercel AI SDK — Part I

06 Sep 2024

Comment construire des applications IA avec Vercel AI SDK — Part I

Vercel a dévoilé une nouvelle version de Vercel AI SDK, conçu pour simplifier le processus de développement d'applications IA en fournissant des outils et des bibliothèques prêts à l'emploi pour les développeurs. Je vous propose un tour d'horizon des exemples clés abordés par Nico Albanese, ingénieur chez Vercel, dans sa vidéo didactique publiée sur YouTube.

par

Vincent

AI et UI #1 - Filtres intelligents avec le SDK Vercel AI et Next.js

12 Jun 2024

AI et UI #1 - Filtres intelligents avec le SDK Vercel AI et Next.js

Dans ce premier article d’une série consacrée à l’IA et l’UI, je vous propose de découvrir différentes manières d’intégrer ces modèles d’IA dans vos applications React pour améliorer l’expérience utilisateur.

par

Baptiste

Retour d'expérience chez 20 Minutes : s'adapter au journalisme numérique

30 May 2024

Retour d'expérience chez 20 Minutes : s'adapter au journalisme numérique

Dans cet article, vous ne trouverez pas de snippets ou de tips croustillants sur React, seulement le récit d'une aventure plutôt ordinaire à travers le développement et l'évolution d'un projet technique.

par

Vincent

Premier Octet vous accompagne dans le développement de vos projets avec nextjs

En savoir plusNous contacter
18 avenue Parmentier
75011 Paris
+33 1 43 57 39 11
hello@premieroctet.com

Suivez nos aventures

GitHub
X
Flux RSS

Naviguez à vue