🎓 What You Will Learn
- Props Basics: Pass data from parent to child components
- Destructuring: Clean syntax for accessing props
- Default Values: Set fallback prop values
- Children Prop: Build flexible wrapper components
- Prop Types: TypeScript for type-safe components
- Prop Drilling: Recognize and avoid common pitfalls
- Reusable Components: Build flexible, composable UIs
- Best Practices: Pro patterns and conventions
What Are Props?
Props (short for "properties") are how components communicate in React. They let you pass data from a parent component to a child component, similar to function parameters.
Props enable component reusability. Instead of creating a different component for every variation, you create ONE component and customize it with different props.
Basic Props — Passing Data
Props are passed as attributes on components, just like HTML attributes.
// Child component receives props
function Greeting({ name, emoji }) {
return <p>{emoji} Hello, {name}!</p>;
}
// Parent component passes props as attributes
export function App() {
return (
<>
<Greeting name="Alice" emoji="👋" />
<Greeting name="Bob" emoji="😊" />
<Greeting name="Charlie" emoji="🎉" />
</>
);
}👩💻
Alice
Frontend Developer
🧑💼
Bob
Project Manager
🎨
Diana
UI Designer
Destructuring — Clean Syntax
Destructuring extracts props into separate variables, making code cleaner and more readable.
// Without destructuring (verbose)
function User(props) {
return <p>{props.name} - {props.email}</p>;
}
// With destructuring (clean!)
function User({ name, email }) {
return <p>{name} - {email}</p>;
}
// Also works with default values
function Button({ text = "Click", color = "blue" }) {
return <button className={color}>{text}</button>;
}Default Props — Fallback Values
Use = syntax to provide default values when props aren't passed.
function Button({ text = "Click me!", variant = "primary" }) {
return (
<button className={variant}>{text}</button>
);
}
// Using defaults (all three work!)
<Button />
<Button text="Submit" />
<Button text="Delete" variant="danger" />Default Props
Custom Props
The Children Prop — Building Wrappers
The children prop is special — it contains whatever is passed between component tags. This enables flexible, reusable wrapper components.
// Component definition
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
{children} {/* This is what goes inside! */}
</div>
);
}
// Usage: Content goes between tags
<Card title="Welcome">
<p>This is a paragraph</p>
<button>Click me</button>
</Card>
<Card title="Features">
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
</ul>
</Card>Welcome
👋 This content is passed as children to the Card component.
Features
- Reusable wrapper
- Flexible content
- Clean code
Code Snippet
const x = 42;Prop Drilling & Solutions
Prop drilling happens when you pass props through many levels of components even though intermediate components don't use them. This makes code hard to maintain.
✅ Solutions:
- •
useContext— Share global data without passing through every component - •
useReducer— Centralize complex state - • State Management Libraries — Redux, Zustand, Jotai
❌ Prop Drilling Problem
Props passed through many levels:
App → Page → Layout → Card → Text
✅ Solution: useContext
Access data directly where needed:
App → [useContext] → Text
Type-Safe Props with TypeScript
Use TypeScript to define prop types and catch errors at compile time instead of runtime.
// Define prop interface
interface ButtonProps {
text: string;
color?: "blue" | "red" | "green"; // Union type
onClick: (e: React.MouseEvent) => void;
disabled?: boolean;
}
// Component uses the interface
function Button({ text, color = "blue", onClick, disabled }: ButtonProps) {
return (
<button
onClick={onClick}
className={color}
disabled={disabled}
>
{text}
</button>
);
}
// TypeScript validates these
<Button
text="Submit"
color="green"
onClick={() => console.log("clicked")}
/>
// This would error:
// <Button text="Submit" color="purple" /> // ❌ not in unionLaptop
$999
⭐ 4.8/5
✓ In Stock
Phone
$599
⭐ 4.5/5
✓ In Stock
Tablet
$399
⭐ 4.2/5
Out of Stock
Monitor
$299
⭐ 5/5
✓ In Stock
Best Practices
userName not nprops.xProps vs State
| Aspect | Props | State |
|---|---|---|
| Source | Parent component | Component itself |
| Mutability | Read-only | Can change |
| Purpose | Configure component | Track data changes |
| Rerender | Yes (when props change) | Yes (when state changes) |
| Example | <Button color='blue' /> | const [count, setCount] = useState(0) |
Common Mistakes
// ❌ BAD: Props are read-only!
function User({ name }) {
name = "Bob"; // Don't do this!
return <p>{name}</p>;
}
// ✅ GOOD: Use state if you need to change
function User({ initialName }) {
const [name, setName] = useState(initialName);
return (
<>
<p>{name}</p>
<input onChange={(e) => setName(e.target.value)} />
</>
);
}// ❌ BAD: Too many props is hard to use
function User({ name, email, phone, address, city, zip, country }) {
// Confusing!
}
// ✅ GOOD: Group related props into object
function User({ name, contact, address }) {
// Cleaner!
}
<User
name="Alice"
contact={{ email: "...", phone: "..." }}
address={{ city: "...", zip: "..." }}
/>✅ Good: Use useContext or state management for deeply nested data
Real-World Example: Product Card
interface ProductCardProps {
id: number;
name: string;
price: number;
image: string;
inStock: boolean;
onAddToCart: (id: number) => void;
}
function ProductCard({
id,
name,
price,
image,
inStock,
onAddToCart,
}: ProductCardProps) {
return (
<div className="card">
<img src={image} alt={name} />
<h3>{name}</h3>
<p className="price">${price}</p>
<p className="stock">
{inStock ? "✓ In Stock" : "Out of Stock"}
</p>
<button
onClick={() => onAddToCart(id)}
disabled={!inStock}
>
Add to Cart
</button>
</div>
);
}
// Usage
<ProductCard
id={1}
name="Laptop"
price={999}
image="/laptop.jpg"
inStock={true}
onAddToCart={handleAddToCart}
/>Advanced Patterns
// Spread all props to child component
function Button(props) {
return <button {...props}>Click</button>;
}
// This passes through all attributes
<Button className="large" disabled={false} data-test="btn" />// Function as prop
function ListRenderer({ data, render }) {
return (
<ul>
{data.map((item) => (
<li key={item.id}>{render(item)}</li>
))}
</ul>
);
}
// Usage
<ListRenderer
data={users}
render={(user) => <span>{user.name}</span>}
/>What's Next?
- React Hooks: useState for component state
- useContext: Avoid prop drilling with global state
- Component Composition: Build complex UIs from simple components
- TypeScript: Full type safety for props and state
- Testing: Test components with different props
Master props through practice! 💪 Build reusable components by experimenting with different prop combinations. Props are the foundation of component-driven development!