Sauvegarder la session utilisateur dans un cookie avec React Router 7

Apprends à gérer la session utilisateur sécurisée via cookies en React Router 7. Protège tes routes et personnalise l’expérience utilisateur facilement.

5 min read
Déverrouillez votre potentiel

avec React Router 7

Vous en avez marre de...

❌ perdre du temps à chercher des informations éparpillées
❌ ne pas avoir de retour sur votre progression
Assistant IA spécialisé

Posez vos questions 24/7 à notre IA experte en React Router 7

Quiz interactifs

Validez vos acquis avec des quiz personnalisés et un feedback instantané

9 modules
45 leçons
Accès à vie
299.00
-50%

Dans un navigateur tu disposes de plusieurs espaces de stockage :
localStorage, sessionStorage, IndexedDB côté client
– les cookies qui voyagent dans chaque requête HTTP.

localStorage est pratique pour retenir un thème ou la vitesse d’un player YouTube, mais il est :

  1. lisible par n’importe quel script tournant sur la page ;
  2. jamais envoyé au serveur.

Pour authentifier un utilisateur nous avons besoin de l’inverse :
une valeur qu’on ne peut pas lire côté client (httpOnly) et qui accompagne chaque requête pour identifier la session.
C’est exactement ce que proposent les cookies.


La boîte à outils React Router 7 : createCookieSessionStorage

React Router 7 expose une API qui masque toute la plomberie de parsing/sérialisation :

app/server/sessions.server.ts
1
import { createCookieSessionStorage } from "react-router"
2
3
type SessionData = {
4
userId: string
5
theme: "light" | "dark"
6
lang: "en" | "fr"
7
}
8
9
export const { getSession, commitSession, destroySession }
10
= createCookieSessionStorage<SessionData>({
11
cookie: {
12
name: "__session",
13
// secrets en production !
14
// secrets: [process.env.SESSION_SECRET!],
15
sameSite: "lax",
16
httpOnly: true,
17
path: "/",
18
// secure: process.env.NODE_ENV === "production",
19
}
20
})

Trois helpers sortent :

HelperRôle
getSessionLire + décoder le cookie reçu
commitSessionEncoder + renvoyer Set-Cookie
destroySessionSupprimer le cookie (déconnexion)

1. Créer la session lors du login

app/routes/login.tsx
1
import { Form, redirect, type ActionFunctionArgs } from "react-router"
2
import { commitSession, getSession } from "~/server/sessions.server"
3
4
export async function action({ request }: ActionFunctionArgs) {
5
// 1. on récupère la session courante (peut être vide)
6
const session = await getSession(request.headers.get("Cookie"))
7
8
// 2. … ici tu valides email + mot de passe contre la DB …
9
// on triche : user #1 est toujours correct
10
session.set("userId", "1")
11
12
// 3. tu peux aussi stocker des préférences
13
session.set("theme", "dark")
14
session.set("lang", "fr")
15
16
// 4. on renvoie une redirection + Set-Cookie
17
return redirect("/dashboard", {
18
headers: {
19
"Set-Cookie": await commitSession(session),
20
},
21
})
22
}

Le cookie est crypté par la / les clé(s) secrets : impossible de deviner l’userId depuis le navigateur.


2. Lire la session dans un loader

app/routes/dashboard.tsx
1
import { data, type LoaderFunctionArgs, redirect }
2
from "react-router"
3
import { getSession } from "~/server/sessions.server"
4
5
export async function loader({ request }: LoaderFunctionArgs) {
6
const session = await getSession(request.headers.get("Cookie"))
7
const userId = session.get("userId")
8
9
if (!userId) {
10
// non authentifié → on renvoie vers /login
11
throw redirect("/login")
12
}
13
14
// tu peux récupérer la préférence utilisateur
15
const theme = session.get("theme") // "dark" | "light"
16
return data({ userId, theme })
17
}

Ici on protège la route : sans cookie → pas d’accès.


3. Modifier la session (ex. changer de thème)

app/routes/settings.theme.tsx
1
import { useLoaderData, Form, type ActionFunctionArgs } from "react-router"
2
import { commitSession, getSession } from "~/server/sessions.server"
3
4
export async function action({ request }: ActionFunctionArgs) {
5
const session = await getSession(request.headers.get("Cookie"))
6
const form = await request.formData()
7
const theme = form.get("theme") // "light" | "dark"
8
9
session.set("theme", theme)
10
return redirect("/settings", {
11
headers: { "Set-Cookie": await commitSession(session) }
12
})
13
}

React Router renvoie automatiquement le cookie mis à jour, le navigateur l’enregistrera et tu pourras le relire au prochain loader.


4. Déconnexion : destroySession

app/routes/logout.tsx
1
import { redirect, type ActionFunctionArgs } from "react-router"
2
import { destroySession, getSession } from "~/server/sessions.server"
3
4
export async function action({ request }: ActionFunctionArgs) {
5
const session = await getSession(request.headers.get("Cookie"))
6
return redirect("/login", {
7
headers: {
8
"Set-Cookie": await destroySession(session),
9
},
10
})
11
}

Le cookie est supprimé ; les loader protégés renverront immédiatement vers /login.


5. Raccourci utilitaire : requireUserId

Pour ne pas répéter le « si pas de cookie ⇒ redirect » dans chaque route, crée un helper :

utils/auth.server.ts
1
import { redirect } from "react-router"
2
import { getSession } from "~/server/sessions.server"
3
4
export async function requireUserId(request: Request) {
5
const session = await getSession(request.headers.get("Cookie"))
6
const id = session.get("userId")
7
if (!id) throw redirect("/login")
8
return id
9
}

Puis utilise-le dans chaque loader sensible.

app/routes/users._usersLayout.tsx
1
export async function loader({ request }) {
2
const userId = await requireUserId(request) // focus sur la logique métier
3
/* … */
4
}

Points clés à retenir

  • Un seul cookie crypté (__session) transporte toutes les infos liées à l’utilisateur.
  • createCookieSessionStorage te donne :
    getSession → lire ; commitSession → écrire ; destroySession → delete.
  • On écrit dans la session depuis une action (login, change settings)
    et on la lit dans les loaders pour protéger ou personnaliser.
  • Grâce aux secrets et à httpOnly, le cookie n’est jamais accessible par window.document.cookie → protection XSS.
  • Le helper requireUserId() centralise la logique de garde.

Exercices rapides

  1. Durée de vie limitée
    Ajoute maxAge: 60 dans la config du cookie puis observe qu’au bout de 60 s un refresh de /dashboard te renvoie sur /login.

  2. Préférence de langue
    – Stocke lang dans la session lors du login.
    – Ajoute un sélecteur dans /settings pour passer de fr à en
    (pense à commitSession).
    – Utilise lang dans le loader de / pour afficher « Bonjour » ou « Hello ».

  3. Page protégée globale
    Crée utils/withAuth.ts qui enveloppe n’importe quel loader :

    {3,7-9} utils/withAuth.ts
    1
    export const withAuth = (loader: LoaderFunction) =>
    2
    async (args: LoaderFunctionArgs) => {
    3
    await requireUserId(args.request)
    4
    return loader(args)
    5
    }

    Refactorise deux routes pour tester le HOC.

Happy hacking !

Premium
Quiz interactif
Testez vos connaissances et validez votre compréhension du module avec notre quiz interactif.
1

Comprendre les concepts fondamentaux

Quelle est la principale différence entre les composants client et serveur dans React ?

Les composants client s'exécutent uniquement dans le navigateur
Les composants serveur peuvent utiliser useState
Les composants client sont plus rapides
Il n'y a aucune différence significative
2

Optimisation des performances

Quelle technique est recommandée pour éviter les rendus inutiles dans React ?

Utiliser React.memo pour les composants fonctionnels
Ajouter plus d'états locaux
Éviter d'utiliser les props
Toujours utiliser les class components
3

Architecture des données

Quel hook permet de gérer les effets de bord dans un composant React ?

useEffect
useState
useMemo
useContext
4

Gestion des erreurs

Comment implémenter la gestion des erreurs pour les requêtes API dans React ?

Utiliser try/catch avec async/await
Ignorer les erreurs
Toujours afficher un message d'erreur
Rediriger l'utilisateur
5

Déploiement et CI/CD

Quelle est la meilleure pratique pour déployer une application React en production ?

Utiliser un service CI/CD comme GitHub Actions
Copier les fichiers manuellement via FTP
Envoyer le code source complet
Ne jamais mettre à jour l'application

Débloquez ce quiz et tous les autres contenus premium en achetant ce cours