Props
Passing data to components with props
What Are Props?
Props (short for "properties") are the way you pass data from a parent component to a child component. They're like function arguments—you can pass any JavaScript value: strings, numbers, objects, arrays, functions, or even other components.
Key Concept
Props are read-only. A component should never modify its own props. Think of props as a component's configuration—they flow down from parent to child (one-way data flow).
Passing Props
Pass props to a component just like HTML attributes:
// Parent component passing props
function App() {
return (
<div>
{/* Passing different types of props */}
<UserCard
name="Alice" {/* string */}
age={25} {/* number */}
isAdmin={true} {/* boolean */}
hobbies={['coding', 'reading']} {/* array */}
address={{ city: 'NYC', zip: '10001' }} {/* object */}
onUpdate={() => console.log('Updated!')} {/* function */}
/>
</div>
);
}
Receiving Props
Props are received as an object in the component's function parameter:
// Method 1: Access via props object
function UserCard(props) {
return (
<div>
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
<p>Admin: {props.isAdmin ? 'Yes' : 'No'}</p>
</div>
);
}
// Method 2: Destructuring (recommended)
function UserCard({ name, age, isAdmin }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Admin: {isAdmin ? 'Yes' : 'No'}</p>
</div>
);
}
// Method 3: Destructuring with rename
function UserCard({ name: userName, age: userAge }) {
return <h2>{userName} is {userAge} years old</h2>;
}
Default Props
Set default values for props that might not be provided:
// Using default parameters (modern approach)
function Button({
text = 'Click me',
variant = 'primary',
size = 'medium',
disabled = false
}) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
>
{text}
</button>
);
}
// Usage - all props are optional now
<Button /> // Uses all defaults
<Button text="Submit" /> // Custom text, default variant
<Button variant="danger" size="large" /> // Custom variant and size
The Children Prop
The special children prop contains whatever you put between a component's opening and closing tags:
// Card component that wraps content
function Card({ title, children }) {
return (
<div className="card">
<h3 className="card-title">{title}</h3>
<div className="card-body">
{children}
</div>
</div>
);
}
// Usage - content between tags becomes children
function App() {
return (
<Card title="Welcome">
<p>This paragraph is passed as children.</p>
<button>And so is this button!</button>
</Card>
);
}
// Another example: Layout component
function Layout({ children }) {
return (
<div className="layout">
<Header />
<main>{children}</main>
<Footer />
</div>
);
}
Passing Functions as Props
Functions are commonly passed as props for handling events:
function Parent() {
const handleClick = (message) => {
alert(message);
};
const handleDelete = (id) => {
console.log('Deleting item:', id);
};
return (
<div>
<Button onClick={() => handleClick('Hello!')} />
<TodoItem
id={1}
text="Learn React"
onDelete={handleDelete}
/>
</div>
);
}
function Button({ onClick }) {
return <button onClick={onClick}>Click Me</button>;
}
function TodoItem({ id, text, onDelete }) {
return (
<div>
<span>{text}</span>
<button onClick={() => onDelete(id)}>Delete</button>
</div>
);
}
Spreading Props
Use the spread operator to pass all properties of an object as props:
function App() {
const userProps = {
name: 'Alice',
age: 25,
email: 'alice@example.com',
isAdmin: true
};
// Instead of passing each prop individually...
// <UserCard name={userProps.name} age={userProps.age} ... />
// Spread them all at once
return <UserCard {...userProps} />;
}
// Forwarding props to child elements
function CustomInput({ label, ...inputProps }) {
return (
<div>
<label>{label}</label>
<input {...inputProps} />
</div>
);
}
// Usage - all extra props go to the input
<CustomInput
label="Email"
type="email"
placeholder="Enter email"
required
/>
Props vs State
| Props | State |
|---|---|
| Passed from parent | Managed within component |
| Read-only (immutable) | Can be changed (mutable) |
| Like function arguments | Like local variables |
| Flow downward only | Local to component |
PropTypes (Runtime Validation)
import PropTypes from 'prop-types';
function UserCard({ name, age, email, isAdmin }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}
UserCard.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
email: PropTypes.string.isRequired,
isAdmin: PropTypes.bool,
hobbies: PropTypes.arrayOf(PropTypes.string),
address: PropTypes.shape({
city: PropTypes.string,
zip: PropTypes.string
})
};
UserCard.defaultProps = {
age: 0,
isAdmin: false
};
💡 Tip: Use TypeScript Instead
For better type safety, use TypeScript instead of PropTypes. It catches errors at compile time rather than runtime.
TypeScript Props
// Define props type with interface
interface UserCardProps {
name: string;
age?: number; // optional
email: string;
isAdmin?: boolean;
onUpdate: (id: number) => void;
}
// Use the interface in the component
function UserCard({
name,
age = 0,
email,
isAdmin = false,
onUpdate
}: UserCardProps) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<button onClick={() => onUpdate(1)}>Update</button>
</div>
);
}
// With children
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div>
<h3>{title}</h3>
{children}
</div>
);
}
🎯 Props Best Practices
- ✓ Keep props simple and focused
- ✓ Use destructuring for cleaner code
- ✓ Provide default values for optional props
- ✓ Use TypeScript for type safety
- ✓ Name callback props with "on" prefix (onClick, onSubmit)
- ✓ Don't mutate props—treat them as read-only
- ✓ Avoid passing too many props (consider restructuring)