- L’orientation future de Next.js est passionnante
- Il y a eu des problèmes autour des Server Actions, mais React 19 avec
useOptimistic et useFormStatus laisse entrevoir des améliorations possibles
- L’approche
useFetcher de Remix offre aussi une bonne DX
- Le PPR (Partial Pre-rendering) de Next.js et le nouveau système de cache granulaire se démarquent particulièrement
- Dans l’ensemble, l’impression est très positive
The Big Picture
- Le nouveau système de cache peut être activé de façon expérimentale dans
next.config.js
- Il est possible de définir des profils de cache pour configurer différentes durées d’expiration et fréquences de revalidation
// next.config.js
const config = {
experimental: {
// Active le nouveau système de cache. On peut désormais utiliser `use cache` dans le code
dynamicIO: true,
// Optionnel : configuration des profils de cache
cacheLife: {
blog: {
stale: 3600, // conservation du cache côté client : 1 heure
revalidate: 900, // rafraîchissement côté serveur : 15 minutes
expire: 86400, // durée de vie maximale : 1 jour
},
},
},
};
Utilisation de base de use cache
- La mise en cache est possible au niveau du fichier, du composant ou de la fonction via la directive
"use cache"
- L’exemple montre qu’il suffit d’ajouter
use cache pour appliquer facilement le cache
cacheTag, revalidateTag et autres permettent d’invalider le cache au moment souhaité
// 1. Mise en cache au niveau du fichier
"use cache";
export default function Page() {
return <div>Cached Page</div>;
}
// 2. Mise en cache au niveau du composant
export async function PriceDisplay() {
"use cache";
const price = await fetchPrice();
return <div>${price}</div>;
}
// 3. Mise en cache au niveau de la fonction
export async function getData() {
"use cache";
return await db.query();
}
Cache basé sur des tags
import { unstable_cacheTag as cacheTag, revalidateTag } from 'next/cache';
// Mettre en cache un groupe de données spécifique
export async function ProductList() {
'use cache';
cacheTag('products');
const products = await fetchProducts();
return <div>{products}</div>;
}
// Invalider le cache quand les données changent
export async function addProduct() {
'use server';
await db.products.add(...);
revalidateTag('products');
}
Profils de cache personnalisés
unstable_cacheLife permet de récupérer les profils de cache définis dans next.config.js
- Le profil déclaré dans le code (par exemple
"blog") est utilisé pour appliquer la politique de cache
import { unstable_cacheLife as cacheLife } from "next/cache";
export async function BlogPosts() {
"use cache";
cacheLife("blog"); // utilisation du profil de cache prédéfini pour le blog
return await fetchPosts();
}
Points importants mais faciles à manquer
Génération automatique des clés de cache
- Les
props et arguments du composant sont automatiquement inclus dans la clé de cache
- Les valeurs non sérialisables (comme les fonctions) sont traitées comme des « références non modifiables »
export async function UserCard({ id, onDelete }) {
"use cache";
// id est inclus dans la clé de cache
// onDelete est transmis mais n’a pas d’effet sur la mise en cache
const user = await fetchUser(id);
return <div onClick={onDelete}>{user.name}</div>;
}
Mélange de contenu dynamique et de contenu mis en cache
- On peut mixer les deux en passant du contenu dynamique comme enfant à l’intérieur d’un contenu mis en cache
- Un tableau de
cacheTag permet d’appliquer et d’invalider plusieurs tags à la fois
export async function CachedWrapper({ children }) {
"use cache";
const header = await fetchHeader();
return (
<div>
<h1>{header}</h1>
{children} {/* le contenu dynamique reste tel quel */}
</div>
);
}
export async function ProductPage({ id }) {
"use cache";
cacheTag(["products", `product-${id}`, "featured"]);
// l’invalidation est possible via n’importe lequel de ces tags
}
Hiérarchie de cache
- Si
"use cache" est déclaré au niveau le plus haut, toute la zone correspondante est mise en cache
- Certaines parties spécifiques (par exemple des sections dynamiques avec Suspense) peuvent être exclues de cette zone de cache
"use cache";
export default async function Page() {
return (
<div>
<CachedHeader />
<div>
<Suspense fallback={<Loading />}>
<DynamicFeed /> {/* contenu dynamique */}
</Suspense>
</div>
</div>
);
}
Sûreté de type
- Les chaînes comme les clés de cache et les profils de cache peuvent être gérées comme des constantes pour réduire l’usage de magic strings
- À l’image des patterns de React Query, utiliser une approche qui génère les tags est pratique
// Gérer les clés de profil de cache comme des constantes
export const CACHE_LIFE_KEYS = {
blog: "blog",
} as const;
const config = {
experimental: {
cacheLife: {
[CACHE_LIFE_KEYS.blog]: {
stale: 3600,
revalidate: 900,
expire: 86400,
},
},
},
};
Comment gérer efficacement les tags de cache
- Application d’un tag factory pattern à la manière de React Query
export const CACHE_TAGS = {
blog: {
all: ["blog"] as const,
list: () => [...CACHE_TAGS.blog.all, "list"] as const,
post: (id: string) => [...CACHE_TAGS.blog.all, "post", id] as const,
comments: (postId: string) =>
[...CACHE_TAGS.blog.all, "post", postId, "comments"] as const,
},
} as const;
// Définir les tags de cache
function tagCache(tags: string[]) {
cacheTag(...tags);
}
// Exemple d’utilisation
export async function BlogList() {
"use cache";
tagCache(CACHE_TAGS.blog.list());
}
3 commentaires
Je pense qu’il vaut mieux utiliser un framework comme Next.js ou Remix uniquement dans les cas où le SSR est nécessaire parce que le SEO est important.
En particulier, je pense qu’il faut être prudent avant d’adopter Next.js pour des services où le SEO n’est pas important, comme les produits B2B ou les back offices. En effet, les interfaces imposées par Next.js et sa complexité peuvent réduire la productivité du développement.
Personnellement, je trouve que lorsque le SEO n’est pas nécessaire, Vite + React est bien meilleur en termes de productivité de développement et de flexibilité.
Depuis la version 13, next.js est devenu vraiment utilisable, mais récemment je l’apprécie vraiment, vraiment beaucoup. J’ai l’impression qu’il va devenir le standard de fait de la stack technologique du développement web full-stack.