Maîtrise la prop children, apprends les patterns de composition plutôt que l'héritage, et construis des composants flexibles et réutilisables en utilisant les patterns de conteneur et de spécialisation.
En HTML, les éléments peuvent contenir d'autres éléments : <div><p>Bonjour</p></div>. React fonctionne de la même manière. Quand tu places du JSX entre les balises ouvrantes et fermantes d'un composant, React passe ce JSX comme une prop spéciale appelée children.
function Wrapper({ children }) {
return (
<div style={{ border: '2px solid blue', padding: '16px' }}>
{children}
</div>
);
}
// Utilisation :
<Wrapper>
<h1>Bonjour !</h1>
<p>Ce contenu est passé comme children.</p>
</Wrapper>La prop children peut être n'importe quoi : du texte, des éléments JSX, des composants, ou même un tableau d'éléments. React affiche tout ce que tu passes comme children.
// Du texte comme children
<Wrapper>Juste une chaîne de caractères</Wrapper>
// Plusieurs éléments comme children
<Wrapper>
<Header />
<Sidebar />
<MainContent />
</Wrapper>
// Des composants imbriqués comme children
<Wrapper>
<Wrapper>
<p>Imbriqué !</p>
</Wrapper>
</Wrapper>La prop children est la base de la composition de composants dans React. Elle te permet de créer des composants conteneurs génériques qui n'ont pas besoin de connaître leur contenu à l'avance. Le composant définit la structure et le style, et l'appelant décide de ce qui va à l'intérieur.
Tu peux accéder à children par déstructuration ({ children }) ou par props.children :
// Les deux sont équivalents :
function Box({ children }) {
return <div className="box">{children}</div>;
}
function Box(props) {
return <div className="box">{props.children}</div>;
}En programmation orientée objet, l'héritage est une façon courante de partager des comportements entre classes. Mais dans React, la composition est toujours préférée à l'héritage.
L'équipe React chez Meta (avec des milliers de composants en production) a déclaré qu'elle n'a trouvé aucun cas d'usage où elle recommanderait d'utiliser l'héritage plutôt que la composition.
Pourquoi la composition l'emporte :
Flexibilité. Un composant qui utilise children peut envelopper n'importe quel contenu sans modification. Un composant hérité est enfermé dans la structure de son parent.
Explicité. Les props rendent le flux de données visible. L'héritage cache le comportement dans la hiérarchie de classes.
Simplicité. La composition consiste simplement à passer des props et afficher des children. L'héritage nécessite de comprendre toute la chaîne de classes.
Approche par héritage (non recommandée) :
// Imagine si les composants utilisaient l'héritage :
class SuccessDialog extends Dialog {
render() {
return super.render({ color: 'green', title: 'Succès !' });
}
}
// Difficile à comprendre, difficile à personnaliserApproche par composition (recommandée) :
function Dialog({ title, color, children }) {
return (
<div style={{ border: `2px solid ${color}`, padding: '16px' }}>
<h2>{title}</h2>
{children}
</div>
);
}
function SuccessDialog({ children }) {
return (
<Dialog title="Succès !" color="green">
{children}
</Dialog>
);
}
// Facile à comprendre, facile à personnaliser
<SuccessDialog>
<p>Tes modifications ont été enregistrées.</p>
</SuccessDialog>La composition te permet de construire des interfaces complexes à partir de pièces simples et réutilisables — comme des blocs LEGO. Chaque composant gère sa propre responsabilité, et ils s'assemblent grâce aux props et children.
Un pattern de composition courant consiste à créer des versions spécialisées d'un composant générique. Le composant spécialisé enveloppe le composant générique et fournit des props par défaut spécifiques.
// Composant générique
function Button({ variant, size, children, onClick }) {
const styles = {
padding: size === 'large' ? '12px 24px' : '8px 16px',
backgroundColor: variant === 'danger' ? 'red' : 'blue',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
};
return (
<button style={styles} onClick={onClick}>
{children}
</button>
);
}
// Composants spécialisés
function DangerButton({ children, onClick }) {
return (
<Button variant="danger" size="large" onClick={onClick}>
{children}
</Button>
);
}
function PrimaryButton({ children, onClick }) {
return (
<Button variant="primary" size="large" onClick={onClick}>
{children}
</Button>
);
}Ce pattern est utile pour :
// Plus d'exemples de spécialisation :
function ErrorAlert({ children }) {
return <Alert type="error" icon="!">{children}</Alert>;
}
function PageLayout({ children }) {
return <Layout sidebar={<Nav />} header={<Header />}>{children}</Layout>;
}
function AdminPage({ children }) {
return <PageLayout><RequireAuth role="admin">{children}</RequireAuth></PageLayout>;
}Chaque niveau ajoute un comportement spécifique tout en déléguant au composant générique sous-jacent.
Le pattern de conteneur se produit quand un composant ne connaît pas ses children à l'avance. Il agit comme un conteneur et affiche tous les children qui lui sont passés.
function Card({ title, children }) {
return (
<div className="card">
<h3 className="card-title">{title}</h3>
<div className="card-body">{children}</div>
</div>
);
}
<Card title="Profil utilisateur">
<img src="avatar.png" alt="Avatar" />
<p>Jean Dupont</p>
<p>jean@exemple.com</p>
</Card>Parfois tu as besoin de plusieurs "slots" — différentes zones d'un composant qui acceptent différents contenus. Bien que React n'ait pas de slots nommés comme certains autres frameworks, tu peux obtenir le même résultat en utilisant des props normales :
function Layout({ header, sidebar, children }) {
return (
<div className="layout">
<header className="layout-header">{header}</header>
<aside className="layout-sidebar">{sidebar}</aside>
<main className="layout-content">{children}</main>
</div>
);
}
// Utilisation :
<Layout
header={<NavBar />}
sidebar={<SideMenu items={menuItems} />}
>
<ArticleList articles={articles} />
</Layout>N'importe quelle prop peut accepter des éléments JSX — pas seulement children. Cela te donne la flexibilité de créer des composants avec plusieurs points d'injection :
function Modal({ title, footer, children }) {
return (
<div className="modal-overlay">
<div className="modal">
<div className="modal-header">{title}</div>
<div className="modal-body">{children}</div>
<div className="modal-footer">{footer}</div>
</div>
</div>
);
}
<Modal
title={<h2>Confirmer la suppression</h2>}
footer={<><button>Annuler</button><button>Supprimer</button></>}
>
<p>Es-tu sûr de vouloir supprimer cet élément ?</p>
</Modal>Une utilisation particulièrement puissante de la composition concerne les composants de mise en page. Ces composants définissent la structure visuelle d'une page ou d'une section et te laissent remplir le contenu.
function SplitPane({ left, right }) {
return (
<div style={{ display: 'flex', gap: '16px' }}>
<div style={{ flex: 1 }}>{left}</div>
<div style={{ flex: 1 }}>{right}</div>
</div>
);
}
<SplitPane
left={<ContactList contacts={contacts} />}
right={<ChatWindow messages={messages} />}
/>Les composants de mise en page sont réutilisables dans toute ton application. Voici quelques patterns courants :
// Un conteneur centré
function CenterContent({ maxWidth, children }) {
return (
<div style={{
maxWidth: maxWidth || '800px',
margin: '0 auto',
padding: '0 16px',
}}>
{children}
</div>
);
}
// Une mise en page empilée (espacement vertical)
function Stack({ gap, children }) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: gap || '8px' }}>
{children}
</div>
);
}
// Utilisation :
<CenterContent maxWidth="600px">
<Stack gap="16px">
<h1>Bienvenue</h1>
<p>Ceci est centré et empilé.</p>
<Button>Commencer</Button>
</Stack>
</CenterContent>Bonnes pratiques pour la composition :
<Card type="user" showAvatar showBio /> avec de nombreux drapeaux, compose des pièces plus petites : <Card><Avatar /><Bio /></Card>.<div id="root"></div>
<script src="https://unpkg.com/react@19/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@19/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function Card({ title, children }) {
return (
<div style={{
border: '1px solid #ccc',
borderRadius: '8px',
padding: '16px',
marginBottom: '12px',
}}>
<h3 style={{ marginTop: 0 }}>{title}</h3>
<div>{children}</div>
</div>
);
}
function Badge({ color, children }) {
return (
<span style={{
backgroundColor: color,
color: 'white',
padding: '2px 8px',
borderRadius: '12px',
fontSize: '12px',
}}>
{children}
</span>
);
}
function App() {
return (
<div style={{ maxWidth: '400px', margin: '20px auto' }}>
<Card title="Fondamentaux de React">
<p>Apprends les concepts fondamentaux de React.</p>
<Badge color="green">Débutant</Badge>
</Card>
<Card title="Patterns avancés">
<p>Composition, render props, et plus encore.</p>
<Badge color="orange">Intermédiaire</Badge>
</Card>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>Qu'est-ce que la prop 'children' dans React ?