React props (short for properties) are the inputs a parent component passes to a child component, written like HTML attributes. The child receives them as a single object, usually destructured in its parameters, and uses them to decide what to render: <Greeting name="Ada" /> gives Greeting a name prop. Props flow one way, from parent to child, and are read-only; to send data back up, the parent passes a function as a prop and the child calls it.
This guide covers how to pass and read props, default values, children, passing functions, spreading, and what changed in React 19, including two older APIs (defaultProps and propTypes) that now do nothing on function components. I ran every example with React 19.3 in the browser and include what actually happened.
Passing and Reading Props
// 1. Receiving props, with default values in the destructuring
function Greeting({ name, greeting = 'Hello', children }) {
return <p className="greeting">{greeting}, {name}! {children}</p>;
}
// Using it
<Greeting name="Ada">Welcome back.</Greeting>
<Greeting name="Alan" greeting="Hi" />Rendered output: “Hello, Ada! Welcome back.” and “Hi, Alan!”. Key points:
- Strings can be written in quotes (
name="Ada"); everything else goes in braces:count={3},active={true},user={user},onSave={handleSave}. - Destructure props in the parameter list to see at a glance what a component expects.
- Default values go in the destructuring:
greeting = 'Hello'applies when the prop isundefined(not when it’snull). childrenis whatever you put between the opening and closing tags, which is how layout components such as cards and modals wrap content.
Passing Objects and Functions
// 2. Passing data down and a function up
function ProductCard({ product, onAdd }) {
return (
<div className="card">
<h3>{product.name}</h3>
<p>${product.price}</p>
<button onClick={() => onAdd(product.id)}>Add {product.name}</button>
</div>
);
}
function Shop() {
const [cart, setCart] = useState([]);
const products = [{ id: 'kb', name: 'Keyboard', price: 49 }, { id: 'ms', name: 'Mouse', price: 19 }];
return (
<>
{products.map((p) => (
<ProductCard key={p.id} product={p} onAdd={(id) => setCart((c) => [...c, id])} />
))}
<p>Cart: {cart.join(', ') || 'empty'}</p>
</>
);
}Clicking “Add Mouse” updated the parent’s cart to “Cart: ms”. That’s the standard pattern for child-to-parent communication: state lives in the parent, the parent passes data down and a callback down, and the child calls the callback. Name callback props onSomething, like DOM events. When the same data is needed by components far apart, passing it through every level becomes “prop drilling”; see React Context for global state or React Redux.
Props Are Read-Only
// 4. Props are read-only
function TryToMutate(props) {
let result;
try {
props.title = 'changed';
result = 'mutation allowed';
} catch (e) {
result = `${e.constructor.name}: ${e.message}`;
}
return <p className="mutate">{result} | frozen: {String(Object.isFrozen(props))}</p>;
}Rendered: “TypeError: Cannot assign to read only property ‘title’ of object ‘#<Object>’ | frozen: true”. In development React freezes the props object, so mutating it throws. Treat props like function arguments you don’t own: if a value needs to change, keep it in state (in this component, or in the parent that owns it).
What Changed in React 19
defaultProps no longer works on function components
// 3. The old APIs, to see what React 19 does with them
function OldDefaults({ label }) {
return <p className="old-defaults">label is: {String(label)}</p>;
}
OldDefaults.defaultProps = { label: 'from defaultProps' };Rendering <OldDefaults /> showed “label is: undefined”. React 19 removed defaultProps for function components (class components still support it), so older code that relies on it silently loses its defaults. Use default values in the destructuring instead.
propTypes are ignored
function OldTypes({ count }) {
return <p className="old-types">count is: {count}</p>;
}
OldTypes.propTypes = { count: PropTypes.number.isRequired };
<OldTypes count="not a number" />This rendered “count is: not a number” with no warning in the console. React 19 removed propTypes checking entirely, so the prop-types package no longer does anything in function components. Use TypeScript for prop types:
type ProductCardProps = {
product: { id: string; name: string; price: number };
onAdd: (id: string) => void;
};
function ProductCard({ product, onAdd }: ProductCardProps) { /* ... */ }TypeScript catches wrong prop types in your editor and at build time, before the code ever runs; see how TypeScript is compiled to JavaScript.
ref is a regular prop
In React 19, function components receive ref like any other prop, so forwardRef is no longer needed. I showed this with higher-order components in higher-order components in React.
key Is Not a Prop
// 5. key is not passed as a prop
function KeyReader(props) {
return <p className="key-reader">props.key is: {String(props.key)}</p>;
}
<KeyReader key="item-1" />This rendered “props.key is: undefined”, and React logged a warning that key is not a prop. React uses key itself to track list items; if the child needs the ID, pass it a second time under another name, such as id={item.id}.
Spreading Props
function Button({ variant = 'primary', ...rest }) {
// forward every other prop (onClick, disabled, aria-label...) to the DOM element
return <button className={`btn btn-${variant}`} {...rest} />;
}
<Button variant="secondary" onClick={save} disabled={saving}>Save</Button>Rendered on the server, this produced <button class="btn btn-secondary" disabled="" aria-label="Save draft">Save</button>: every extra prop reached the DOM element. Spread with care, though. When I passed isLoading={true}, React dropped it from the HTML and warned “React does not recognize the isLoading prop on a DOM element”. Take component-only props out of rest before spreading, and remember that spreading hides what a component accepts.
Props vs State
| Props | State | |
|---|---|---|
| Owned by | The parent | The component itself |
| Can the component change it? | No (read-only) | Yes, with its setter |
| Purpose | Configure the component from outside | Remember values that change over time |
| Changes cause a re-render? | Yes, when the parent passes new values | Yes |
A common pattern: the parent keeps the state and passes it to children as props. To understand components and rendering in more depth, see React core concepts and advanced React concepts.
Common Mistakes
- Calling a callback while rendering:
onClick={onAdd(id)}runs immediately; writeonClick={() => onAdd(id)}. - Copying props into state (
useState(props.value)) and expecting it to update when the prop changes; it won’t. Use the prop directly or derive values during render. - Relying on
defaultPropsorpropTypesin React 19 function components. - Reading
props.key, which is alwaysundefined. - Passing a new object or function every render to a
memocomponent, which defeats memoization (the React Compiler can handle this for you).
Conclusion
React props are how components talk: the parent passes values and callbacks down as attributes, the child reads them (usually by destructuring) and never changes them, and calls a callback to send data back up. In React 19, use destructuring defaults instead of defaultProps, TypeScript instead of propTypes (both are now ignored on function components, as my tests showed), and pass ref like any other prop. When data needs to travel through many layers, move to Context or a state library instead of drilling props.
FAQs
What are props in React?
Props are the inputs a parent component passes to a child, written like HTML attributes. The child receives them as an object and uses them to decide what to render. They are read-only.
How do I pass data from a child to a parent in React?
The parent passes a function as a prop, for example onAdd={handleAdd}, and the child calls it with the data: onAdd(product.id). The parent then updates its own state.
How do I set default props in React 19?
Use default values in the destructuring: function Greeting({ greeting = “Hello” }). React 19 no longer applies defaultProps to function components.
Do PropTypes still work in React 19?
No. React 19 removed propTypes checking, so the prop-types package has no effect on function components. Use TypeScript to type props instead.
Can a child component change its props?
No. Props are read-only, and React freezes the props object in development, so assigning to it throws a TypeError. Keep changing values in state, in the component that owns them.
What is the difference between props and state?
Props are passed in by the parent and cannot be changed by the component. State is owned by the component and changes with its setter function. Both trigger a re-render when they change.








