Retour aux articlesTutorial
Le Proxy Component en React
Un pattern simple pour isoler vos composants du changement : centraliser une implémentation au lieu de la dupliquer partout dans votre code.
#react#patterns#components
Mike Codeur
01 août 20266 minTutorial
Derrière ce nom un peu barbare se cache une idée toute simple, et probablement le pattern React qui vous fera gagner le plus de temps sur la durée : ne jamais laisser une implémentation se répandre dans votre code.
Un Proxy Component est un composant qui n'existe que pour en envelopper un autre. Il ne fait presque rien. Et c'est précisément pour ça qu'il est utile.
Tout va bien. Jusqu'au jour où il faut changer l'implémentation du bouton — passer à la librairie de composants de l'entreprise, ajouter une classe, brancher un tracking analytics :
Vous voilà parti pour un rechercher-remplacer dans tout le projet. Sur cinq occurrences, ça passe. Sur cent-cinquante, réparties dans quarante fichiers, c'est une journée perdue — et une occasion d'en oublier trois.
Puis utilisez-le partout :
À première vue, vous avez juste ajouté une indirection. En réalité vous avez créé un seul point de changement. Le jour où l'implémentation évolue, un seul fichier bouge :
Zéro modification ailleurs. C'est tout l'intérêt du pattern.
Même chose pour envelopper un composant existant plutôt qu'un élément HTML :
La première forme est presque toujours la bonne : elle donne des défauts sans enfermer. Gardez la seconde pour ce qui ne doit jamais varier, comme un
Six lignes, et une faille de sécurité (
Le problème
Prenons une application avec des boutons « Like » un peu partout :function Header() {
return (
<div>
<h1>Bienvenue</h1>
<button>Like</button>
</div>
)
}
function Content() {
return (
<div>
<h2>Articles</h2>
<span>Article 1</span>
<button>Like</button>
<span>Article 2</span>
<button>Like</button>
</div>
)
}
function Footer() {
return (
<div>
<h3>Nous contacter</h3>
<button>Like</button>
</div>
)
}// avant
<button>Like</button>
// après
<Button variant="ghost" size="sm" onClick={track}>Like</Button>Le vrai coût n'est pas le nombre de lignes à changer, c'est le risque.
Chaque occurrence oubliée devient une incohérence visuelle que personne ne
verra avant la production.
La solution
Créez un composant qui ne fait que déléguer :function LikeButton(props) {
return <button {...props}>Like</button>
}function Header() {
return (
<div>
<h1>Bienvenue</h1>
<LikeButton />
</div>
)
}function LikeButton(props) {
return (
<Button variant="ghost" size="sm" {...props}>
Like
</Button>
)
}Le point clé : le spread des props
Le{...props} n'est pas un détail cosmétique, c'est ce qui rend le composant réellement réutilisable. Sans lui, votre proxy devient un mur : impossible de passer un onClick, un disabled ou un aria-label sans retoucher le composant à chaque fois.
function LikeButton(props) {
return <button {...props}>Like</button>
}
// tout fonctionne, sans rien changer au proxy
<LikeButton onClick={handleLike} disabled={isPending} aria-label="Aimer" />Un proxy qui ne transmet pas ses props est pire que pas de proxy du tout : les
attributs passent à la trappe sans erreur. Vous cherchez le bug dans le
parent alors qu'il est dans le wrapper.
En TypeScript
Typez les props avec celles de l'élément que vous enveloppez, et vous héritez automatiquement de tout ce que l'élément accepte :import type {ComponentProps} from 'react'
type LikeButtonProps = ComponentProps<'button'>
export function LikeButton(props: LikeButtonProps) {
return <button {...props}>Like</button>
}import type {ComponentProps} from 'react'
import {Button} from '@/components/ui/button'
type LikeButtonProps = ComponentProps<typeof Button>
export function LikeButton(props: LikeButtonProps) {
return (
<Button variant="ghost" size="sm" {...props}>
Like
</Button>
)
}Avec React 19, la
ref est une prop comme les autres : elle est incluse
dans ComponentProps et transmise par le spread. Plus besoin de forwardRef
pour écrire un proxy transparent.Ordre des props : qui gagne ?
Détail qui compte : la position du spread décide qui a le dernier mot.// valeurs par défaut — l'appelant peut les remplacer
<Button variant="ghost" {...props} />
// valeurs imposées — l'appelant ne peut plus les changer
<Button {...props} variant="ghost" />type="button" qui éviterait une soumission de formulaire accidentelle.
Les vrais cas d'usage
1
Envelopper une librairie tierce
Tous vos boutons passent par votre composant maison plutôt que directement par
celui de la librairie. Le jour où vous changez de librairie UI, vous réécrivez
une poignée de fichiers, pas votre application.
2
Fixer une convention produit
Vos liens externes doivent toujours porter
target="_blank" et rel="noopener noreferrer" ? Un proxy le garantit une fois pour toutes, au lieu de compter
sur la vigilance de chacun en revue de code.3
Brancher un comportement transverse
Analytics, log, état de chargement : le proxy est l'endroit naturel pour
ajouter un comportement à tous les usages d'un composant d'un coup.
4
Absorber une migration
Pendant un changement d'API, le proxy traduit l'ancienne interface vers la
nouvelle. Le reste du code continue de tourner pendant que vous migrez.
Un exemple concret : le lien externe
import type {ComponentProps} from 'react'
type ExternalLinkProps = ComponentProps<'a'>
export function ExternalLink(props: ExternalLinkProps) {
return <a target="_blank" rel="noopener noreferrer" {...props} />
}rel oublié sur un target="_blank") devient structurellement impossible dans votre application.
Quand ne PAS en faire
Le pattern est simple, donc tentant. Deux garde-fous :| Situation | Verdict |
|---|---|
| Un composant utilisé à trois endroits ou plus | Bon candidat |
| Une convention à garantir (sécurité, a11y) | Bon candidat, même pour deux usages |
| Un usage unique, sans perspective de réemploi | Inutile — vous ajoutez une indirection pour rien |
| Un proxy qui enveloppe un proxy qui enveloppe… | Signal d'alarme : aplatissez |
La bonne question n'est pas « est-ce que ça se répète ? » mais « qu'est-ce
qui va changer ? ». On isole ce qui bouge, pas ce qui se ressemble.
À retenir
Un Proxy Component, c'est trois lignes de code pour transformer un changement global en changement local. Le pattern ne demande aucune librairie, aucune abstraction sophistiquée : juste la discipline de ne pas appeler directement une implémentation depuis quarante endroits. C'est exactement l'esprit du principe DRY — non pas « ne jamais écrire deux fois la même chose », mais « n'avoir qu'un seul endroit à modifier quand une décision change ».Écrit par
MC
Mike Codeur
Auteur
Articles connexes
DevelopmentIntroduction à Next.js 16
Ce qui change concrètement quand on développe avec Next.js 16 et React 19 : params asynchrones, Server Components, Server Actions et Turbopack.
03 août 20265 min
Lire la suite
Design
Tailwind CSS v4 : la configuration passe dans le CSS
Fini le tailwind.config.js. La v4 déplace toute la configuration dans votre feuille de style, avec @theme, @plugin et @custom-variant.
02 août 20265 min
Lire la suite