Skip to content

React Props Explained — Passing Data Between Components

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about React Props Explained. We cover key concepts, practical examples, and best practices to help you master this topic.

React props are the mechanism for passing data from a parent component to a child component, enabling reusable and configurable components throughout your application.

What You'll Learn

  • How props work and why they are read-only
  • How to destructure props for cleaner code
  • How to set default values for props
  • How to use the children prop for composition
  • How to validate props with PropTypes

Why It Matters

Props are the primary way components communicate in React. Without props, every component would be static and isolated. Props make components configurable, reusable, and connected to their parent's data.

Real-World Use

Durga Antivirus Pro's ScanResultCard accepts props for threat level, file name, scan date, and action handlers. The same component renders differently based on the data passed, without knowing where the data comes from.

flowchart LR
    A[Parent Component] -->|props: name, email, onSelect| B[Child Component]
    B -.->|reads props| C[Renders UI]
    A -.->|can pass callbacks| B
    B -.->|calls callback| A
    style A fill:#3b82f6,color:#fff

Basic Props

Props are passed like HTML attributes:

function WelcomeBanner({ title, subtitle, theme }) {
  const styles = {
    light: { background: "#f0f9ff", color: "#1e40af" },
    dark: { background: "#1e3a5f", color: "#93c5fd" },
  };

  return (
    <div style={{ padding: "20px", borderRadius: "8px", ...styles[theme || "light"] }}>
      <h1>{title}</h1>
      {subtitle && <p>{subtitle}</p>}
    </div>
  );
}

// Usage
<WelcomeBanner
  title="Welcome to the Dashboard"
  subtitle="You have 3 new notifications"
  theme="dark"
/>

<WelcomeBanner
  title="Quick Start Guide"
  theme="light"
/>

Expected output: Two banners with different titles, optional subtitle, and theme-specific colors.

Props are the component's input parameters. They arrive as an object. Destructuring them in the function parameter makes the code cleaner and documents what props the component expects.

Props Are Read-Only

Never modify props directly:

function Counter({ initialCount }) {
  // BAD: Never modify props
  // initialCount = initialCount + 1;

  // GOOD: Use local state
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
}

function UserProfile({ user }) {
  // BAD: Mutating prop
  // user.name = "New Name";

  // GOOD: Create a copy
  const displayName = user.name.toUpperCase();

  return <p>{displayName}</p>;
}

Expected output: The counter uses local state initialized from props, never modifying the prop directly.

React components must treat props as immutable. Mutating props causes unpredictable behavior and defeats React's change detection. If you need to modify data, lift state up or use a callback.

The Children Prop

children lets components wrap arbitrary content:

function Panel({ title, children, footer }) {
  return (
    <div className="panel">
      <div className="panel-header">
        <h2>{title}</h2>
      </div>
      <div className="panel-body">
        {children}
      </div>
      {footer && (
        <div className="panel-footer">
          {footer}
        </div>
      )}
    </div>
  );
}

// Usage with children
<Panel
  title="User Settings"
  footer={<button>Save Changes</button>}
>
  <div className="form-group">
    <label>Name: <input /></label>
  </div>
  <div className="form-group">
    <label>Email: <input /></label>
  </div>
</Panel>

Expected output: A panel with header, body (containing form fields), and footer with a save button.

children is whatever JSX is placed between the component's opening and closing tags. It can be a string, element, array, or function. This pattern is essential for layout and wrapper components.

Passing Callbacks as Props

Functions as props enable child-to-parent communication:

function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <li className="todo-item">
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => onToggle(todo.id)}
      />
      <span style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
        {todo.title}
      </span>
      <button onClick={() => onDelete(todo.id)} className="delete-btn">
        Delete
      </button>
    </li>
  );
}

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, title: "Learn React", completed: false },
    { id: 2, title: "Build an app", completed: false },
  ]);

  const handleToggle = (id) => {
    setTodos(todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t));
  };

  const handleDelete = (id) => {
    setTodos(todos.filter(t => t.id !== id));
  };

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={handleToggle}
          onDelete={handleDelete}
        />
      ))}
    </ul>
  );
}

Expected output: A todo list where clicking the checkbox toggles completion and clicking delete removes the item.

Passing callbacks as props follows the "data flows down, events flow up" pattern. The parent owns the state and provides functions for the child to call when events occur.

Default Props and PropTypes

Document and validate props with default values and PropTypes:

import PropTypes from "prop-types";

function ProgressBar({ value, max, color, size, showLabel }) {
  const percentage = Math.min((value / max) * 100, 100);
  const heights = { small: 8, medium: 16, large: 24 };

  return (
    <div style={{ width: "100%", background: "#e5e7eb", borderRadius: "8px", height: `${heights[size]}px` }}>
      <div
        style={{
          width: `${percentage}%`,
          background: color,
          height: "100%",
          borderRadius: "8px",
          transition: "width 0.3s ease"
        }}
      />
      {showLabel && <span style={{ fontSize: "0.8rem" }}>{Math.round(percentage)}%</span>}
    </div>
  );
}

ProgressBar.defaultProps = {
  max: 100,
  color: "#3b82f6",
  size: "medium",
  showLabel: false,
};

ProgressBar.propTypes = {
  value: PropTypes.number.isRequired,
  max: PropTypes.number,
  color: PropTypes.string,
  size: PropTypes.oneOf(["small", "medium", "large"]),
  showLabel: PropTypes.bool,
};

Expected output: A progress bar with configurable value, color, size, and label. Default values apply when props are omitted.

PropTypes warn in development when expected props are missing or have wrong types. Default props ensure the component works with minimal configuration.

Prop Patterns

Common patterns for flexible component APIs:

// Spread props pattern
function Input({ label, error, ...inputProps }) {
  return (
    <div className="input-group">
      {label && <label>{label}</label>}
      <input
        {...inputProps}
        className={`input ${error ? "input-error" : ""}`}
      />
      {error && <span className="error-message">{error}</span>}
    </div>
  );
}

// Render prop pattern
function DataFetcher({ url, render }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(r => r.json())
      .then(d => { setData(d); setLoading(false); });
  }, [url]);

  return render({ data, loading });
}

// Usage
<DataFetcher
  url="/api/users"
  render={({ data, loading }) => (
    loading ? <Spinner /> : <UserList users={data} />
  )}
/>

Expected output: Flexible components that accept spread props for native attributes and render props for customizable rendering.

The spread pattern lets components pass unknown props directly to DOM elements. Render props invert control, letting the parent decide how to render.

Common Mistakes

  1. Modifying props directly — Props are immutable. Mutating them causes bugs and React warnings.

  2. Passing too many unrelated props — If a component receives 10+ props, it likely does too much. Group related props or split the component.

  3. Prop drilling — Passing props through many intermediate components that do not use them. Use context or composition to avoid this.

  4. Forgetting to destructure propsprops.name everywhere is verbose and unclear. Destructure at the function parameter.

  5. Not providing default values — Components crash when optional props are missing. Always provide sensible defaults or use PropTypes.

Practice Questions

  1. How does data flow between React components? Data flows down through props from parent to child. Events flow up through callback props.

  2. What happens if you try to modify a prop? React warns in development. The prop does not change. Mutations cause unpredictable behavior.

  3. What is the children prop? A special prop that represents the JSX content between the component's opening and closing tags.

  4. How do you pass a function as a prop? Pass it like any other prop: <Child onEvent={handleEvent} />. The child calls it with props.onEvent().

  5. What is prop drilling? Passing props through multiple intermediate components that do not use them, just to reach a deeper component.

Challenge

Build a ConfigurableTable component that accepts columns configuration (array of objects with key, label, render functions), data array, sorting callbacks, and row click handler. The component should be reusable for any data type by changing the columns prop.

FAQ

Are props reactive in React?

Props are the initial data. When props change, the component re-renders with new props. React handles the reactivity.

What is the difference between props and state?

Props are passed from parent to child and are read-only. State is internal to the component and can be updated.

Can I pass JSX as a prop?

Yes, any valid JSX can be passed as a prop, including other components. This is the render prop pattern.

How do I pass all props to a child?

Use the spread operator: <Child {...props} />. Passes every prop the parent received.

How do I debug props?

Add console.log(props) in the component. React DevTools also shows props for the selected component.

Mini Project

Build a FormField component that accepts label, error, helperText, and spreads remaining props to an <input>. Build a CheckoutForm using the component with fields for name, email, address, and credit card. Validate that required fields are not empty. Show error messages. Use prop spreading to pass native input attributes like type, placeholder, maxLength.

What's Next

Continue with state and effects:

React State, React Effects, React Hooks

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro