Architecture de Composants React Évolutive

Architecture de Composants React Évolutive

Composants composés, design tokens et bibliothèques tree-shakeable que votre équipe adoptera vraiment.

18 avril 202612 min de lecturePar Shehzad Asadullah

À mesure que les applications React passent de prototypes à des systèmes de production, l'architecture des composants fait la différence entre une codebase qui évolue gracieusement et une qui s'effondre sous sa propre complexité. Les composants composés, les design tokens et les bibliothèques tree-shakeable forment une boîte à outils moderne pour construire des systèmes UI flexibles, performants et maintenables. Ce guide explore chaque modèle avec des conseils pratiques pour les équipes livrant des applications React en production en 2026.

Diagramme d'architecture de composants montrant les composants composés, les design tokens et la structure modulaire de bibliothèque
Une architecture évolutive sépare les préoccupations de présentation via composition, tokens et imports modulaires.

L'argument pour les composants composés

Les composants composés sont un modèle où un composant parent gère l'état partagé et le contexte tandis que les composants enfants gèrent des responsabilités de rendu spécifiques. Les utilisateurs composent l'UI en imbriquant des sous-composants nommés plutôt qu'en configurant tout via une interface de props plate. Ce modèle apparaît dans les design systems matures — onglets, accordéons, menus et modales bénéficient tous de la composition composée.

L'avantage principal est l'ergonomie de l'API. Comparez un composant monolithique piloté par props avec une alternative composée :

// Prop-driven — becomes unwieldy as features grow
<Tabs
  items={[
    { label: "Overview", content: <Overview /> },
    { label: "Settings", content: <Settings />, disabled: true },
  ]}
  defaultIndex={0}
  onChange={handleTabChange}
  variant="underline"
/>

// Compound — flexible, readable, extensible
<Tabs defaultValue="overview" onValueChange={handleTabChange}>
  <Tabs.List>
    <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
    <Tabs.Trigger value="settings" disabled>Settings</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="overview">
    <Overview />
  </Tabs.Content>
  <Tabs.Content value="settings">
    <Settings />
  </Tabs.Content>
</Tabs>

Les composants composés communiquent via React Context. Le parent crée un fournisseur de contexte avec l'état partagé — onglet actif, état ouvert, valeur sélectionnée — et les composants enfants consomment ce contexte pour rendre leur portion de l'UI. Ce découplage signifie que vous pouvez ajouter de nouveaux sous-composants sans modifier l'API du parent.

Implémenter les composants composés

Commencez par définir un contexte avec une interface typée. Le composant racine enveloppe les enfants dans le provider et accepte des props de configuration. Les sous-composants lisent depuis le contexte et rendent conditionnellement selon l'état partagé.

  • Exportez les sous-composants comme propriétés statiques sur la racine — Tabs.List, Tabs.Trigger.
  • Validez la composition en développement avec des messages d'erreur utiles lorsque les sous-composants sont utilisés hors de leur parent.
  • Gardez les valeurs de contexte stables avec useMemo pour éviter les re-rendus inutiles de tous les enfants.
  • Supportez les modes contrôlé et non contrôlé pour une flexibilité maximale.

Les design tokens comme source unique de vérité

Les design tokens sont des entités nommées qui stockent les décisions de design visuel — couleurs, espacement, typographie, ombres, rayons de bordure — comme valeurs agnostiques de plateforme. Au lieu de coder en dur #3b82f6 ou 16px dans vos composants, vous référencez des tokens sémantiques comme color.action.primary ou spacing.component.md.

Cette indirection apporte des bénéfices tangibles à grande échelle :

  • Cohérence — Chaque composant référence les mêmes valeurs de token, éliminant la dérive visuelle.
  • Theming — Échangez les valeurs de token pour basculer entre thèmes clair, sombre et à fort contraste.
  • Alignement cross-platform — Exportez les tokens vers CSS, JavaScript, iOS et Android depuis une source unique.
  • Collaboration design-dev — Designers et développeurs partagent un vocabulaire commun.
// tokens.ts — semantic design tokens
export const tokens = {
  color: {
    surface: {
      primary: "var(--color-surface-primary)",
      secondary: "var(--color-surface-secondary)",
    },
    text: {
      primary: "var(--color-text-primary)",
      muted: "var(--color-text-muted)",
    },
    action: {
      primary: "var(--color-action-primary)",
      primaryHover: "var(--color-action-primary-hover)",
    },
  },
  spacing: {
    xs: "0.25rem",
    sm: "0.5rem",
    md: "1rem",
    lg: "1.5rem",
    xl: "2rem",
  },
  radius: {
    sm: "0.25rem",
    md: "0.5rem",
    lg: "0.75rem",
    full: "9999px",
  },
} as const;

Définissez les propriétés personnalisées CSS dans votre feuille de style globale et référencez-les via l'objet token dans les composants. Lorsque l'équipe design met à jour la palette de marque, vous changez les valeurs à un seul endroit et chaque composant se met à jour automatiquement.

Tree-shaking et bibliothèques modulaires

La taille du bundle impacte directement les performances de l'application, surtout sur les réseaux mobiles. Le tree-shaking élimine le code mort pendant le build, mais seulement si l'architecture de votre bibliothèque le supporte. Un export monolithique qui réexporte chaque composant force les bundlers à inclure toute la bibliothèque même lorsque vous n'utilisez qu'un seul bouton.

Structurez votre bibliothèque de composants pour un tree-shaking optimal :

  • Exportez chaque composant depuis son propre point d'entrée de module.
  • Marquez votre package comme sans effet de bord dans package.json.
  • Évitez les barrel files qui réexportent tout depuis un seul index.
  • Utilisez le format ESM pour les bundlers modernes ; fournissez CJS uniquement si le support legacy est requis.
  • Gardez les peer dependencies externes — React, React DOM et les bibliothèques de style ne doivent pas être bundlées.
// package.json — enable tree-shaking
{
  "name": "@acme/ui",
  "sideEffects": false,
  "exports": {
    "./button": "./dist/button/index.js",
    "./input": "./dist/input/index.js",
    "./modal": "./dist/modal/index.js",
    "./tokens": "./dist/tokens/index.js"
  }
}

// Consumer imports only what they need
import { Button } from "@acme/ui/button";
import { tokens } from "@acme/ui/tokens";

Mesurez l'impact avec des outils d'analyse de bundle. Importez un seul composant et vérifiez que le bundle de production exclut les modules non liés. Si toute votre bibliothèque apparaît dans la sortie, investiguez les exports barrel et les déclarations side-effect.

Structure de dossiers et colocalisation

La structure organisationnelle influence la rapidité avec laquelle les développeurs trouvent et modifient les composants. Le principe de colocalisation — garder les fichiers liés ensemble — réduit le changement de contexte et rend les composants des unités autonomes pouvant être déplacées, testées et supprimées indépendamment.

Une structure de dossiers évolutive pour une bibliothèque de composants :

  • src/components/Button/Button.tsx — Implémentation du composant.
  • src/components/Button/Button.test.tsx — Tests unitaires colocalisés avec le composant.
  • src/components/Button/Button.stories.tsx — Documentation Storybook.
  • src/components/Button/index.ts — Surface d'export publique.
  • src/tokens/ — Définitions partagées de design tokens.
  • src/hooks/ — Hooks partagés utilisés par plusieurs composants.

Évitez d'organiser par type (components/, hooks/, utils/ au niveau supérieur de chaque fonctionnalité). Groupez plutôt par domaine fonctionnel pour l'architecture applicative, et par composant pour une bibliothèque partagée.

Composition plutôt que configuration

Le fil conducteur reliant composants composés, design tokens et modules tree-shakeable est une philosophie : favoriser la composition plutôt que la configuration. Les composants doivent être petits, ciblés et combinables plutôt que des widgets monolithiques avec des dizaines de props contrôlant chaque variante visuelle.

Appliquez le principe ouvert-fermé — les composants sont ouverts à l'extension via composition mais fermés à la modification. Au lieu d'ajouter une prop showIcon à un bouton, acceptez une icône en tant qu'enfant ou slot. Au lieu d'une prop size avec cinq valeurs enum, exposez des propriétés personnalisées CSS que les consommateurs remplacent.

Cette approche produit des bibliothèques de composants qui vieillissent bien. Les nouvelles exigences sont satisfaites en composant des primitives existantes plutôt qu'en modifiant des composants éprouvés et en risquant des régressions dans tout le système.

Documentation et adoption

Les modèles d'architecture ne délivrent de la valeur que lorsque l'équipe les comprend et les adopte de manière cohérente. Investissez dans Storybook ou une plateforme de documentation similaire qui présente la composition des composants composés, l'utilisation des tokens et les modèles d'import. Rédigez des guides de contribution expliquant quand créer un nouveau composant versus étendre un existant.

Tenez des architecture decision records pour les choix structurels significatifs — pourquoi vous avez choisi les composants composés plutôt que les render props, pourquoi les tokens vivent dans des propriétés personnalisées CSS plutôt qu'un objet thème JavaScript. Les futurs membres de l'équipe hériteront de ces décisions et auront besoin de contexte pour maintenir la cohérence.

L'architecture de composants React évolutive ne consiste pas à appliquer tous les modèles simultanément. Il s'agit de choisir les bonnes abstractions pour la taille de votre équipe, la complexité de votre application et vos contraintes de performance. Les composants composés gèrent la composition UI flexible, les design tokens imposent la cohérence visuelle, et les modules tree-shakeable maintiennent les bundles légers — ensemble, ils forment une fondation qui supporte la croissance de dix à dix mille composants.

Cette lecture vous a plu ?

Un projet ou une idée en tête ? J'aimerais beaucoup en discuter.

Me contacter