Props (Properties)
Props (Properties)
Level 1 — Core Concepts The input configuration mechanism for passing read-only data and event callbacks from parent components down to child components.
1. Prerequisites
- Components — The visual functional units that receive props as function arguments.
2. Term Category
Component Pattern (data contract): Props (short for properties) represent the external input parameters passed into a React component. They serve as a component's public data contract, allowing parent components to pass state data, configuration settings, primitive values, objects, elements, and callback functions down to child components.
In React's unidirectional data flow model, props are strictly immutable (read-only) snapshots; a child component can read its incoming props but is forbidden from mutating them directly.
3. Explanation
(1) Design Motivation — "Why did we design this?"
If a component like <UserProfile /> had hardcoded user details ("Alice", "25", "Developer") inside its body, rendering it five times would display five identical copies. The component would be reusable, but entirely static.
React introduced Props to transform static component templates into dynamic, customizable functions. Props act exactly like arguments passed into a standard JavaScript function:
// Plain JavaScript Function
function add(a, b) { return a + b; }
// React Component Function receiving Props object
function AddDisplay({ a, b }) { return <div>Result: {a + b}</div>; }
By passing different prop values (<Profile name="Alice" />, <Profile name="Bob" />), parent components customize the visual appearance and behavior of child components while sharing a single underlying template implementation.
(2) Reality Metaphor
Imagine a custom T-shirt printing factory.
- Static Template (Component Body): The factory owns a high-tech printing machine (
<TShirt />) configured with a specific cotton shirt design and seam stitching. - Custom Order Sheet (Props): Each customer submits a custom order sheet (
size="L",color="navy",graphic="logo.png"). The machine reads the custom order sheet and prints a customized shirt. The machine never modifies the original order sheet submitted by the customer; it simply reads the specifications and delivers the requested shirt.
(3) React Code Examples
Short Snippet
// Destructuring props directly in the parameter signature
function ActionButton({ label, variant = 'primary', onClick }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{label}
</button>
);
}
Fuller Example
import React from 'react';
// Child component receiving destructured primitive props, object props, and callbacks
function MetricCard({ title, data, onRefresh }) {
return (
<div className="metric-card">
<div className="card-header">
<h4>{title}</h4>
<button onClick={onRefresh} className="btn-icon">↻</button>
</div>
<div className="card-body">
<span className="value">{data.current} {data.unit}</span>
<span className={data.delta >= 0 ? 'positive' : 'negative'}>
{data.delta >= 0 ? '+' : ''}{data.delta}% vs last week
</span>
</div>
</div>
);
}
// Parent component passing props down
export default function DashboardGrid() {
const cpuMetric = { current: 48, unit: '%', delta: -3.2 };
const handleCpuRefresh = () => {
console.log("Refreshing CPU telemetry...");
};
return (
<div className="grid">
<MetricCard
title="CPU Utilization"
data={cpuMetric}
onRefresh={handleCpuRefresh}
/>
</div>
);
}
4. Common Mistakes & Pitfalls
Mistake 1: Attempting to Mutate Props Directly Inside Child Components
The mistake: Writing props.count = props.count + 1 or user.name = 'Bob' inside a child component body.
Why it's wrong: Props are Strictly Read-Only. Mutating a prop directly breaks React's unidirectional data flow, bypasses state change tracking, and mutates object references shared across other components. If a child needs to modify data, the parent must pass a callback function prop that updates parent state.
Incorrect:
function Counter(props) {
const handleIncrement = () => {
// ❌ Error: Props are read-only! Cannot mutate props directly.
props.count = props.count + 1;
};
return <button onClick={handleIncrement}>Count: {props.count}</button>;
}
Fix:
// ✅ Child invokes parent callback prop to request state changes
function Counter({ count, onIncrement }) {
return <button onClick={onIncrement}>Count: {count}</button>;
}
Mistake 2: Passing String Numbers or Booleans Without Curly Braces {}
The mistake: Writing <Rating score="5" isVerified="true" /> expecting numeric and boolean primitives.
Why it's wrong: Quoted strings pass literal string primitives ('5' and 'true'). Passing numeric values as strings leads to mathematical concatenation bugs ('5' + 1 = '51'). Wrap numbers and booleans inside curly braces <Rating score={5} isVerified={true} />.
Incorrect:
// ❌ Passes string '5' instead of number 5
<ProductCard price="29.99" discount="5" />
Fix:
// ✅ Passes numbers using expression curly braces {}
<ProductCard price={29.99} discount={5} />
Mistake 3: Forgetting Destructuring Fallback Default Prop Values
The mistake: Reading nested prop properties like user.profile.avatar without checking if user or profile props are undefined.
Why it's wrong: If a parent component passes null or undefined for a prop, accessing properties on it throws a runtime TypeError: Cannot read properties of undefined. Provide default parameters directly in parameter destructuring signatures.
Incorrect:
// ❌ Throws TypeError if settings prop is omitted!
function UserAvatar({ user }) {
return <img src={user.profile.avatar} alt="User" />;
}
Fix:
// ✅ Clean default parameters and optional chaining prevent crashes
function UserAvatar({ user = { profile: { avatar: '/default.png' } } }) {
return <img src={user.profile?.avatar ?? '/default.png'} alt="User" />;
}
5. Practice Exercises
Exercise 1: IoT Sensor Gauge Component (IoT Telemetry)
Scenario: Create a reusable SensorGauge component for an industrial monitoring platform that receives telemetry parameters and threshold alert callbacks.
Requirements:
- Create
SensorGaugeacceptinglabel,reading,min,max, andonAlertprops. - Calculate percentage fill
((reading - min) / (max - min)) * 100. - If
reading > max, invokeonAlert(label, reading)during user button clicks. - Use parameter destructuring with fallback default values.
Answer
Implementation
import React from 'react';
export function SensorGauge({
label = 'Unknown Sensor',
reading = 0,
min = 0,
max = 100,
onAlert
}) {
const percentage = Math.min(100, Math.max(0, ((reading - min) / (max - min)) * 100));
const isOverLimit = reading > max;
return (
<div className="gauge-box">
<h5>{label}</h5>
<div className="reading-display">{reading}</div>
<div className="meter-track">
<div className="meter-bar" style={{ width: `${percentage}%` }} />
</div>
{isOverLimit && (
<button className="btn-alert" onClick={() => onAlert?.(label, reading)}>
Trigger Over-Limit Log
</button>
)}
</div>
);
}
Technical Explanation
- Default Parameters: Default values in destructuring signatures (
min = 0,max = 100) protect against missing props. - Read-Only Props: Input props remain strictly read-only; calculations create derived local constants (
percentage). - Callback Invocations: Optional chaining (
onAlert?.()) safely fires parent callbacks without direct state mutation. - Dynamic Style Binding: Inline width percentages map cleanly from calculated prop ratios.
Exercise 2: Financial Order Form Input Field (Financial Trading)
Scenario: Build a reusable OrderInputField component for an algorithmic trading desk that accepts input configuration parameters and value change callbacks.
Requirements:
- Create
OrderInputFieldtakinglabel,value,type('number' | 'text'),step, andonChange. - Format label to uppercase inside template markup.
- Render styled numeric step controls calling
onChange. - Ensure components do not mutate input
valueprops directly.
Answer
Implementation
import React from 'react';
export function OrderInputField({
label,
value,
type = 'number',
step = '0.01',
onChange
}) {
return (
<div className="order-input-group">
<label>{label.toUpperCase()}</label>
<input
type={type}
step={step}
value={value}
onChange={e => onChange(e.target.value)}
/>
</div>
);
}
Technical Explanation
- Controlled Inputs: Component receives current value via
valueprop and notifies parent of user typing viaonChange. - Derived Text Transformations:
label.toUpperCase()formats prop text during render without mutating input props. - Configurable Defaults: Default
type = 'number'parameter allows clean consumption for numeric financial fields. - Unidirectional Event Flow: User keystrokes pass value strings upward to parent state setters cleanly.
Exercise 3: E-Commerce Product Badge Bar (E-Commerce)
Scenario: Build a ProductBadgeBar component displaying product status tags (isNew, onSale, discountPercent).
Requirements:
- Create
ProductBadgeBartakingbadgesobject prop (isNew,onSale,discountPercent). - Render badges conditionally based on boolean properties inside
badges. - Format discount text (
-${discountPercent}% OFF). - Destructure nested properties safely inside parameter signature.
Answer
Implementation
import React from 'react';
export function ProductBadgeBar({ badges = {} }) {
const { isNew = false, onSale = false, discountPercent = 0 } = badges;
return (
<div className="badge-bar">
{isNew && <span className="badge badge-new">NEW ARRIVAL</span>}
{onSale && (
<span className="badge badge-sale">
SALE -{discountPercent}% OFF
</span>
)}
</div>
);
}
Technical Explanation
- Nested Destructuring: Destructuring nested properties with defaults (
const { isNew = false } = badges) provides safe prop parsing. - Conditional Tag Rendering: Logical AND operators (
{isNew && ...}) render badge nodes conditionally based on prop flags. - Prop Snapshot Purity: Props act as immutable inputs; no global objects are modified.
- Modular Customization: Allows parent components to configure badges via simple object prop pass-throughs.
6. Related Terms
- Render Purity — The rule explaining why props must remain read-only snapshots during render.
- State — Unlike Props (passed down read-only), State is internal component data that mutates to trigger renders.
- Unidirectional Data Flow — The architectural rule specifying that props only flow top-down.
- Components — Reusable functional units receiving props.
7. Key Takeaways
- Props are read-only input parameters passed from parent components to child components.
- Component functions receive props as a single JavaScript object parameter; modern developers destructure props in parameters.
- Props are Strictly Read-Only; child components must never mutate incoming props directly.
- Pass numbers, booleans, and JavaScript expressions wrapped inside curly braces (
count={5}). - To update parent data from a child, pass callback functions as props (
onUpdate={handleUpdate}).