Page 404/500 personnalisée Next.js App Router
Page 404/500 personnalisée Next.js App Router se construit avec les conventions not-found.tsx, error.tsx et global-error.tsx. Placez not-found.tsx dans app pour les ressources introuvables, error.tsx pour les erreurs récupérables d’un segment, et global-error.tsx pour un crash du layout racine. Cette structure fournit une interface utile, un retour à l’accueil et un vrai statut 404 quand la page n’existe pas.
Dans l’App Router, une erreur 404 et une erreur 500 ne doivent pas être traitées de la même manière. La première répond à une URL ou une ressource absente. La seconde signale un défaut inattendu, comme une requête de base de données qui échoue. Ce guide montre une implémentation complète, compatible avec les versions récentes de Next.js.
Comprendre les fichiers spéciaux de l’App Router
Next.js associe chaque fichier spécial à un niveau de la hiérarchie des routes. Un fichier not-found.tsx affiche l’interface 404 lorsqu’un segment appelle notFound(). Un fichier error.tsx devient une frontière d’erreur React pour le segment et ses enfants. Le fichier global-error.tsx est réservé aux erreurs du layout ou du template racine, que la frontière locale ne peut pas intercepter.
app/
├── layout.tsx
├── page.tsx
├── not-found.tsx # 404 générale et notFound()
├── error.tsx # erreur inattendue du segment racine
├── global-error.tsx # erreur du layout racine
└── produits/
├── [slug]/
│ ├── page.tsx
│ ├── not-found.tsx
│ └── error.tsx
└── not-found.tsx
Cette granularité évite de remplacer toute l’application pour une panne isolée dans un tableau de bord. Elle permet aussi d’afficher un message cohérent avec le contexte, par exemple un lien vers les produits lorsqu’un article n’existe plus. Pour une erreur d’hydratation qui ressemble à un écran 500, consultez aussi notre guide sur le Next.js hydration error.
Le composant peut rester très léger, mais il doit répondre à trois attentes concrètes : identifier clairement la situation, offrir une action immédiate et ne pas créer une nouvelle dépendance vers le contenu manquant. Un lien vers l’accueil, une recherche ou une catégorie stable suffit souvent. Évitez les redirections automatiques vers l’accueil, car elles masquent les URL cassées et compliquent le diagnostic dans les outils de recherche.
Créer une page 404 avec not-found.tsx
Commencez par créer app/not-found.tsx. Ce composant reste un Server Component par défaut, ce qui suffit pour afficher du texte et des liens. Utilisez le composant Link afin de conserver la navigation client et ajoutez un titre explicite pour l’accessibilité.
import Link from 'next/link'
export default function NotFound() {
return (
<main aria-labelledby="not-found-title">
<p>Erreur 404</p>
<h1 id="not-found-title">Cette page n’existe pas</h1>
<p>L’adresse est incorrecte ou le contenu a été déplacé.</p>
<Link href="/">Retourner à l’accueil</Link>
</main>
)
}
Le fichier racine couvre les appels à notFound() qui ne disposent pas d’une version plus proche. Pour une identité visuelle complète, reprenez les classes de votre design system, mais évitez de dépendre d’un état client complexe. Une page 404 doit rester disponible même lorsque les données principales ne le sont plus.
Retourner un vrai 404 pour une ressource absente
Dans une page dynamique, ne retournez pas simplement un titre « introuvable » avec un statut 200. Appelez notFound() dès que la requête ne renvoie aucun résultat. Next.js arrête alors le rendu du composant et sélectionne le not-found.tsx du segment le plus proche.
import { notFound } from 'next/navigation'
import { getProduct } from '@/lib/products'
type Props = {
params: Promise<{ slug: string }>
}
export default async function ProductPage({ params }: Props) {
const { slug } = await params
const product = await getProduct(slug)
if (!product) {
notFound()
}
return <h1>{product.name}</h1>
}
Le statut HTTP est important pour le référencement. Un moteur doit comprendre qu’une URL supprimée n’est pas une page valide. Il faut donc distinguer l’absence attendue d’une exception serveur. Pour vérifier le résultat, utilisez curl -I sur une URL inexistante et contrôlez le code retourné, au lieu de vous fier uniquement au texte affiché dans le navigateur.
Personnaliser une 404 par section
Une boutique, une documentation et un espace membre n’ont pas les mêmes appels à l’action. Ajoutez app/produits/not-found.tsx pour proposer une recherche ou une liste de produits. Ajoutez app/docs/not-found.tsx pour renvoyer vers la documentation d’accueil. Next.js choisit automatiquement le fichier situé dans le segment concerné.
import Link from 'next/link'
export default function ProductNotFound() {
return (
<main>
<h1>Produit introuvable</h1>
<p>Ce produit n’est plus au catalogue.</p>
<nav aria-label="Solutions">
<Link href="/produits">Voir tous les produits</Link>
{' '}
<Link href="/contact">Contacter le support</Link>
</nav>
</main>
)
}
Ne dupliquez pas la logique de récupération dans ce composant. La page dynamique décide qu’une ressource manque, puis la page 404 présente une réponse. Cette séparation rend les tests plus simples et empêche une requête secondaire de provoquer une nouvelle erreur sur l’écran d’erreur.
Créer une page 500 avec error.tsx
Un fichier error.tsx doit être un Client Component, car il utilise une frontière React et propose souvent un bouton de récupération. Il reçoit l’erreur et une fonction de nouvelle tentative. En production, Next.js masque généralement le message serveur détaillé. Conservez le digest pour faire le rapprochement avec les logs sans exposer une requête SQL ou une clé interne.
'use client'
import { useEffect } from 'react'
export default function Error({
error,
unstable_retry,
}: {
error: Error & { digest?: string }
unstable_retry: () => void
}) {
useEffect(() => {
console.error('Erreur de segment', error.digest)
}, [error])
return (
<main role="alert">
<h1>Une erreur est survenue</h1>
<p>Réessayez. Si le problème persiste, revenez à l’accueil.</p>
<button type="button" onClick={() => unstable_retry()}>
Réessayer
</button>
</main>
)
}
La fonction de récupération relance le rendu des enfants du segment. Elle peut résoudre une panne transitoire, comme une requête réseau momentanément indisponible. Elle ne réparera pas une variable d’environnement absente ou une migration de base de données cassée. Ajoutez toujours un lien de sortie ou une navigation vers une zone stable.
Gérer le crash du layout avec global-error.tsx
Une erreur dans app/layout.tsx ne peut pas être capturée par le app/error.tsx du même niveau, puisque ce layout se trouve au-dessus de la frontière. Pour ce cas extrême, créez app/global-error.tsx. Ce composant remplace toute l’interface et doit donc fournir lui-même les balises html et body.
'use client'
export default function GlobalError({
unstable_retry,
}: {
unstable_retry: () => void
}) {
return (
<html lang="fr">
<body>
<main role="alert">
<h1>Le site rencontre un problème</h1>
<p>Rechargez la page ou revenez plus tard.</p>
<button onClick={() => unstable_retry()}>Recharger</button>
</main>
</body>
</html>
)
}
Comme ce composant contourne le layout, ses styles globaux et ses polices ne sont pas automatiquement disponibles. Préférez un style minimal intégré ou importez explicitement les ressources nécessaires. Ne mettez jamais dans cette page le message brut de error.message en production.
Ajouter une 404 globale pour les routes inconnues
Les versions récentes de Next.js proposent aussi global-not-found.tsx pour une URL qui ne correspond à aucun routeur, notamment avec plusieurs root layouts ou un segment dynamique au niveau racine. Cette convention est différente de not-found.tsx et peut être expérimentale selon votre version. Activez-la uniquement après avoir vérifié la documentation correspondant à votre version de Next.js.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
globalNotFound: true,
},
}
export default nextConfig
Une page globale contourne le rendu normal de l’application. Elle doit donc importer ses styles, ses polices et ses dépendances indispensables. Si votre projet ne possède qu’un layout racine classique, app/not-found.tsx est souvent plus simple et plus stable.
Tester les réponses 404 et 500 en production
Une page d’erreur n’est terminée que lorsque son statut, son contenu et sa récupération sont testés. Testez une route dynamique dont le slug n’existe pas, une route totalement inconnue et une erreur simulée côté serveur. Faites les essais en production ou avec un build local, car le mode développement affiche davantage de détails.
npm run build
npm run start
curl -I http://localhost:3000/produits/introuvable
curl -I http://localhost:3000/route-qui-n-existe-pas
# Vérifier le HTML sans exposer le message interne
curl -s http://localhost:3000/produits/introuvable | grep -E '404|introuvable'
Vérifiez aussi le clavier, le contraste, le titre de document et les liens de sortie. Une erreur doit rester compréhensible sur mobile. Pour une erreur côté navigateur liée à window, séparez le problème SSR de la page 500 avec notre guide Next.js window is not defined. Pour les appels API qui échouent uniquement dans le navigateur, notre article sur Next.js CORS error complète le diagnostic.
Checklist de mise en production
Cette checklist évite les erreurs les plus fréquentes lors d’un déploiement App Router :
□ not-found.tsx existe au niveau racine
□ notFound() est appelé quand une ressource manque
□ error.tsx commence par use client
□ un bouton de récupération utilise unstable_retry
□ global-error.tsx contient html et body
□ les messages internes ne sont pas affichés en production
□ les logs conservent le digest de l’erreur
□ curl confirme les statuts 404 attendus
□ les pages sont accessibles au clavier
□ les erreurs 500 sont testées après npm run build
La meilleure page 404 ne cherche pas à retenir artificiellement l’utilisateur. Elle explique ce qui s’est passé, propose un chemin clair et conserve un statut correct. La meilleure page 500 protège les détails techniques, permet une nouvelle tentative et donne aux logs assez de contexte pour corriger la cause.
Sources
- Next.js, convention not-found.js
- Next.js, convention error.js
- Next.js, gestion des erreurs App Router
- Next.js, fonction notFound
- React, Error Boundaries
- MDN, statut HTTP 404
FAQ : Page 404/500 personnalisée Next.js App Router
Quel fichier utiliser pour une page 404 dans Next.js App Router ?
Utilisez app/not-found.tsx pour la page 404 générale et appelez notFound() lorsqu’une ressource dynamique est absente. Vous pouvez placer un fichier not-found.tsx plus proche d’un segment pour une interface contextualisée.
Comment créer une page 500 personnalisée dans Next.js ?
Créez un fichier app/error.tsx avec la directive use client. Affichez un message générique, conservez le digest dans les logs et proposez une fonction unstable_retry() ou un lien vers une route stable.
Pourquoi error.tsx doit-il être un Client Component ?
error.tsx utilise une frontière d’erreur React et doit pouvoir gérer une interaction comme le bouton Réessayer. La directive use client est donc obligatoire au début du fichier.
Comment vérifier qu’une page Next.js renvoie bien 404 ?
Construisez l’application puis lancez-la en production avec npm run build et npm run start. Exécutez ensuite curl -I sur une URL inexistante et vérifiez que le serveur retourne le statut 404.
Quelle différence entre not-found.tsx et global-not-found.tsx ?
not-found.tsx est rendu dans la hiérarchie d’un segment après l’appel à notFound(). global-not-found.tsx traite les routes inconnues au niveau du routeur et contourne le layout, ce qui impose d’importer explicitement ses styles.
Commentaires (0)
Laisser un commentaire
Les commentaires sont modérés. Questions WordPress, cybersécurité ou dev web bienvenues.