Guide complet Next.js Error Boundary : 5 techniques pour gérer élégamment les erreurs runtime

Dans le groupe ops, c’était la panique : « La page d’accueil ne s’ouvre plus ! Écran blanc partout ! »
Sur la plateforme de monitoring, un composant tiers en panne avait entraîné toute la page. L’utilisateur ne voyait qu’un blanc, sans le moindre message d’erreur. Une page qui tournait bien en prod s’effondrait parfois à cause d’un format de données incorrect ou d’un timeout API. Le try-catch classique ne couvre pas le rendu des composants React : résultat, écran blanc, fermeture de l’onglet.
Selon les études UX, un écran blanc fait fuir plus de 80 % des utilisateurs.
Heureusement, Next.js propose Error Boundary pour gérer ces erreurs runtime avec élégance : pas d’écran blanc, une UI de repli conviviale, parfois un bouton « Réessayer ». Ce guide couvre l’usage complet — de error.tsx à global-error.tsx, jusqu’aux spécificités des Server Components.
À la fin, vous saurez rendre votre app plus résiliente et éviter les réveils nocturnes pour corriger un bug en prod.
Pourquoi Error Boundary ? Les limites du traitement d’erreur classique
Au début avec React, je pensais que try-catch suffisait. La réalité m’a vite rattrapé.
Trois limites du try-catch
Première limite : il ne capture que les erreurs synchrones. JSON.parse(badData) dans un try, oui. Une erreur pendant le rendu d’un composant ? Non.
Deuxième limite : les erreurs asynchrones dans les gestionnaires d’événements. Un clic qui appelle une API en échec : le try-catch ne sert à rien, le contexte du try est déjà terminé.
Troisième limite, la plus grave : les erreurs de rendu React. Accéder à une propriété de undefined dans le return, et c’est l’écran blanc. try-catch ne protège pas.
Comment fonctionne React Error Boundary
React a introdu tôt Error Boundary. L’idée : l’arbre de composants comme des poupées russes ; l’erreur remonte jusqu’à la Error Boundary la plus proche.
Traditionnellement, un class component avec componentDidCatch et getDerivedStateFromError. Fastidieux, et incompatible avec les hooks des function components.
La solution Next.js
Avec l’App Router (Next.js 13+), Next.js encapsule Error Boundary : créez error.tsx dans un segment de route, et il devient automatiquement la limite d’erreur de ce segment. Pas de class component, pas de gestion d’état manuelle.
Point clé : Next.js gère erreurs serveur et client. Une erreur en rendu Server Component est capturée par le error.tsx le plus proche — impossible en React classique côté client seul.
Attention : error.tsx doit être un Client Component avec 'use client', car il utilise des hooks pour l’état d’erreur et la récupération.
Facebook Messenger en est l’exemple : sidebar, dialogue, saisie — chaque zone dans sa propre Error Boundary. Une zone plante, le reste continue. L’utilisateur ne remarque parfois rien.
Valeur centrale : empêcher qu’une erreur locale devienne une catastrophe globale.
Utiliser error.tsx — limite d’erreur locale
Passons à la pratique.
Structure de base : prise en main en 5 minutes
Créez error.tsx dans n’importe quel segment de route :
'use client'
import { useEffect } from 'react'
export default function Error({
error,
reset
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Envoyer l'erreur à Sentry ou autre plateforme
console.error('Erreur capturée:', error)
}, [error])
return (
<div className="flex flex-col items-center justify-center min-h-screen p-4">
<h2 className="text-2xl font-bold mb-4">Oups, un problème est survenu</h2>
<p className="text-gray-600 mb-4">
{error.message || 'Échec du chargement de la page'}
</p>
<button
onClick={() => reset()}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Réessayer
</button>
</div>
)
}
Points essentiels :
'use client'obligatoire en tête de fichier- Objet
error: message, stack, etdigest(Next.js 15) pour le suivi reset: re-rend le contenu sous la limite d’erreur — une chance de récupération pour l’utilisateur
Mécanisme de remontée : couche par couche
app/
├── layout.tsx # layout racine
├── error.tsx # erreurs sous la racine (A)
├── page.tsx # page d'accueil
├── dashboard/
│ ├── layout.tsx # layout dashboard
│ ├── error.tsx # erreurs dashboard (B)
│ └── page.tsx # page dashboard
└── profile/
└── page.tsx # page profile
Si dashboard/page.tsx plante → capturé par (B), le error.tsx parent le plus proche.
Si profile/page.tsx plante → pas d’error.tsx dans profile → remonte jusqu’à (A).
Piège : error.tsx ne capture pas les erreurs du layout.tsx du même niveau. La limite est à l’intérieur du layout ; si le layout échoue avant le chargement de la limite, il faut un error.tsx plus haut (ex. app/error.tsx pour dashboard/layout.tsx).
Bon usage de reset()
reset re-rend l’arbre sous la limite d’erreur. Adapté aux erreurs temporaires :
- timeout API (retry possible)
- réseau instable
- conditions limites liées à la saisie
Pour un bug (undefined.property), retry inutile : corriger le code et déployer.
Certaines équipes limitent à 3 tentatives puis orientent vers rafraîchissement ou support :
'use client'
import { useEffect, useState } from 'react'
export default function Error({ error, reset }: {
error: Error & { digest?: string }
reset: () => void
}) {
const [retryCount, setRetryCount] = useState(0)
const handleReset = () => {
setRetryCount(prev => prev + 1)
reset()
}
return (
<div>
<h2>Une erreur est survenue</h2>
{retryCount < 3 ? (
<button onClick={handleReset}>
Réessayer ({retryCount}/3)
</button>
) : (
<p>Plusieurs tentatives ont échoué. Actualisez la page ou <a href="/contact">contactez-nous</a></p>
)}
</div>
)
}
global-error.tsx — filet de sécurité global
error.tsx est puissant, mais il ne capture pas les erreurs du layout racine app/layout.tsx. C’est le rôle de global-error.tsx.
Quand l’utiliser ?
Peu déclenché en production. Principalement :
- échec d’initialisation du layout racine (ex. lib de state global)
- erreurs non capturées par aucun error.tsx
Dernier filet de sécurité : on espère ne jamais en avoir besoin, mais il doit exister.
Spécificités de global-error.tsx
Contrairement à error.tsx ordinaire, il doit inclure toute la structure HTML : balises <html> et <body>.
Pourquoi ? Il remplace entièrement le layout racine. Si le layout racine plante, plus de squelette de page — global-error.tsx reconstruit une page minimale viable.
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<html>
<body>
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
padding: '20px',
fontFamily: 'system-ui, sans-serif'
}}>
<h1>L'application a rencontré un problème grave</h1>
<p style={{ color: '#666', marginBottom: '20px' }}>
{process.env.NODE_ENV === 'development'
? error.message
: 'Nous traitons ce problème, veuillez réessayer plus tard'}
</p>
<button
onClick={() => reset()}
style={{
padding: '10px 20px',
background: '#0070f3',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
Recharger l'application
</button>
</div>
</body>
</html>
)
}
Styles inline plutôt que Tailwind ou CSS modules : le design system peut ne pas être chargé.
Développement vs production
global-error.tsx ne s’active qu’en production. En dev, Next.js affiche la page d’erreur rouge avec stack pour le debug.
En prod, masquez les détails techniques. Le test process.env.NODE_ENV ci-dessus sert à ça. L’utilisateur veut savoir si l’app est utilisable, pas lire TypeError: Cannot read property 'map' of undefined.
Faut-il l’ajouter ?
Oui. Probabilité faible, impact énorme. Mieux vaut une page d’erreur digne que « impossible d’accéder à ce site » du navigateur.
Comme une assurance : on n’y pense pas, jusqu’au jour où elle compte.
Server Components : points d’attention
Les Server Components (Next.js 13+) changent la donne : erreurs serveur et client ne se traitent pas pareil.
Où vont les erreurs des Server Components ?
Au début, je me demandais : erreur au rendu serveur, est-ce que error.tsx côté client capte ?
Oui. Next.js transmet l’erreur au client et déclenche le error.tsx le plus proche. En production, les détails sont masqués (sécurité).
Échec de connexion BDD : stack complète en dev, message générique en prod.
Erreurs attendues vs inattendues
Distinction importante dans la doc officielle :
Erreurs attendues (logique métier, traitement explicite) :
- validation de formulaire
- API 404
- permissions insuffisantes
Erreurs inattendues (bugs, infra → Error Boundary) :
- panne base de données
- service tiers indisponible
- accès à une propriété de
undefined
Pour les attendues, try-catch dans Server Action ou fetch, puis retour d’erreur au composant :
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const email = formData.get('email') as string
// Erreur attendue : format email invalide
if (!email.includes('@')) {
return { error: 'Veuillez saisir une adresse e-mail valide' }
}
try {
await db.user.create({ email })
return { success: true }
} catch (error) {
// Erreur inattendue : BDD — laisser Error Boundary gérer
throw new Error('Échec de la création utilisateur')
}
}
Pour les inattendues : throw et laisser remonter vers error.tsx.
Erreurs lors de la récupération de données
// app/posts/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts')
// Erreur attendue : statut HTTP d'erreur
if (!res.ok) {
if (res.status === 404) {
return { posts: [], error: 'Aucune donnée pour le moment' }
}
// Erreur serveur — Error Boundary
throw new Error('Échec de la récupération des données')
}
return { posts: await res.json() }
}
export default async function PostsPage() {
const { posts, error } = await getPosts()
if (error) {
return <div>Aucun article pour le moment</div>
}
return (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
)
}
« Aucune donnée » n’a pas besoin d’une page d’erreur complète ; seules les vraies pannes système déclenchent error.tsx.
Utilité de error.digest
Next.js 15 ajoute digest, identifiant unique auto-généré.
Scénario : l’utilisateur voit l’erreur, envoie une capture au support avec le digest ; le support retrouve la requête exacte dans les logs.
'use client'
export default function Error({ error }: { error: Error & { digest?: string }}) {
return (
<div>
<h2>Une erreur est survenue</h2>
<p>Référence : {error.digest}</p>
<p>Contactez le support avec ce numéro</p>
</div>
)
}
Avec Sentry ou équivalent, le digest accélère fortement le diagnostic.
Bonnes pratiques en production
1. Limites d’erreur granulaires
Un seul error.tsx à la racine ne suffit pas. Isolez les zones critiques.
Exemple e-commerce :
app/
├── error.tsx # filet global
├── (shop)/
│ ├── products/
│ │ └── error.tsx # liste produits isolée
│ ├── cart/
│ │ └── error.tsx # panier isolé
│ └── checkout/
│ └── error.tsx # checkout isolé
Panier en panne → navigation produits possible.
2. Monitoring et reporting
useEffect dans error.tsx est l’endroit idéal :
'use client'
import { useEffect } from 'react'
import * as Sentry from '@sentry/nextjs'
export default function Error({ error, reset }: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
Sentry.captureException(error, {
tags: {
errorDigest: error.digest,
errorBoundary: 'app-root'
},
extra: {
userAgent: navigator.userAgent,
timestamp: new Date().toISOString()
}
})
}, [error])
return (
// UI d'erreur...
)
}
Incluez digest et contexte navigateur. Certaines équipes journalisent aussi les 5 dernières pages visitées.
3. UI d’erreur conviviale
Les utilisateurs veulent savoir :
- que s’est-il passé ? (langage simple)
- que faire ? (actions claires)
- mes données sont-elles perdues ? (périmètre d’impact)
return (
<div className="error-container">
<h2>Échec du chargement de la page</h2>
<p>Le réseau est peut-être instable, ou nos serveurs font une pause</p>
<div className="actions">
<button onClick={reset}>Réessayer</button>
<a href="/">Retour à l'accueil</a>
<a href="/help">Contacter le support</a>
</div>
<details className="error-details">
<summary>Informations techniques (optionnel)</summary>
<code>{error.digest}</code>
</details>
</div>
)
« Nos serveurs font une pause » bat « 500 Internal Server Error » pour l’anxiété utilisateur.
4. Stratégie de retry intelligente
- délai : attendre 1–2 s avant reset
- backoff exponentiel : 1 s, 2 s, 4 s…
- type d’erreur : retry pour réseau, support direct pour bug de code
const [retryCount, setRetryCount] = useState(0)
const [isRetrying, setIsRetrying] = useState(false)
const handleReset = async () => {
setIsRetrying(true)
setRetryCount(prev => prev + 1)
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, retryCount) * 1000)
)
setIsRetrying(false)
reset()
}
5. Différencier les environnements
const isDev = process.env.NODE_ENV === 'development'
return (
<div>
<h2>{isDev ? error.message : 'Un problème est survenu'}</h2>
{isDev && (
<pre>
<code>{error.stack}</code>
</pre>
)}
{!isDev && (
<p>Nous avons enregistré ce problème et le corrigerons rapidement</p>
)}
</div>
)
Dev : stack complète. Prod : message sobre, pas de fuite technique.
6. Ne pas en abuser
Error Boundary = filet, pas stratégie principale.
Erreurs attendues → try-catch. Dégradation locale possible → pas de page d’erreur globale.
Avatar qui ne charge pas → avatar par défaut, pas toute la page profil en erreur.
Réservez Error Boundary aux surprises qu’on ne peut pas isoler localement.
Conclusion
Trois idées à retenir :
Première : Error Boundary n’est pas optionnel. La fuite due à l’écran blanc est sous-estimée ; quelques fichiers error.tsx évitent bien des alertes nocturnes.
Deuxième : traitement en couches. error.tsx pour le local, global-error.tsx en dernier recours, distinction attendu/inattendu dans les Server Components.
Troisième : l’UX d’abord. Détails techniques → monitoring. Côté utilisateur : message clair et actions. Le bouton retry récupère ~40 % des erreurs temporaires — excellent ROI.
Ajoutez error.tsx à la racine, puis aux zones critiques. Couplez avec Sentry : la stabilité perçue progresse vite.
Et n’oubliez pas global-error.tsx — rarement utilisé, indispensable comme une ceinture de sécurité.
Implémenter Error Boundary dans Next.js
Ajouter des limites d'erreur à une application Next.js pour gérer élégamment les erreurs runtime
- 1
Step 1: Créer le fichier error.tsx
Créez error.tsx dans app ou dans n'importe quel segment de route, avec la directive 'use client' - 2
Step 2: Implémenter le composant de gestion d'erreur
Définissez le composant Error avec les paramètres error et reset, et concevez une UI d'erreur conviviale - 3
Step 3: Ajouter le reporting d'erreurs
Dans useEffect, envoyez l'erreur à Sentry ou une plateforme similaire et enregistrez error.digest - 4
Step 4: Implémenter une nouvelle tentative intelligente
Ajoutez un bouton de retry, limitez le nombre de tentatives, mécanisme de récupération pour erreurs temporaires - 5
Step 5: Créer global-error.tsx
Créez global-error.tsx dans app comme dernier filet, avec une structure HTML complète - 6
Step 6: Distinguer les types d'erreur
Dans les Server Components, séparez erreurs attendues (traitement explicite) et inattendues (Error Boundary)
FAQ
Quelle différence entre error.tsx et global-error.tsx ?
Pourquoi error.tsx doit-il être un Client Component ?
Les erreurs des Server Components sont-elles capturées par error.tsx ?
Quand utiliser try-catch plutôt qu'Error Boundary ?
Comment fonctionne la fonction reset() ?
10 min de lecture · Publié le: 6 janv. 2026 · Mis à jour le: 27 juil. 2026
Guide complet Next.js
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Pages d'erreur Next.js 404 et 500 : guide complet de l'implémentation au design
Personnalisez vos pages d'erreur Next.js avec not-found.tsx, error.tsx et global-error.tsx : exemples de code complets, bonnes pratiques de design et solutions aux problèmes courants pour améliorer l'UX et réduire le taux de rebond.
Partie 33 sur 51
Suivant
Tests unitaires Next.js : guide complet Jest + React Testing Library
Configurez de zéro l'environnement de test Next.js 15 avec Jest et React Testing Library : config, tests Client/Server Components, hooks, mocks et dépannage, avec exemples de code complets.
Partie 35 sur 51



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire