J'ai récemment migré mon portfolio de Next.js 14 vers Next.js 15 avec React 19. Voici un guide pratique basé sur mon expérience, avec les pièges à éviter.
params devient asynchroneC'est le changement le plus impactant. Dans Next.js 15, les params des pages dynamiques sont désormais des Promises.
Avant (Next.js 14) :
// ❌ Ne fonctionne plus
export default function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
return <div>{post.title}</div>;
}Après (Next.js 15+) :
// ✅ La nouvelle syntaxe
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return <div>{post.title}</div>;
}N'oubliez pas de mettre à jour generateMetadata aussi :
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
// ...
}forwardRefReact 19 permet de passer ref comme une prop normale. Tous les composants utilisant React.forwardRef peuvent être simplifiés.
Avant :
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, ...props }, ref) => {
return <button ref={ref} className={className} {...props} />;
}
);
Button.displayName = "Button";Après :
function Button({
className,
ref,
...props
}: ButtonProps & { ref?: React.Ref<HTMLButtonElement> }) {
return <button ref={ref} className={className} {...props} />;
}Plus besoin de displayName non plus.
next-themes v0.4Si vous utilisez next-themes, la v0.4 change l'export des types :
// ❌ Ancien import
import { type ThemeProviderProps } from "next-themes/dist/types";
// ✅ Nouveau
import { ThemeProvider } from "next-themes";
type Props = React.ComponentProps<typeof ThemeProvider>;Tailwind v4 est un changement majeur. Voici les étapes.
// postcss.config.js
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
},
};Plus besoin de autoprefixer — il est intégré.
tailwind.config.tsEn Tailwind v4, la configuration se fait dans le CSS :
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--color-primary: hsl(165 100% 50%);
--color-background: hsl(222 25% 4%);
/* ... */
}Le dark mode basé sur les classes nécessite une déclaration explicite :
@custom-variant dark (&:where(.dark, .dark *));En Tailwind v4, les couleurs CSS custom se déclarent avec --color- :
/* Les variables CSS sont directement utilisables comme couleurs Tailwind */
--color-primary: hsl(165 100% 50%);
/* Utilisable comme : bg-primary, text-primary, etc. */Framer Motion 12 change son chemin d'import :
// ❌ Ancien
import { motion } from "framer-motion";
// ✅ Nouveau
import { motion } from "motion/react";Le reste de l'API reste identique.
package.json (next, react, react-dom, tailwindcss, framer-motion)@tailwindcss/postcss aux devDependenciespostcss.config.jstailwind.config.ts vers globals.css avec @themeparams async dans les pages dynamiquesforwardRef (composants UI)framer-motion vers motion/reactLa migration demande du travail, mais les bénéfices sont réels : meilleure performance, API plus propre, et un système de design plus maintenable avec Tailwind v4. Prenez le temps de bien tester chaque page après la migration — les régressions CSS sont les plus courantes.