How to Send Data from Parent to Child in React
Advertisement
🧩 Scenario
Architecture Walkthrough
What Can Be Passed as a Prop
Any JavaScript value can be a prop: strings, numbers, booleans, objects, arrays, functions, React elements, and even other components. Props are passed as JSX attributes in the parent and received as a single props object in the child. Boolean props can use the shorthand <Button disabled /> which is equivalent to <Button disabled={true} />.
Props are the data channel for parent-to-child communication. When the parent re-renders with a new value for a prop, the child receives the new value and React reconciles the child's output accordingly.
Props Are Read-Only
Props are immutable from the child's perspective. A component that receives props must not modify them. This constraint is central to React's predictable data flow: the parent owns the data and controls when it changes. If the child needs to change a value, it calls a callback function received as a prop, and the parent decides how to update its own state.
Mutating a prop object property (props.user.name = 'new') does mutate the underlying JavaScript object (since objects are passed by reference), but it does not trigger a React re-render. It also violates the React model and causes bugs that are difficult to trace.
The children Prop
children is a special prop that contains the JSX content placed between the opening and closing tags of a component. It enables wrapper components, layout components, and slot patterns. Typing children in TypeScript uses React.ReactNode, which accepts elements, strings, numbers, null, and arrays.
Key Code Explained
// Typing props in TypeScript
interface UserCardProps {
name: string; // required string
age: number; // required number
isVerified: boolean; // required boolean
tags: string[]; // required array
onFollow: () => void; // required callback
avatar?: string; // optional string
}
function UserCard({ name, age, isVerified, tags, onFollow, avatar }: UserCardProps) {
return (
<div className="user-card">
{avatar && <img src={avatar} alt={name} className="avatar" />}
<h3>{name}</h3>
<p>Age: {age}</p>
{isVerified && <span className="badge">Verified</span>}
<ul>
{tags.map((tag) => (
<li key={tag}>{tag}</li>
))}
</ul>
<button onClick={onFollow}>Follow</button>
</div>
);
}
// Parent passes values via JSX attributes
function App() {
return (
<UserCard
name="Ghazi Khan"
age={28}
isVerified // shorthand for isVerified={true}
tags={['React', 'TypeScript', 'Node.js']}
onFollow={() => console.log('Followed!')}
avatar="/avatars/ghazi.png"
/>
);
}
// Passing components and elements as props
interface CardProps {
title: string;
icon: React.ReactNode; // any renderable: element, string, number, null
children: React.ReactNode; // content between open and close tags
}
function Card({ title, icon, children }: CardProps) {
return (
<div className="card">
<header className="card-header">
{icon}
<h2>{title}</h2>
</header>
<div className="card-body">{children}</div>
</div>
);
}
// Usage: children is the JSX between the tags
function Dashboard() {
return (
<Card title="Analytics" icon={<ChartIcon />}>
<p>Traffic: 12,400 visits today</p>
<p>Conversion: 3.2%</p>
</Card>
);
}
// Prop spreading: forward known props to a DOM element
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
error?: string;
}
function LabeledInput({ label, error, ...rest }: InputProps) {
// rest contains all HTML input attributes (placeholder, type, value, onChange, etc.)
return (
<div className="input-wrapper">
<label>{label}</label>
<input {...rest} className={error ? 'input-error' : 'input'} />
{error && <span className="error-message">{error}</span>}
</div>
);
}
// Usage: any valid input attribute can be passed
<LabeledInput
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@example.com"
error={emailError}
/>
Extending React.InputHTMLAttributes<HTMLInputElement> for the InputProps interface is the idiomatic way to build wrapper components around HTML elements in TypeScript. It gives the parent access to all native input attributes without needing to enumerate them manually, and future HTML attributes are automatically included.
Tradeoffs
| Prop type | New reference each render | Affects React.memo | Notes |
|---|---|---|---|
| Primitive (string, number, boolean) | No | No | Safe to pass inline |
| Object / array literal in JSX | Yes | Yes (breaks memo) | Extract as constant or use useMemo |
| Inline arrow function | Yes | Yes (breaks memo) | Use useCallback for memoized children |
| Stable reference (const, useMemo, useCallback) | No | No | Correct for memoized children |
What Interviewers Actually Check
- Whether you know props are read-only and what to do when the child needs to signal a change
- Whether you can type props correctly in TypeScript
- Whether you know about the
childrenprop and how to type it - Whether you know that inline objects and functions create new references on every render
- Whether you can use prop spreading to build wrapper components
Follow-Up Questions
- What is the difference between
React.ReactNodeandReact.ReactElementfor typing thechildrenprop? - How does
React.cloneElementallow a parent to inject additional props into achildrenelement? - How would you build a
Tooltipcomponent that wraps any child element and shows a tooltip on hover without using a render prop? - What is the render prop pattern and how does it differ from using
childrenas JSX? - How do you pass a generic typed component as a prop with a TypeScript generic constraint?
Common Candidate Mistakes
- Trying to modify
props.user.namedirectly inside the child and not knowing why the parent's data is affected but the component does not re-render - Not knowing about the
childrenprop and re-implementing slot patterns by passing JSX as a custom prop - Passing
{ onClick: () => doSomething() }as a prop inside JSX and not knowing it creates a new reference every render - Using
...propsspread without knowing which props are being forwarded, accidentally forwarding unknown attributes to DOM elements (which React warns about) - Confusing props (received, read-only) with state (owned, mutable)
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you pass strings, numbers, booleans, objects, arrays, and functions as props?
- Can you type a props interface in TypeScript including optional props?
- Can you use the
childrenprop to build wrapper or layout components? - Can you explain why props are read-only and what the child should do when it needs to signal a value change?
- Can you use prop spreading to forward HTML attributes to a DOM element wrapper?
Summary
Props are the mechanism for parent-to-child data flow in React. Any JavaScript value can be a prop: primitives, objects, arrays, functions, React elements, or other components. Props are passed as JSX attributes by the parent and received as a destructured object in the child.
Props are read-only inside the receiving component. The parent owns the data and controls when it changes. If the child needs to signal a change, it calls a callback function received as a prop. Mutating prop properties directly (even if JavaScript allows it on objects) violates React's data flow model and produces bugs without triggering re-renders.
The children prop is a special prop that receives JSX content placed between a component's opening and closing tags, enabling wrapper components, layouts, and slot-based composition. In TypeScript, type children as React.ReactNode to accept any renderable content. Inline objects, arrays, and functions in JSX create new references on every render, which breaks memoization for children wrapped in React.memo.
Can a child modify the props it receives?
No. Props are read-only inside the component that receives them. Only the parent that passed them can change the value. If the child needs to signal a change, it calls a callback function passed as a prop.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement