Next.js 16 vs Remix
Next.js 16 (Turbopack par défaut, Partial Prerendering) contre Remix (Shopify, standards web) — comparatif des méta-frameworks React en 2026.
Une couche React pré-rendue côté serveur, zéro JS par défaut
Un mode d'usage classique et mature de React, rendu dans le navigateur
Sur les surfaces à contenu dense et critiques pour le SEO, RSC apporte un gain mesurable : dans l'exemple de react.dev, une bibliothèque de 75 Ko gzippés ne part jamais vers le client. Sur les panneaux internes fortement interactifs (dashboard, admin), le client-first offre à la fois plus de simplicité et une surface d'erreur réduite. L'avertissement critique est réel : passer un objet complet du Server au Client en tant que prop écrit tous les champs de cet objet dans le flight payload ; si la frontière est mal tracée, le gain s'érode silencieusement. Prenez la décision par la mesure, pas par la mode.
| Catégorie | React Server Components | Client-side React (SPA) |
|---|---|---|
| Performance | 8/10 | 6/10 |
| Facilité d'apprentissage | 4/10 | 8/10 |
| Écosystème | 7/10 | 9/10 |
| Communauté | 7/10 | 9/10 |
| Marché de l'emploi | 7/10 | 8/10 |
| Pérennité | 9/10 | 7/10 |
// Next.js App Router — Server Component (par défaut)
// app/posts/[slug]/page.tsx
import { db } from '@/lib/db';
import LikeButton from './like-button'; // Client Component (dans un fichier séparé avec 'use client')
interface PageProps {
params: Promise<{ slug: string }>;
}
export default async function PostPage({ params }: PageProps) {
const { slug } = await params;
// await direct côté serveur — pas de cascade client-serveur,
// des bibliothèques lourdes comme marked/sanitize-html n'entrent jamais dans le bundle client
const post = await db.post.findUnique({ where: { slug } });
if (!post) return <div>Introuvable</div>;
return (
<article>
<h1>{post.title}</h1>
{/* Seules les props sérialisables sont transmises au Client Component */}
<div dangerouslySetInnerHTML={{ __html: post.renderedHtml }} />
<LikeButton postId={post.id} initialCount={post.likeCount} />
</article>
);
}
// app/posts/[slug]/like-button.tsx
'use client';
import { useState, useTransition } from 'react';
export default function LikeButton({ postId, initialCount }: { postId: string; initialCount: number }) {
const [count, setCount] = useState(initialCount);
const [isPending, startTransition] = useTransition();
return (
<button
disabled={isPending}
onClick={() => startTransition(async () => {
setCount((c) => c + 1);
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
})}
>
J'aime ({count})
</button>
);
}// Vite + React — Client-side SPA
// src/pages/PostPage.tsx
import { useEffect, useState } from 'react';
import { useParams } from 'react-router';
interface Post {
id: string;
title: string;
renderedHtml: string;
likeCount: number;
}
export default function PostPage() {
const { slug } = useParams();
const [post, setPost] = useState<Post | null>(null);
const [isLiking, setIsLiking] = useState(false);
useEffect(() => {
// Toute la récupération de données se fait côté client — le risque de cascade commence ici
let cancelled = false;
fetch(`/api/posts/${slug}`)
.then((res) => res.json())
.then((data) => { if (!cancelled) setPost(data); });
return () => { cancelled = true; };
}, [slug]);
if (!post) return <div>Chargement...</div>; // Cet écran n'apparaît jamais sans l'exécution du JS
async function handleLike() {
setIsLiking(true);
setPost((p) => (p ? { ...p, likeCount: p.likeCount + 1 } : p));
await fetch(`/api/posts/${post!.id}/like`, { method: 'POST' });
setIsLiking(false);
}
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.renderedHtml }} />
<button disabled={isLiking} onClick={handleLike}>
J'aime ({post.likeCount})
</button>
</article>
);
}
// vite.config.ts — le code-splitting est délégué à Rollup
export default {
build: {
rollupOptions: {
output: { manualChunks: { vendor: ['react', 'react-dom', 'react-router'] } },
},
},
};Sur les surfaces à contenu dense et critiques pour le SEO, RSC apporte un gain mesurable : dans l'exemple de react.dev, une bibliothèque de 75 Ko gzippés ne part jamais vers le client. Sur les panneaux internes fortement interactifs (dashboard, admin), le client-first offre à la fois plus de simplicité et une surface d'erreur réduite. L'avertissement critique est réel : passer un objet complet du Server au Client en tant que prop écrit tous les champs de cet objet dans le flight payload ; si la frontière est mal tracée, le gain s'érode silencieusement. Prenez la décision par la mesure, pas par la mode.
Obtenir une consultation gratuiteSur les surfaces à contenu dense, critiques pour le SEO, où le chargement initial (LCP/TTI) compte (blog, page produit, page de comparaison), RSC apporte un gain mesurable — récupération de données côté serveur et moins de JS client. Selon les propres données de Next.js 16.3, le passage aux flux natifs de Node.js pour le rendu côté serveur permet de traiter 22 % de requêtes en plus sous charge. Sur les panneaux internes fortement interactifs (dashboard, admin), le client-first offre généralement une expérience de développement plus simple.