Props - Passing Data to Components
Documentation for Props - Passing Data to Components.
Props - Passing Data to Components
What are Props?
Props are how you pass data from parent to child components.
Definition: Props (short for "properties") are arguments passed to React components. They allow parent components to pass data down to child components, making components dynamic and reusable. Props are read-only - a component must never modify its own props.
Why Use Props?
| Purpose | Explanation |
|---|---|
| Data Flow | Pass data from parent to child |
| Configuration | Customize component behavior |
| Reusability | Same component, different data |
| Component Communication | Parent controls child's appearance |
Props Flow (Unidirectional)
┌─────────────────────────────────────────────────────────┐
│ One-Way Data Flow │
├─────────────────────────────────────────────────────────┤
│ │
│ Parent Component │
│ │ │
│ │ props = { name: "Alice", age: 25 } │
│ ↓ │
│ Child Component │
│ │ │
│ │ (Cannot modify props) │
│ │ (Can only read and use) │
│ ↓ │
│ Grandchild Component │
│ │
└─────────────────────────────────────────────────────────┘Basic Props Usage
Passing Props
// Parent component
function App() {
return (
<div>
<UserCard
name="Alice"
age={25}
isActive={true}
hobbies={["reading", "coding"]}
address={{ city: "Mumbai", country: "India" }}
/>
</div>
);
}Receiving Props
// Method 1: Props object
function UserCard(props) {
return (
<div>
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
<p>Status: {props.isActive ? "Active" : "Inactive"}</p>
</div>
);
}
// Method 2: Destructuring (preferred)
function UserCard({ name, age, isActive }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Status: {isActive ? "Active" : "Inactive"}</p>
</div>
);
}Props Data Types
| Type | Syntax | Example |
|---|---|---|
| String | prop="value" or prop={'value'} | title="Hello" |
| Number | prop={123} | age={25} |
| Boolean | prop={true} or just prop | isActive = isActive={true} |
| Array | prop={[1, 2, 3]} | items={[1, 2, 3]} |
| Object | prop={{ key: value }} | user={{ name: 'Alice' }} |
| Function | prop={function} | onClick={handleClick} |
| JSX | prop={<Component />} | icon={<Icon />} |
| Null | prop={null} | data={null} |
// All data types example
function App() {
const handleClick = () => console.log("Clicked!");
return (
<UserProfile
name="Alice" // String
age={25} // Number
isAdmin={false} // Boolean
isActive // Boolean (true)
skills={["React", "Node"]} // Array
address={{ city: "Mumbai" }} // Object
onSave={handleClick} // Function
header={<h1>Profile</h1>} // JSX
data={null} // Null
/>
);
}Default Props
Set fallback values when props aren't provided.
// Method 1: Default parameters (recommended)
function Button({ text = "Click Me", variant = "primary", disabled = false }) {
return (
<button className={`btn btn-${variant}`} disabled={disabled}>
{text}
</button>
);
}
// Method 2: defaultProps (legacy)
function Button({ text, variant, disabled }) {
return (
<button className={`btn btn-${variant}`} disabled={disabled}>
{text}
</button>
);
}
Button.defaultProps = {
text: "Click Me",
variant: "primary",
disabled: false,
};PropTypes (Type Checking)
Runtime type checking for props.
import PropTypes from "prop-types";
function UserCard({ name, age, email, isActive, role, onClick }) {
return (
<div onClick={onClick}>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}
UserCard.propTypes = {
// Basic types
name: PropTypes.string.isRequired,
age: PropTypes.number,
email: PropTypes.string,
isActive: PropTypes.bool,
// Specific values
role: PropTypes.oneOf(["admin", "user", "guest"]),
// Multiple types
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
// Array of specific type
tags: PropTypes.arrayOf(PropTypes.string),
// Object shape
user: PropTypes.shape({
name: PropTypes.string.isRequired,
age: PropTypes.number,
}),
// Function
onClick: PropTypes.func,
// Any renderable (string, number, element, array)
children: PropTypes.node,
// React element
icon: PropTypes.element,
};
UserCard.defaultProps = {
age: 0,
isActive: false,
};PropTypes Reference
| PropType | Validates |
|---|---|
PropTypes.string | String |
PropTypes.number | Number |
PropTypes.bool | Boolean |
PropTypes.array | Array |
PropTypes.object | Object |
PropTypes.func | Function |
PropTypes.node | Anything renderable |
PropTypes.element | React element |
PropTypes.any | Any type |
.isRequired | Mark as required |
PropTypes.arrayOf() | Array of specific type |
PropTypes.shape({}) | Object with specific shape |
PropTypes.oneOf([]) | One of specific values |
PropTypes.oneOfType() | One of specific types |
children Prop
Special prop for content between component tags.
// Card component using children
function Card({ title, children }) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-body">{children}</div>
</div>
);
}
// Usage - anything between tags becomes children
function App() {
return (
<Card title="User Info">
<p>Name: Alice</p>
<p>Age: 25</p>
<button>Edit</button>
</Card>
);
}children Patterns
// Single child
<Container>
<p>Single element</p>
</Container>
// Multiple children
<Container>
<Header />
<Main />
<Footer />
</Container>
// Function as children (render props)
<DataFetcher url="/api/users">
{(data) => <UserList users={data} />}
</DataFetcher>
// Children manipulation
function Wrapper({ children }) {
return (
<div>
{React.Children.map(children, (child, index) => (
<div key={index} className="wrapped">
{child}
</div>
))}
</div>
);
}Spread Props
Pass all properties of an object as props.
const userProps = {
name: 'Alice',
age: 25,
email: 'alice@example.com'
};
// Without spread
<UserCard name={userProps.name} age={userProps.age} email={userProps.email} />
// With spread
<UserCard {...userProps} />
// Combining with other props
<UserCard {...userProps} isActive={true} />
// Rest props pattern
function Button({ variant, size, ...rest }) {
return (
<button className={`btn btn-${variant} btn-${size}`} {...rest}>
{rest.children}
</button>
);
}Passing Functions as Props
Enable child-to-parent communication.
// Parent component
function App() {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
const handleChange = (newValue) => {
setCount(newValue);
};
return (
<div>
<p>Count: {count}</p>
<Counter onIncrement={handleIncrement} onChange={handleChange} />
</div>
);
}
// Child component
function Counter({ onIncrement, onChange }) {
return (
<div>
<button onClick={onIncrement}>+1</button>
<button onClick={() => onChange(10)}>Set to 10</button>
</div>
);
}Props vs State
| Aspect | Props | State |
|---|---|---|
| Source | Parent component | Component itself |
| Mutability | Read-only | Can be changed |
| Update trigger | Parent re-renders | setState/setter |
| Purpose | Configure from outside | Internal data management |
| Analogy | Function parameters | Local variables |
Common Mistakes & Exceptions
1. Modifying Props Directly
// ❌ Never modify props
function UserCard({ user }) {
user.name = "New Name"; // Mutation!
return <h1>{user.name}</h1>;
}
// ✅ Create new object if needed
function UserCard({ user }) {
const displayName = user.name.toUpperCase();
return <h1>{displayName}</h1>;
}2. Forgetting to Pass Required Props
// Define requirements
UserCard.propTypes = {
name: PropTypes.string.isRequired, // Required!
age: PropTypes.number
};
// ❌ Missing required prop
<UserCard age={25} /> // Warning: name is required
// ✅ Provide required props
<UserCard name="Alice" age={25} />3. Incorrect Boolean Props
// ❌ String "false" is truthy!
<Button disabled="false" /> // Button IS disabled!
// ✅ Use actual boolean
<Button disabled={false} />
// ✅ Omit for false, include for true
<Button disabled /> // true
<Button /> // disabled is undefined (falsy)4. Inline Object/Array Props
// ❌ Creates new reference every render
<MyComponent style={{ color: 'red' }} />
<MyComponent items={[1, 2, 3]} />
// ✅ Define outside component or use useMemo
const style = { color: 'red' };
const items = [1, 2, 3];
<MyComponent style={style} items={items} />Interview Questions & Answers
Q1: What are props in React and why are they important?
Props (properties) are arguments passed to React components to make them dynamic and reusable. They enable one-way data flow from parent to child components, following React's unidirectional data architecture. Props are read-only - a component cannot modify its own props, maintaining predictable behavior. They serve as a component's "configuration" - the same component with different props produces different outputs (like a function with different arguments).
Q2: Why are props immutable (read-only)?
Props are immutable to maintain React's predictable, unidirectional data flow. If components could modify props, data could flow unpredictably causing bugs and making debugging difficult. Immutability enables optimizations - React can quickly determine if a component needs re-rendering by comparing props references. It also enforces clear component responsibilities: parents control data, children display it. If a child needs to "change" data, it calls a function prop to notify the parent.
Q3: What is the children prop and when do you use it?
The children prop is a special prop that contains content passed between a component's opening and closing tags. It enables component composition - creating wrapper components that can contain any content. Use cases: Layout components (Card, Modal, Container), providers (ThemeProvider, AuthProvider), and components that enhance/wrap others. Access via props.children or destructure { children }. It can be any renderable content: text, elements, arrays, or even functions (render props pattern).
Q4: What is the difference between props and state?
Props are passed from parent to child, are read-only, and change triggers come from the parent re-rendering. State is managed within the component, can be changed using setState/hooks, and changes trigger the component's own re-render. Think of props as function parameters (external input) and state as local variables (internal data). A component is "controlled" when its behavior is determined by props, and "uncontrolled" when it manages its own state.
Q5: What are PropTypes and why use them?
PropTypes provide runtime type-checking for props in development. They validate that components receive props of the correct type, catching bugs early. Benefits: documentation (shows expected props), debugging (console warnings for type mismatches), and team collaboration (clear contracts). However, they only work in development and add bundle size. For production apps and better developer experience, many teams use TypeScript instead, which provides compile-time type checking.
Q6: How do you set default values for props?
Two methods: Default parameters (recommended for functional components): function Button({ text = 'Click' }) {}. defaultProps (works for both but legacy): Button.defaultProps = { text: 'Click' }. Default parameters are preferred because they're standard JavaScript, work with TypeScript better, and are evaluated at destructuring time. defaultProps are evaluated after PropTypes checking. For required props, use PropTypes.isRequired instead of defaults.
Q7: What is prop drilling and how do you avoid it?
Prop drilling is passing props through multiple intermediate components that don't need them, just to reach a deeply nested child. Example: App → Layout → Sidebar → UserName all passing user prop. Problems: cluttered code, harder maintenance, unnecessary re-renders. Solutions: Context API for global data (themes, user auth), State management (Redux, Zustand) for complex state, Component composition - pass components rather than data, Custom hooks for shared logic.
Q8: How do you pass a function as a prop and why?
Pass functions to enable child-to-parent communication. Define in parent: const handleClick = () => {}, pass as prop: <Child onClick={handleClick} />, call in child: <button onClick={onClick}>. Use cases: form submissions, state updates, event handling. Naming convention: use on prefix for event handlers (onClick, onChange). Avoid inline arrow functions for performance: onClick={() => handleClick(id)} creates new reference each render. Use useCallback if this causes issues.
Q9: What is the spread operator for props and when to use it?
The spread operator (...) passes all object properties as individual props: <Component {...props} />. Use cases: forwarding props to child components, passing HTML attributes to base elements, combining prop objects. Caution: can pass unintended props causing console warnings or errors. Use destructuring to extract known props: const { className, ...rest } = props then <div {...rest} />. Useful in HOCs and wrapper components.
Q10: Why do inline object/array props cause performance issues?
Inline literals like style={{ color: 'red' }} or items={[1, 2, 3]} create new references every render. Even if values are identical, React sees different references and re-renders child components (especially problematic with React.memo). Solutions: define constants outside the component, use useMemo for computed values, or use useCallback for functions. This is why primitive props (strings, numbers) don't have this issue - they're compared by value.