Gestion des erreurs 404 et ErrorBoundary : apprends à sécuriser tes routes dynamiques React Router 7 et à renvoyer des réponses HTTP propres.
avec React Router 7
Posez vos questions 24/7 à notre IA experte en React Router 7
Validez vos acquis avec des quiz personnalisés et un feedback instantané
Dans React Router 7, chaque route peut renvoyer une erreur contrôlée depuis le loader ou l’action sans planter toute l'application. Au lieu d’afficher une page blanche, on renvoie un objet Response
avec un code HTTP et un message, et on intercepte ce cas dans un ErrorBoundary dédié. Ce pattern améliore l’UX et facilite le débogage.
On part d’une route qui affiche le détail d’un utilisateur à partir de son slug
:
1import {2isRouteErrorResponse,3useLoaderData,4useRouteError,5type LoaderFunctionArgs,6} from 'react-router'7import { getUserBySlug } from '~/users.server'89export async function loader({ params }: LoaderFunctionArgs) {10const user = await getUserBySlug({ slug: params.userSlug })11if (!user) {12// ^? On lève une Response pour renvoyer une erreur HTTP contrôlée13// @callout: throw new Response déclenche isRouteErrorResponse()14throw new Response(`User "${params.userSlug}" was not found`, {15status: 404,16statusText: 'Not Found',17})18}19return { user }20}2122export default function UserDetail() {23const { user } = useLoaderData<typeof loader>()24return (25<div className="p-4">26<h1 className="text-2xl font-bold">{user.name}</h1>27<p>ID: {user.id}</p>28</div>29)30}
LoaderFunctionArgs
et useLoaderData
depuis <Link to="https://reactrouter.com/api/hooks">react-router</Link>
.Response
avec un status
et statusText
.Response
et le transmet à l’ErrorBoundary.On ajoute un export ErrorBoundary
dans le même fichier pour capturer uniquement les erreurs de cette route :
1export function ErrorBoundary() {2const error = useRouteError()34if (isRouteErrorResponse(error)) {5return (6<div className="h-screen flex flex-col items-center justify-center p-4">7<h1 className="text-3xl font-bold text-red-600">8{error.status} {error.statusText}9</h1>10<p className="mt-2 text-red-500">{error.data}</p>11</div>12)13}1415if (error instanceof Error) {16return (17<div className="h-screen flex flex-col items-center justify-center p-4">18<h1 className="text-3xl font-bold text-red-600">Erreur</h1>19<p className="mt-2 text-red-500">{error.message}</p>20</div>21)22}2324return <h1>Une erreur inconnue est survenue.</h1>25}
useRouteError()
rend l’erreur levée par le loader/action.isRouteErrorResponse(error)
repère les Response
contrôlées.development
, tu peux aussi afficher la stack trace.Cela évite de dupliquer les messages d’erreur génériques et de bloquer l’ensemble de l’application pour un simple détail manquant.
Dans users.server.ts
, la fonction getUserBySlug
renvoie undefined
si pas trouvé :
1const db = {2users: [3{ id: 1, name: 'Virgile', slug: 'virgile' },4{ id: 2, name: 'Robert', slug: 'robert' },5// …6]7}89export async function getUserBySlug({ slug }: { slug: string }) {10// Simule un délai réseau11await new Promise(r => setTimeout(r, 100))12return db.users.find(u => u.slug === slug)13}
Le loader l’appelle, puis génère un 404
pour un slug manquant. Pour d’autres erreurs (500, 403), le pattern est identique : on lève une Response
avec le code adapté.
root.tsx
Pour toutes les erreurs non capturées localement, on a un ErrorBoundary global dans app/root.tsx
:
1import { isRouteErrorResponse, Link, Outlet } from 'react-router'23export function ErrorBoundary({ error }: { error: unknown }) {4let title = 'Oops!'5let message = 'Une erreur inattendue est survenue.'67if (isRouteErrorResponse(error)) {8title = error.status === 404 ? '404 – Introuvable' : `Erreur ${error.status}`9message = error.data || error.statusText || message10} else if (error instanceof Error) {11message = error.message12}1314return (15<div className="container mx-auto p-4">16<h1 className="text-4xl font-bold text-red-600 mb-2">{title}</h1>17<p className="text-lg text-gray-700">{message}</p>18<Link to="/" className="text-blue-500 hover:underline mt-4 block">19Retour à l’accueil20</Link>21</div>22)23}
En production, masque la stack trace pour ne pas exposer d’informations sensibles. Garde les messages concis et utiles.
throw new Response(...)
dans loader/actionroot.tsx
pour attraper le reste.loader
:
"admin"
, throw une Response
403.getUserBySlug
, lancez throw new Error("DB inaccessible")
./settings
:
Response
410 (Gone).Quelle est la principale différence entre les composants client et serveur dans React ?
Quelle technique est recommandée pour éviter les rendus inutiles dans React ?
Quel hook permet de gérer les effets de bord dans un composant React ?
Comment implémenter la gestion des erreurs pour les requêtes API dans React ?
Quelle est la meilleure pratique pour déployer une application React en production ?