Components are the building blocks of React. Props are how data flows between them. Understanding this relationship is the foundation of React development.
Component Composition
JSX
// Layout component
function PageLayout({ children, sidebar }) {
return (
<div className="page">
<header className="header">
<h1>My App</h1>
<nav>Home | About | Contact</nav>
</header>
<div className="content">
{sidebar && <aside className="sidebar">{sidebar}</aside>}
<main>{children}</main>
</div>
</div>
);
}
// Using composition
function Dashboard() {
return (
<PageLayout sidebar={<UserInfo />}>
<h2>Dashboard</h2>
<Stats />
<RecentActivity />
</PageLayout>
);
}Props Deep Dive
JSX
// Destructuring props
function ProductCard({ name, price, image, inStock, onAddToCart }) {
return (
<div className="product-card">
<img src={image} alt={name} />
<h3>{name}</h3>
<p>${price.toFixed(2)}</p>
{inStock ? (
<button onClick={onAddToCart}>Add to Cart</button>
) : (
<span className="out-of-stock">Out of Stock</span>
)}
</div>
);
}
// Using it
function ProductList({ products }) {
const handleAddToCart = (product) => {
console.log(`Adding ${product.name} to cart`);
};
return (
<div className="product-grid">
{products.map(product => (
<ProductCard
key={product.id}
{...product} // Spread all properties as props
onAddToCart={() => handleAddToCart(product)}
/>
))}
</div>
);
}Children Prop
JSX
// Any content between tags becomes the children prop
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
}
// Usage
<Card title="User Profile">
<p>Name: Alice</p>
<p>Email: alice@example.com</p>
<button>Edit Profile</button>
</Card>Component Patterns
Container/Presentational
JSX
// Presentational (dumb) — just renders UI
function TodoItem({ todo, onToggle }) {
return (
<li
className={todo.completed ? 'completed' : ''}
onClick={() => onToggle(todo.id)}
>
{todo.text}
</li>
);
}
// Container (smart) — manages state and logic
function TodoListContainer() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Build an app', completed: false },
]);
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} onToggle={toggleTodo} />
))}
</ul>
);
}Controlled vs Uncontrolled Components
JSX
// Controlled — React controls the value
function ControlledInput() {
const [value, setValue] = useState('');
return (
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
// Uncontrolled — DOM manages the value
function UncontrolledInput() {
const inputRef = useRef();
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return <input type="text" ref={inputRef} />;
}PropTypes and Default Props
JSX
import PropTypes from 'prop-types';
function UserAvatar({ src, name, size = 40 }) {
return (
<img
src={src}
alt={name}
style={{ width: size, height: size, borderRadius: '50%' }}
/>
);
}
UserAvatar.propTypes = {
src: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
size: PropTypes.number,
};
UserAvatar.defaultProps = {
size: 40,
};💡 Tip: Keep components small and focused. If a component has more than 100 lines, consider splitting it. Props flow down, events flow up.
Next: State and Events