Props and Data Flow in React

Props (short for properties) let a parent component pass data to a child component. They work like arguments to a function. Whatever the parent passes in, the child receives and uses to render its output.

A component cannot change its own props. Props are read-only. If you need data that changes over time, that is what state is for.

Passing props

You pass props the same way you write HTML attributes:

function App() {
  return (
    <UserCard name="Alice" age={28} isAdmin={true} />
  );
}

The child receives all props as a single object:

function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      {props.isAdmin && <span>Admin</span>}
    </div>
  );
}

Destructuring props

Most React code destructures props directly in the function signature. It is cleaner and saves you from writing props. everywhere:

function UserCard({ name, age, isAdmin }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      {isAdmin && <span>Admin</span>}
    </div>
  );
}

Both approaches produce identical output. Destructuring is the standard pattern in modern React code.

Default prop values

Use JavaScript default parameter syntax to set a fallback when a prop is not passed:

function Greeting({ name = 'World' }) {
  return <h1>Hello, {name}!</h1>;
}

// Without a prop:
<Greeting />             // Hello, World!

// With a prop:
<Greeting name="Alice" /> // Hello, Alice!

Passing different types

You can pass any JavaScript value as a prop. Use curly braces for everything except strings:

TypeExample
Stringtitle="Hello"
Numbercount={42}
BooleanisActive={true} or just isActive
Arrayitems={['a', 'b', 'c']}
Objectuser={{ name: 'Alice', age: 28 }}
FunctiononClick={handleClick}

Passing a function as a prop is how child components communicate back to their parent:

function Button({ label, onClick }) {
  return <button onClick={onClick}>{label}</button>;
}

function App() {
  function handleClick() {
    console.log('Button clicked');
  }

  return <Button label="Click me" onClick={handleClick} />;
}

The Button component does not need to know what handleClick does. It just calls whatever function it receives.

The children prop

When you write content between a component’s opening and closing tags, React passes it as a special prop called children:

function Card({ children }) {
  return <div className="card">{children}</div>;
}

function App() {
  return (
    <Card>
      <h2>Card Title</h2>
      <p>Some text inside the card.</p>
    </Card>
  );
}

The children prop is how you build wrapper components: layouts, modals, panels, and anything else that wraps content it does not know about in advance.

One-way data flow

Data in React flows in one direction: from parent to child. A child component cannot directly change data in its parent.

If a child needs to trigger a change in the parent, the parent passes a handler function down as a prop, and the child calls it:

import { useState } from 'react';

function Counter({ count, onIncrement }) {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <Counter
      count={count}
      onIncrement={() => setCount(count + 1)}
    />
  );
}

The parent owns the state. The child only displays it and calls the handler when something happens. This pattern keeps data flow predictable.

Props vs state

PropsState
Where it livesPassed from the parentInside the component
Who controls itThe parentThe component itself
Can it change?No (read-only)Yes, with the setter function
Triggers re-render?When parent re-rendersYes, when the setter is called

A simple rule: if data comes from outside the component, it is a prop. If the component manages it itself, it is state.

Common mistakes

Mutating a prop directly

// Wrong: props are read-only
function UserCard({ user }) {
  user.name = 'Changed'; // do not do this
  return <p>{user.name}</p>;
}

// Correct: derive a new value instead
function UserCard({ user }) {
  const displayName = user.name.toUpperCase();
  return <p>{displayName}</p>;
}

Calling a function instead of passing it

// Wrong: handleClick() runs immediately when the component renders
<button onClick={handleClick()}>Click</button>

// Correct: pass the function reference
<button onClick={handleClick}>Click</button>

Forgetting curly braces for non-string values

// Wrong: age becomes the string "28", not the number 28
<UserCard age="28" />

// Correct: use curly braces for numbers, booleans, arrays, objects
<UserCard age={28} />
  • Functional Components : props in detail, component composition, and the children pattern
  • State : how to manage data that changes over time inside a component
  • Lifting State Up : how to share state between sibling components