React Essentials — Components, JSX, Props, State & MoreEntry 2 of 11
children vs Attribute Props and Component Composition
children is the special prop holding whatever JSX is placed between a component's opening and closing tags. Attribute props are values passed as named attributes.
Notes
Composition means building complex UIs by nesting simple components. A wrapper component doesn't need to know what it wraps; it renders children wherever the content should appear.
The same TabButton can receive its label either way:
- Using
children:<TabButton>Components</TabButton>. Best for components that take a single piece of renderable content. It's closer to normal HTML usage and is especially convenient when passing JSX as a value to another component. - Using attributes:
<TabButton label="Components" />. Best when a component needs multiple smaller pieces of information. The trade-off is extra work: each piece needs its own prop.
Ultimately, the choice comes down to the use case and personal preference.
What / Why / When
- What
Content passed between tags instead of as a named attribute.
- Why
It produces generic wrappers (cards, modals, layouts) that work with any content.
- When
The component defines the frame and the parent supplies the contents.
- Use case
A
CardorModalwrapper used across the whole app.
Code compare
import type { ReactNode } from "react";// Using childrenexport function TabButton({ children }: { children: ReactNode }) { return <button>{children}</button>;}// Using an attribute propexport function TabButtonWithLabel({ label }: { label: string }) { return <button>{label}</button>;}<TabButton>Components</TabButton><TabButtonWithLabel label="Components" />A generic wrapper:
import type { ReactNode } from "react";export function Card({ children }: { children: ReactNode }) { return <div className="card">{children}</div>;}<Card> <h2>Title</h2> <p>Any JSX can go here.</p></Card>;import { Component, input } from "@angular/core";// children → ng-content@Component({ selector: "app-tab-button", template: `<button><ng-content /></button>`,})export class TabButtonComponent {}// attribute prop → input()@Component({ selector: "app-tab-button-label", template: `<button>{{ label() }}</button>`,})export class TabButtonLabelComponent { label = input.required<string>();}<app-tab-button>Components</app-tab-button> <app-tab-button-label label="Components" />Differences and gotchas
childrenis a value. You can render it conditionally, render it twice, or inspect it.<ng-content>projects content exactly once.- Projected content in Angular is created by the parent even if the child wraps
<ng-content>in an@if. For truly lazy or repeated content, pass an<ng-template>instead.
Quick reference
| React | Angular |
|---|---|
children prop | <ng-content /> |
| Attribute props | input() |