Difficulty: Beginner
Props (short for properties) are the mechanism React uses to pass data from a parent component to a child component. Think of props as the arguments you pass to a function - because that is exactly what they are. When you write `<UserCard name="Alice" age={25} />`, React calls the UserCard function with `{ name: 'Alice', age: 25 }` as its first argument. The child component reads these values from the props object to render its UI.
Props flow in one direction only: from parent to child. This is called unidirectional data flow and is a core principle of React. A child component cannot directly modify the props it receives; it can only read them. If a child needs to communicate back to a parent, the parent passes down a callback function as a prop, and the child calls that function. This one-way flow makes your application predictable and easier to debug because you always know where data comes from.
You can pass any JavaScript value as a prop: strings, numbers, booleans, arrays, objects, functions, and even other React elements. Strings can be passed with quotes (`name="Alice"`), while all other types require curly braces (`age={25}`, `items={[1,2,3]}`, `onClick={handleClick}`). There is no limit to how many props you can pass, but if a component needs many props, it may be a sign that it should be broken into smaller components.
Props are read-only - this is a strict rule in React. A component must never modify its own props. If you try to do `props.name = 'Bob'` inside a component, React will not prevent it at the language level, but it violates the React contract and will cause bugs. If a value needs to change over time, it should be state (managed with useState), not a prop. Props are for values that the parent controls.
A useful mental model is to think of components as pure functions with respect to their props. Given the same props, a component should always return the same output. This predictability is what allows React to optimize re-rendering and makes your components easy to test. You can render a component with specific props and assert what it outputs, without worrying about hidden internal state affecting the result.
function Greeting(props) {
return 'Hello, ' + props.name + '! You are ' + props.age + ' years old.';
}
// Parent passes props to child
const result1 = Greeting({ name: 'Alice', age: 25 });
const result2 = Greeting({ name: 'Bob', age: 30 });
console.log(result1);
console.log(result2);
The parent decides what data to pass, and the child reads it from the props object. The same component can render different output based on different props.
function UserProfile(props) {
const status = props.isActive ? 'Active' : 'Inactive';
const skills = props.skills.join(', ');
return props.name + ' | ' + status + ' | Skills: ' + skills;
}
const profile = UserProfile({
name: 'Charlie', // string
isActive: true, // boolean
skills: ['React', 'JS'], // array
});
console.log(profile);
// Functions as props
function ActionButton(props) {
const message = props.onClick();
return 'Button: ' + props.label + ' -> ' + message;
}
const btn = ActionButton({
label: 'Save',
onClick: () => 'Saved!'
});
console.log(btn);
Props can be any JavaScript value: strings, booleans, arrays, objects, or functions. Functions as props enable child-to-parent communication.
function Display(props) {
// WRONG: Never mutate props!
// props.value = 'modified'; // Don't do this!
// RIGHT: Use props as read-only data
const formatted = props.value.toUpperCase();
return formatted;
}
const original = { value: 'hello' };
const result = Display(original);
// The original object is unchanged
console.log('Displayed: ' + result);
console.log('Original: ' + original.value);
console.log('Props unchanged: ' + (original.value === 'hello'));
Components must treat props as read-only. Transform prop values into local variables instead of mutating the props object directly.
props, passing props, reading props, props are read-only, unidirectional data flow