Frontend React
Règles pour chaque frontend React. Ce fichier contient ce qui s'applique toujours ; lisez la référence correspondante avant de travailler sur ce sujet.
| Tâche | À lire |
|---|---|
| Dossiers, couches, slices, API publique, imports, fichiers barillet | references/structure.md |
| Plongées FSD : arbre de décision, imports croisés, entités, assets, placement auth/API, câblage framework, migration | references/fsd/ (index à la fin de structure.md) |
| Nommage, taille de fichier, props, système de design, layouts, constantes | references/code-style.md |
| Composants, hooks, effets, mémoïsation, transitions, longues listes, hydratation | references/hooks-and-rendering.md |
| Cascades, taille du bundle, code splitting, préchargement, coût de re-rendu | references/performance.md |
| Données serveur, requêtes, mutations, loaders, paramètres de recherche, Zustand | references/data-and-state.md |
| Formulaires, validation, erreurs de formulaire | references/forms.md |
| Sémantique, clavier, focus, contraste, régions live, tests d'accessibilité | references/accessibility.md (implémentation React) + la compétence frontend-accessibility (règles complètes, audits) |
| Tests unitaires, de composants et E2E, couverture, simulation d'API | references/testing.md |
| Stories, vérifications d'accessibilité Storybook, régression visuelle | references/storybook.md |
| XSS, rendu HTML/Markdown/sortie LLM, auth, CSRF, CSP, variables d'environnement, déconnexion | references/security.md |
| Version de Node, gestionnaire de paquets, pre-commit + gitleaks, audit des dépendances | references/tooling.md |
Traductions, changement de langue, <html lang> | references/i18n.md |
| Configuration d'un nouveau projet | references/setup.md |
| Image conteneur pour le frontend | references/docker.md + la compétence docker-kubernetes |
Stack
- React 19 (≥ 19.2 pour
useEffectEventet<Activity>) · TypeScript strict - Rendu : SPA Vite ou mode framework React Router (SSR + hydratation). Les règles marquées React Router framework only ne s'appliquent que là. Pas de Next.js, pas de React Server Components, pas de SWR.
- Routage : TanStack Router ou React Router — fixé par projet, détectez-le à partir du code, ne mélangez jamais.
- État serveur : TanStack Query · État client : Zustand
- Formulaires : React Hook Form + Zod ou TanStack Form + Zod — fixé par projet
- Validation : Zod à chaque frontière (réponses API, formulaires, paramètres de recherche, env, stockage,
postMessage) - Outillage : Biome (lint + format), Steiger (règles FSD), Vitest + Testing Library, Playwright, Storybook
- Pas de React Compiler → la mémoïsation est manuelle, mais uniquement après mesure.
Structure — Feature-Sliced Design
Couches app → pages → widgets → features → entities → shared ; imports uniquement vers le bas ; pas d'imports entre slices de la même couche ; chaque slice est importée uniquement via son index.ts avec des exports nommés explicites (pas de export *). shared/ui et shared/lib ont un index par composant/bibliothèque. Commencez par app, pages, shared ; pas de dossiers de couches vides. Le code reste dans la page jusqu'à ce que la règle d'extraction s'applique — utilisé à plusieurs endroits actuellement, une raison de changer indépendamment de tout consommateur unique, une responsabilité ciblée ; les trois conditions, la duplication est acceptable jusque-là. widgets est déconseillé : uniquement pour un bloc composé réutilisé sur plusieurs pages qui ne peut pas vivre dans une page (les widgets existants restent valides). Détails : references/structure.md, livre de règles amont references/fsd/rules.md.
- Un composant par fichier, un hook par fichier, ~150 lignes par composant ; composants préfixés par le nom de leur slice (
references/code-style.md). - Système de design d'abord : utilisez les composants de
shared/ui/; n'importez jamaisshared/ui/primitives/(sortie CLI de la bibliothèque UI, jamais éditée) en dehors deshared/ui/. Pas de nouveau CSS avant d'avoir vérifié ce que les composants, props et tokens du système de design peuvent faire.
Composants et hooks — toujours
- Ne définissez jamais un composant à l'intérieur d'un autre composant — il se remonte à chaque rendu.
- Dérivez, ne synchronisez pas : les valeurs calculables à partir des props/état sont calculées pendant le rendu. Pas de
useEffectqui copie des props ou de l'état dans l'état. Réinitialisez aveckey. - Les actions utilisateur s'exécutent dans les gestionnaires d'événements, pas dans des effets déclenchés par des flags d'état.
- Les effets synchronisent uniquement avec des systèmes externes, avec des dépendances primitives étroites, un seul objectif par effet, et un nettoyage. Ne désactivez jamais la règle exhaustive-deps.
setState(prev => …)lorsque la valeur suivante dépend de la précédente ;useState(() => expensive())pour les valeurs initiales coûteuses.- Ne mutez jamais les props ou l'état :
toSorted(),toReversed(),toSpliced(),with(), spread. {count > 0 ? <X /> : null}— jamais&&avec un nombre à gauche.- Pas de
fetchdansuseEffect; les données serveur proviennent de TanStack Query ou d'un route loader. memo/useMemo/useCallbackuniquement après profilage ou lorsque la stabilité référentielle est requise.- Les erreurs ne sont jamais avalées : pas de
catchvide, pas de.catch(() => {}). Chaque route a une error boundary ; chaque requête a une UI de chargement, d'erreur et vide.
Données et état — toujours
- Fonction de requête → hook → composant. HTTP uniquement via le client dans
shared/api/; n'importez jamais un client HTTP dans les hooks ou composants. - Les données serveur vivent uniquement dans TanStack Query, via une fabrique de clés de requête avec
queryOptions; les réponses sont parsées avec Zod dans la fonction de requête. - L'accès aux ressources simple, le CRUD et les fabriques de clés de requête vivent dans
shared/api(ou la page/feature qui les utilise seule) ;entities/<x>/apiuniquement pour les requêtes avec des règles de domaine ou un mapping de cette entité. - Les mutations invalident (ou mettent à jour) chaque requête affectée.
- L'état d'URL (filtres, tri, pagination, onglets) vit dans des paramètres de recherche typés et validés par Zod — jamais parsés à la main depuis
window.location. - Zustand uniquement pour l'état client partagé ; toujours avec un sélecteur ; les stores persistés utilisent
version,migrateetpartialize, et ne contiennent aucune donnée sensible. - Chargez les données de route dans le route loader (
queryClient.query({ ...options, staleTime: 'static' }), anciennementensureQueryData) et en parallèle, pas dans des composants imbriqués les uns après les autres.
Accessibilité — intégrée, jamais ajoutée après
Objectif : WCAG 2.2 AA. Partie de la définition de fini pour chaque composant et page. La compétence frontend-accessibility contient les règles complètes et la procédure d'audit — chargez-la pour chaque modification de l'interface utilisateur.
- HTML sémantique d'abord :
<button>pour les actions,<a>/<Link>pour la navigation,<label>pour chaque champ, repères, un<h1>et titres ordonnés. ARIA uniquement lorsqu'aucun élément natif ne convient. - Clavier : tout est accessible et opérable au clavier ; pas de
tabIndexpositif ; pas de pièges clavier. - Focus visible sur chaque élément interactif (
:focus-visible, ≥ 3:1 de contraste). - Gestion du focus : lors d'un changement de route, définissez le titre de la page et déplacez le focus vers le
<h1>ou<main>; les boîtes de dialogue piègent le focus, se ferment surEscapeet ramènent le focus sur le déclencheur. - Noms : les boutons icônes ont un
aria-label; les images ont unalt(alt=""si décoratif). - Contraste 4.5:1 pour le texte, 3:1 pour les composants UI ; la couleur n'est jamais le seul signal.
- Mouvement réduit respecté ; régions live (
role="status"/role="alert") pour les résultats asynchrones et les erreurs. - Les tests interrogent par rôle et nom accessible ; les stories passent le module complémentaire d'accessibilité ; les pages critiques passent axe dans Playwright.
Tests — écrits avec la fonctionnalité, pas après
- Vitest + Testing Library tests unitaires et de composants pour chaque fonctionnalité, dans le même changement. Testez le comportement via l'interface utilisateur (rôles, labels,
user-event), pas les détails d'implémentation. - Couverture ≥ 80 % lignes et ≥ 80 % branches, appliquée par les seuils Vitest dans le CI.
- Playwright E2E pour chaque flux utilisateur critique (connexion, flux métier principaux, paiement/checkout, permissions) : locateurs basés sur les rôles, pas d'attentes fixes, état d'authentification réutilisé, scan axe sur les pages clés.
- Histoire Storybook pour chaque composant d'interface utilisateur partagé/réutilisable, couvrant tous les états (par défaut, chargement, vide, erreur, désactivé, contenu long). Viewport desktop par défaut ; viewports mobiles uniquement lorsque le projet nécessite des layouts responsifs.
- Régression visuelle via Storybook + captures d'écran Playwright ; tests de snapshot uniquement avec parcimonie et ciblés.
Sécurité — toujours
- Pas de
dangerouslySetInnerHTMLsans DOMPurify ; en dehors de React, utiliseztextContent, jamaisinnerHTML. La sortie du modèle et le Markdown ne sont jamais rendus sous forme de HTML brut. - Pas de tokens ou données sensibles dans
localStorage/sessionStorage/IndexedDB. L'authentification est un cookie de sessionHttpOnlydéfini par le backend ; pour OIDC, préférez un backend-for-frontend. - Les requêtes modifiant l'état envoient l'en-tête CSRF ;
credentials: 'include'uniquement vers votre propre API. - Les variables
VITE_*sont publiques — pas de secrets dedans, jamais. - Les cibles
?redirect=sont uniquement des chemins relatifs de même origine (redirection ouverte). - La déconnexion efface la session serveur, le cache TanStack Query et tous les stores persistés.
- CSP en tant qu'en-tête HTTP avec
object-src 'none',base-uri 'none',frame-ancestors. - Les gestionnaires
postMessagevérifientevent.origin; ne fusionnez jamais d'objets non fiables (pollution de prototype). - Le frontend n'applique jamais l'autorisation — c'est le backend qui le fait [ASVS 8.3.1].
TypeScript et Biome
strictactivé (par défaut depuis TypeScript 7 — gardez-le explicite) ; aussinoUncheckedIndexedAccess,verbatimModuleSyntax,moduleResolution: "bundler",libavecES2023+DOM. TypeScript 7 définittypespar défaut à[]— listez"vite/client"explicitement ;baseUrla disparu, utilisezpaths("@/*": ["./src/*"]).- Pas de
any(utilisezunknownet réduisez), pas d'assertions non nulles pour faire taire les erreurs, pas de@ts-ignore(@ts-expect-erroravec une raison si inévitable). Pas de nombres ou chaînes magiques — constantes nommées dansshared/config/ou leconfig/modelde la slice. - Les types des données externes proviennent des schémas Zod (
z.infer/z.output), pas d'interfaces écrites à la main qui peuvent dériver. - Biome : règles recommandées plus
noNestedComponentDefinitions,noReExportAlletnoConsole(pas dans l'ensemble recommandé) ;noBarrelFilereste désactivé pour les fichiersindex.tsde slice (references/structure.md). biome ci,tsc --noEmit,steiger ./srcetknippassent localement et dans le CI. Ne désactivez pas les règles en ligne sans un commentaire expliquant pourquoi.- Commentaires en anglais ; les TODOs utilisent le marqueur TODO du projet.
Hygiène du dépôt — toujours
- Version de Node épinglée :
.nvmrc(LTS),enginesdanspackage.jsonavecengine-strict,packageManageravec la version exacte de npm/pnpm ; le fichier de verrouillage est commité. - pre-commit installé pour le commit et le push : gitleaks sur les modifications indexées et sur tous les commits non poussés, Biome ;
tsc, les tests Vitest associés et l'audit des dépendances sur le push. Ne contournez jamais avec--no-verify. - Audit des dépendances dans le CI sur chaque MR et selon un calendrier.
Détails : references/tooling.md.
Docker et déploiement
Les images conteneur, .dockerignore, Compose et Kubernetes sont couverts par la compétence docker-kubernetes ; le workflow frontend et la checklist sont dans references/docker.md. Chaque image a un .dockerignore. Faits spécifiques au frontend à respecter ici :
- Les valeurs
VITE_*sont intégrées dans le bundle au moment de la construction. Une image pour plusieurs environnements a besoin d'une configuration runtime : servez unconfig.json(ouconfig.js) qui est écrit au démarrage du conteneur à partir des variables d'environnement et récupéré/validé au démarrage de l'application — toujours public, toujours sans secrets. - Une SPA Vite est constituée de fichiers statiques : servez-les à partir d'un serveur web avec un fallback vers
index.htmlpour les routes côté client, un cache long pour les assets hachés,no-cachepourindex.html, et les en-têtes de sécurité dereferences/security.md. - Le mode framework React Router (SSR) nécessite un serveur Node au moment de l'exécution (
react-router-serveou un serveur personnalisé) — ce n'est pas un déploiement statique.
Après l'écriture
Pour une revue de sécurité du résultat, suggérez la compétence appsec-review.
Sources
Combiné des meilleures pratiques React de Vercel Engineering (github.com/vercel-labs/agent-skills, react-best-practices, licence MIT) — adapté de Next.js à Vite/React Router avec TanStack Query ; les identifiants de règles sont conservés pour la traçabilité — et de l'OWASP Secure Agent Playbook (CC BY 4.0 ; OWASP ASVS 5.0). La structure suit le Feature-Sliced Design (feature-sliced.design, fsd.how) ; references/fsd/ est adapté de la compétence officielle FSD pour agent (github.com/feature-sliced/skills, licence MIT). L'accessibilité cible WCAG 2.2.