Migration depuis Create React App
Ce guide vous aidera à migrer un site existant créé avec Create React App vers Next.js.
Pourquoi migrer ?
Plusieurs raisons peuvent vous pousser à passer de Create React App à Next.js :
Temps de chargement initial lent
Create React App utilise React exclusivement côté client. Les applications purement client-side, aussi appelées applications monopages (SPA), souffrent souvent d'un temps de chargement initial lent. Cela est dû à plusieurs facteurs :
- Le navigateur doit attendre que le code React et l'ensemble de votre bundle applicatif soient téléchargés et exécutés avant que votre code puisse envoyer des requêtes pour charger les données.
- Votre code applicatif grossit avec chaque nouvelle fonctionnalité et dépendance ajoutée.
Pas de découpage de code automatique
Le problème précédent de temps de chargement lent peut être partiellement résolu avec le découpage de code. Cependant, si vous essayez de le faire manuellement, vous risquez souvent de dégrader les performances. Il est facile d'introduire involontairement des cascades de requêtes réseau lors d'un découpage manuel. Next.js intègre un découpage de code automatique dans son routeur.
Cascades de requêtes réseau
Une cause fréquente de mauvaises performances survient lorsque les applications effectuent des requêtes client-serveur séquentielles pour récupérer des données. Un schéma courant dans une SPA consiste à afficher d'abord un contenu de substitution, puis à récupérer les données après que le composant a été monté. Malheureusement, cela signifie qu'un composant enfant qui récupère des données ne peut commencer qu'après que le composant parent ait fini de charger ses propres données.
Bien que Next.js prenne en charge la récupération de données côté client, il vous offre également la possibilité de déplacer cette récupération côté serveur, ce qui peut éliminer les cascades client-serveur.
États de chargement rapides et intentionnels
Avec la prise en charge intégrée du streaming via React Suspense, vous pouvez mieux contrôler quelles parties de votre interface utilisateur charger en premier et dans quel ordre, sans introduire de cascades réseau.
Cela vous permet de créer des pages qui se chargent plus rapidement et d'éliminer les décalages de mise en page.
Choisissez votre stratégie de récupération de données
Selon vos besoins, Next.js vous permet de choisir votre stratégie de récupération de données au niveau de la page ou du composant. Vous pouvez décider de récupérer les données au moment de la construction, à la demande côté serveur, ou côté client. Par exemple, vous pouvez récupérer des données depuis votre CMS et générer vos articles de blog au moment de la construction, ce qui permet ensuite une mise en cache efficace sur un CDN.
Middleware
Le Middleware Next.js vous permet d'exécuter du code côté serveur avant qu'une requête ne soit terminée. C'est particulièrement utile pour éviter d'afficher brièvement du contenu non authentifié lorsque l'utilisateur visite une page réservée aux utilisateurs connectés, en le redirigeant vers une page de connexion. Le middleware est aussi utile pour les tests et l'internationalisation.
Optimisations intégrées
Les images, polices et scripts tiers ont souvent un impact significatif sur les performances d'une application. Next.js inclut des composants qui les optimisent automatiquement pour vous.
Étapes de migration
Notre objectif avec cette migration est d'obtenir une application Next.js fonctionnelle le plus rapidement possible, afin que vous puissiez ensuite adopter les fonctionnalités de Next.js progressivement. Pour commencer, nous la conserverons comme une application purement côté client (SPA) sans migrer votre routeur existant. Cela minimise les risques de problèmes pendant la migration et réduit les conflits de fusion.
Étape 1 : Installer la dépendance Next.js
La première chose à faire est d'installer next
comme dépendance :
npm install next@latest
Étape 2 : Créer le fichier de configuration Next.js
Créez un fichier next.config.mjs
à la racine de votre projet. Ce fichier contiendra vos options de configuration Next.js.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // Génère une application monopage (SPA).
distDir: './dist', // Change le répertoire de build en `./dist/`.
}
export default nextConfig
Étape 3 : Mettre à jour la configuration TypeScript
Si vous utilisez TypeScript, vous devez mettre à jour votre fichier tsconfig.json
avec les modifications suivantes pour le rendre compatible avec Next.js :
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"./dist/types/**/*.ts"
],
"exclude": ["node_modules"]
}
Vous trouverez plus d'informations sur la configuration de TypeScript dans la documentation Next.js.
Étape 4 : Créer la mise en page racine
Une application utilisant le routeur App de Next.js doit inclure un fichier de mise en page racine, qui est un composant serveur React englobant toutes les pages de votre application. Ce fichier est défini au niveau supérieur du répertoire app
.
L'équivalent le plus proche dans une application CRA est le fichier index.html
, qui contient vos balises <html>
, <head>
et <body>
.
Dans cette étape, vous allez convertir votre fichier index.html
en un fichier de mise en page racine :
- Créez un nouveau répertoire
app
dans votre répertoiresrc
. - Créez un nouveau fichier
layout.tsx
dans ce répertoireapp
:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return null
}
export default function RootLayout({ children }) {
return null
}
Bon à savoir : Les extensions
.js
,.jsx
ou.tsx
peuvent être utilisées pour les fichiers de mise en page.
Copiez le contenu de votre fichier index.html
dans le composant <RootLayout>
précédemment créé, en remplaçant les balises body.div#root
et body.script
par <div id="root">{children}</div>
:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
Bon à savoir : Nous ignorerons le fichier manifest, les icônes supplémentaires autres que le favicon, et la configuration des tests, mais si ces éléments sont nécessaires, Next.js les prend également en charge.
Étape 5 : Métadonnées
Next.js inclut déjà par défaut les balises meta charset et meta viewport, vous pouvez donc les supprimer de votre <head>
:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
Tous les fichiers de métadonnées tels que favicon.ico
, icon.png
, robots.txt
sont automatiquement ajoutés à la balise <head>
de l'application tant qu'ils sont placés à la racine du répertoire app
. Après avoir déplacé tous les fichiers pris en charge dans le répertoire app
, vous pouvez supprimer leurs balises <link>
:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
Enfin, Next.js peut gérer vos dernières balises <head>
avec l'API Metadata. Déplacez vos dernières informations de métadonnées dans un objet metadata
exporté :
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export const metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
Avec ces modifications, vous êtes passé d'une déclaration manuelle dans votre index.html
à une approche basée sur les conventions de Next.js intégrée au framework (API Metadata). Cette approche vous permet d'améliorer plus facilement votre SEO et la partageabilité web de vos pages.
Étape 6 : Styles
Comme Create React App, Next.js prend en charge nativement les CSS Modules.
Si vous utilisez un fichier CSS global, importez-le dans votre fichier app/layout.tsx
:
import '../index.css'
// ...
Si vous utilisez Tailwind, vous devrez installer postcss
et autoprefixer
:
npm install postcss autoprefixer
Puis, créez un fichier postcss.config.js
à la racine de votre projet :
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Étape 7 : Créer la page d'entrée
Dans Next.js, vous déclarez un point d'entrée pour votre application en créant un fichier page.tsx
. L'équivalent le plus proche dans CRA est votre fichier src/index.tsx
. Dans cette étape, vous allez configurer le point d'entrée de votre application.
Créez un répertoire [[...slug]]
dans votre répertoire app
.
Comme ce guide vise d'abord à configurer Next.js comme une SPA (Single Page Application), vous avez besoin que votre point d'entrée capture toutes les routes possibles de votre application. Pour cela, créez un nouveau répertoire [[...slug]]
dans votre répertoire app
.
Ce répertoire est ce qu'on appelle un segment de route attrape-tout optionnel. Next.js utilise un routeur basé sur le système de fichiers où les répertoires définissent les routes. Ce répertoire spécial garantira que toutes les routes de votre application seront dirigées vers son fichier page.tsx
.
Créez un nouveau fichier page.tsx
dans le répertoire app/[[...slug]]
avec le contenu suivant :
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // Nous mettrons à jour ceci
}
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // Nous mettrons à jour ceci
}
Ce fichier est un composant serveur. Lorsque vous exécutez next build
, le fichier est prérendu en un asset statique. Il ne nécessite aucun code dynamique.
Ce fichier importe notre CSS global et indique à generateStaticParams
que nous allons uniquement générer une route, la route index à /
.
Maintenant, déplaçons le reste de notre application CRA qui s'exécutera uniquement côté client.
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
Ce fichier est un composant client, défini par la directive 'use client'
. Les composants clients sont toujours prérendus en HTML côté serveur avant d'être envoyés au client.
Comme nous voulons commencer avec une application purement côté client, nous pouvons configurer Next.js pour désactiver le prérendu à partir du composant App
.
const App = dynamic(() => import('../../App'), { ssr: false })
Maintenant, mettez à jour votre page d'entrée pour utiliser le nouveau composant :
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
Étape 8 : Mettre à jour les imports d'images statiques
Next.js gère les imports d'images statiques légèrement différemment de CRA. Avec CRA, importer un fichier image renvoie son URL publique sous forme de chaîne :
import image from './img.png'
export default function App() {
return <img src={image} />
}
Avec Next.js, les imports d'images statiques renvoient un objet. Cet objet peut ensuite être utilisé directement avec le composant <Image>
de Next.js, ou vous pouvez utiliser la propriété src
de l'objet avec votre balise <img>
existante.
Le composant <Image>
offre des avantages supplémentaires comme l'optimisation automatique des images. Le composant <Image>
définit automatiquement les attributs width
et height
de la balise <img>
résultante en fonction des dimensions de l'image. Cela évite les décalages de mise en page lors du chargement de l'image. Cependant, cela peut causer des problèmes si votre application contient des images dont une seule dimension est stylée sans que l'autre ne soit définie sur auto
. Lorsqu'elle n'est pas stylée sur auto
, la dimension prendra par défaut la valeur de l'attribut de dimension de la balise <img>
, ce qui peut entraîner une distorsion de l'image.
Conserver la balise <img>
réduira le nombre de modifications dans votre application et évitera les problèmes mentionnés ci-dessus. Vous pourrez ensuite éventuellement migrer vers le composant <Image>
pour profiter de l'optimisation des images en configurant un loader, ou en passant au serveur Next.js par défaut qui offre une optimisation automatique des images.
Convertissez les chemins d'import absolus pour les images importées depuis /public
en imports relatifs :
// Avant
import logo from '/logo.png'
// Après
import logo from '../public/logo.png'
Passez la propriété src
de l'image au lieu de l'objet image entier à votre balise <img>
:
// Avant
<img src={logo} />
// Après
<img src={logo.src} />
Alternativement, vous pouvez référencer l'URL publique de l'image en fonction du nom de fichier. Par exemple, public/logo.png
servira l'image à l'adresse /logo.png
pour votre application, qui serait la valeur de src
.
Avertissement : Si vous utilisez TypeScript, vous pourriez rencontrer des erreurs de type lors de l'accès à la propriété
src
. Vous pouvez les ignorer pour l'instant. Elles seront résolues à la fin de ce guide.
Étape 9 : Migrer les variables d'environnement
Next.js prend en charge les variables d'environnement .env
de manière similaire à CRA.
La principale différence est le préfixe utilisé pour exposer les variables d'environnement côté client. Changez toutes les variables d'environnement avec le préfixe REACT_APP_
en NEXT_PUBLIC_
.
Étape 10 : Mettre à jour les scripts dans package.json
Vous devriez maintenant pouvoir exécuter votre application pour tester si vous avez migré avec succès vers Next.js. Mais avant cela, vous devez mettre à jour vos scripts
dans votre package.json
avec les commandes liées à Next.js, et ajouter .next
, next-env.d.ts
et dist
à votre fichier .gitignore
:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
# ...
.next
next-env.d.ts
dist
Maintenant, exécutez npm run dev
et ouvrez http://localhost:3000
. Vous devriez voir votre application fonctionnant maintenant sur Next.js.
Étape 11 : Nettoyer
Vous pouvez maintenant nettoyer votre codebase des artefacts liés à Create React App :
- Supprimez
src/index.tsx
- Supprimez
public/index.html
- Supprimez la configuration de
reportWebVitals
- Désinstallez les dépendances de CRA (
react-scripts
)
Compatibilité avec les bundlers
Create React App et Next.js utilisent par défaut webpack pour le bundling.
Lors de la migration de votre application CRA vers Next.js, vous pourriez avoir une configuration webpack personnalisée que vous souhaitez migrer. Next.js prend en charge la fourniture d'une configuration webpack personnalisée.
De plus, Next.js prend en charge Turbopack via next dev --turbo
pour améliorer les performances de développement local. Turbopack prend également en charge certains loaders webpack pour la compatibilité et l'adoption progressive.
Prochaines étapes
Si tout s'est déroulé comme prévu, vous avez maintenant une application Next.js fonctionnelle exécutée comme une application monopage. Cependant, vous ne profitez pas encore de la plupart des avantages de Next.js, mais vous pouvez maintenant commencer à apporter des modifications progressives pour en tirer tous les bénéfices. Voici ce que vous pourriez faire ensuite :
- Migrer de React Router vers le Next.js App Router pour obtenir :
- Le découpage de code automatique
- Le rendu côté serveur en streaming
- Les composants serveur React
- Optimiser les images avec le composant
<Image>
- Optimiser les polices avec
next/font
- Optimiser les scripts tiers avec le composant
<Script>
- Mettre à jour votre configuration ESLint pour prendre en charge les règles Next.js
Bon à savoir : L'utilisation d'une exportation statique ne prend pas actuellement en charge l'utilisation du hook
useParams
.