Docs LogoDocs

Props - Passing Data to Components

Documentation for Props - Passing Data to Components.

Props - Passing Data to Components

What are Props?

Props are how you pass data from parent to child components.

Definition: Props (short for "properties") are arguments passed to React components. They allow parent components to pass data down to child components, making components dynamic and reusable. Props are read-only - a component must never modify its own props.

Why Use Props?

PurposeExplanation
Data FlowPass data from parent to child
ConfigurationCustomize component behavior
ReusabilitySame component, different data
Component CommunicationParent controls child's appearance

Props Flow (Unidirectional)

┌─────────────────────────────────────────────────────────┐
│                    One-Way Data Flow                    │
├─────────────────────────────────────────────────────────┤
│                                                         │
│    Parent Component                                     │
│         │                                               │
│         │  props = { name: "Alice", age: 25 }           │
│         ↓                                               │
│    Child Component                                      │
│         │                                               │
│         │  (Cannot modify props)                        │
│         │  (Can only read and use)                      │
│         ↓                                               │
│    Grandchild Component                                 │
│                                                         │
└─────────────────────────────────────────────────────────┘

Basic Props Usage

Passing Props

// Parent component
function App() {
  return (
    <div>
      <UserCard
        name="Alice"
        age={25}
        isActive={true}
        hobbies={["reading", "coding"]}
        address={{ city: "Mumbai", country: "India" }}
      />
    </div>
  );
}

Receiving Props

// Method 1: Props object
function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      <p>Status: {props.isActive ? "Active" : "Inactive"}</p>
    </div>
  );
}

// Method 2: Destructuring (preferred)
function UserCard({ name, age, isActive }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Status: {isActive ? "Active" : "Inactive"}</p>
    </div>
  );
}

Props Data Types

TypeSyntaxExample
Stringprop="value" or prop={'value'}title="Hello"
Numberprop={123}age={25}
Booleanprop={true} or just propisActive = isActive={true}
Arrayprop={[1, 2, 3]}items={[1, 2, 3]}
Objectprop={{ key: value }}user={{ name: 'Alice' }}
Functionprop={function}onClick={handleClick}
JSXprop={<Component />}icon={<Icon />}
Nullprop={null}data={null}
// All data types example
function App() {
  const handleClick = () => console.log("Clicked!");

  return (
    <UserProfile
      name="Alice" // String
      age={25} // Number
      isAdmin={false} // Boolean
      isActive // Boolean (true)
      skills={["React", "Node"]} // Array
      address={{ city: "Mumbai" }} // Object
      onSave={handleClick} // Function
      header={<h1>Profile</h1>} // JSX
      data={null} // Null
    />
  );
}

Default Props

Set fallback values when props aren't provided.

// Method 1: Default parameters (recommended)
function Button({ text = "Click Me", variant = "primary", disabled = false }) {
  return (
    <button className={`btn btn-${variant}`} disabled={disabled}>
      {text}
    </button>
  );
}

// Method 2: defaultProps (legacy)
function Button({ text, variant, disabled }) {
  return (
    <button className={`btn btn-${variant}`} disabled={disabled}>
      {text}
    </button>
  );
}

Button.defaultProps = {
  text: "Click Me",
  variant: "primary",
  disabled: false,
};

PropTypes (Type Checking)

Runtime type checking for props.

import PropTypes from "prop-types";

function UserCard({ name, age, email, isActive, role, onClick }) {
  return (
    <div onClick={onClick}>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
    </div>
  );
}

UserCard.propTypes = {
  // Basic types
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
  email: PropTypes.string,
  isActive: PropTypes.bool,

  // Specific values
  role: PropTypes.oneOf(["admin", "user", "guest"]),

  // Multiple types
  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

  // Array of specific type
  tags: PropTypes.arrayOf(PropTypes.string),

  // Object shape
  user: PropTypes.shape({
    name: PropTypes.string.isRequired,
    age: PropTypes.number,
  }),

  // Function
  onClick: PropTypes.func,

  // Any renderable (string, number, element, array)
  children: PropTypes.node,

  // React element
  icon: PropTypes.element,
};

UserCard.defaultProps = {
  age: 0,
  isActive: false,
};

PropTypes Reference

PropTypeValidates
PropTypes.stringString
PropTypes.numberNumber
PropTypes.boolBoolean
PropTypes.arrayArray
PropTypes.objectObject
PropTypes.funcFunction
PropTypes.nodeAnything renderable
PropTypes.elementReact element
PropTypes.anyAny type
.isRequiredMark as required
PropTypes.arrayOf()Array of specific type
PropTypes.shape({})Object with specific shape
PropTypes.oneOf([])One of specific values
PropTypes.oneOfType()One of specific types

children Prop

Special prop for content between component tags.

// Card component using children
function Card({ title, children }) {
  return (
    <div className="card">
      <h3>{title}</h3>
      <div className="card-body">{children}</div>
    </div>
  );
}

// Usage - anything between tags becomes children
function App() {
  return (
    <Card title="User Info">
      <p>Name: Alice</p>
      <p>Age: 25</p>
      <button>Edit</button>
    </Card>
  );
}

children Patterns

// Single child
<Container>
  <p>Single element</p>
</Container>

// Multiple children
<Container>
  <Header />
  <Main />
  <Footer />
</Container>

// Function as children (render props)
<DataFetcher url="/api/users">
  {(data) => <UserList users={data} />}
</DataFetcher>

// Children manipulation
function Wrapper({ children }) {
  return (
    <div>
      {React.Children.map(children, (child, index) => (
        <div key={index} className="wrapped">
          {child}
        </div>
      ))}
    </div>
  );
}

Spread Props

Pass all properties of an object as props.

const userProps = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com'
};

// Without spread
<UserCard name={userProps.name} age={userProps.age} email={userProps.email} />

// With spread
<UserCard {...userProps} />

// Combining with other props
<UserCard {...userProps} isActive={true} />

// Rest props pattern
function Button({ variant, size, ...rest }) {
  return (
    <button className={`btn btn-${variant} btn-${size}`} {...rest}>
      {rest.children}
    </button>
  );
}

Passing Functions as Props

Enable child-to-parent communication.

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

  const handleIncrement = () => {
    setCount(count + 1);
  };

  const handleChange = (newValue) => {
    setCount(newValue);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <Counter onIncrement={handleIncrement} onChange={handleChange} />
    </div>
  );
}

// Child component
function Counter({ onIncrement, onChange }) {
  return (
    <div>
      <button onClick={onIncrement}>+1</button>
      <button onClick={() => onChange(10)}>Set to 10</button>
    </div>
  );
}

Props vs State

AspectPropsState
SourceParent componentComponent itself
MutabilityRead-onlyCan be changed
Update triggerParent re-renderssetState/setter
PurposeConfigure from outsideInternal data management
AnalogyFunction parametersLocal variables

Common Mistakes & Exceptions

1. Modifying Props Directly

// ❌ Never modify props
function UserCard({ user }) {
  user.name = "New Name"; // Mutation!
  return <h1>{user.name}</h1>;
}

// ✅ Create new object if needed
function UserCard({ user }) {
  const displayName = user.name.toUpperCase();
  return <h1>{displayName}</h1>;
}

2. Forgetting to Pass Required Props

// Define requirements
UserCard.propTypes = {
  name: PropTypes.string.isRequired,  // Required!
  age: PropTypes.number
};

// ❌ Missing required prop
<UserCard age={25} />  // Warning: name is required

// ✅ Provide required props
<UserCard name="Alice" age={25} />

3. Incorrect Boolean Props

// ❌ String "false" is truthy!
<Button disabled="false" />  // Button IS disabled!

// ✅ Use actual boolean
<Button disabled={false} />

// ✅ Omit for false, include for true
<Button disabled />     // true
<Button />              // disabled is undefined (falsy)

4. Inline Object/Array Props

// ❌ Creates new reference every render
<MyComponent style={{ color: 'red' }} />
<MyComponent items={[1, 2, 3]} />

// ✅ Define outside component or use useMemo
const style = { color: 'red' };
const items = [1, 2, 3];
<MyComponent style={style} items={items} />

Interview Questions & Answers

Q1: What are props in React and why are they important?

Props (properties) are arguments passed to React components to make them dynamic and reusable. They enable one-way data flow from parent to child components, following React's unidirectional data architecture. Props are read-only - a component cannot modify its own props, maintaining predictable behavior. They serve as a component's "configuration" - the same component with different props produces different outputs (like a function with different arguments).


Q2: Why are props immutable (read-only)?

Props are immutable to maintain React's predictable, unidirectional data flow. If components could modify props, data could flow unpredictably causing bugs and making debugging difficult. Immutability enables optimizations - React can quickly determine if a component needs re-rendering by comparing props references. It also enforces clear component responsibilities: parents control data, children display it. If a child needs to "change" data, it calls a function prop to notify the parent.


Q3: What is the children prop and when do you use it?

The children prop is a special prop that contains content passed between a component's opening and closing tags. It enables component composition - creating wrapper components that can contain any content. Use cases: Layout components (Card, Modal, Container), providers (ThemeProvider, AuthProvider), and components that enhance/wrap others. Access via props.children or destructure { children }. It can be any renderable content: text, elements, arrays, or even functions (render props pattern).


Q4: What is the difference between props and state?

Props are passed from parent to child, are read-only, and change triggers come from the parent re-rendering. State is managed within the component, can be changed using setState/hooks, and changes trigger the component's own re-render. Think of props as function parameters (external input) and state as local variables (internal data). A component is "controlled" when its behavior is determined by props, and "uncontrolled" when it manages its own state.


Q5: What are PropTypes and why use them?

PropTypes provide runtime type-checking for props in development. They validate that components receive props of the correct type, catching bugs early. Benefits: documentation (shows expected props), debugging (console warnings for type mismatches), and team collaboration (clear contracts). However, they only work in development and add bundle size. For production apps and better developer experience, many teams use TypeScript instead, which provides compile-time type checking.


Q6: How do you set default values for props?

Two methods: Default parameters (recommended for functional components): function Button({ text = 'Click' }) {}. defaultProps (works for both but legacy): Button.defaultProps = { text: 'Click' }. Default parameters are preferred because they're standard JavaScript, work with TypeScript better, and are evaluated at destructuring time. defaultProps are evaluated after PropTypes checking. For required props, use PropTypes.isRequired instead of defaults.


Q7: What is prop drilling and how do you avoid it?

Prop drilling is passing props through multiple intermediate components that don't need them, just to reach a deeply nested child. Example: App → Layout → Sidebar → UserName all passing user prop. Problems: cluttered code, harder maintenance, unnecessary re-renders. Solutions: Context API for global data (themes, user auth), State management (Redux, Zustand) for complex state, Component composition - pass components rather than data, Custom hooks for shared logic.


Q8: How do you pass a function as a prop and why?

Pass functions to enable child-to-parent communication. Define in parent: const handleClick = () => {}, pass as prop: <Child onClick={handleClick} />, call in child: <button onClick={onClick}>. Use cases: form submissions, state updates, event handling. Naming convention: use on prefix for event handlers (onClick, onChange). Avoid inline arrow functions for performance: onClick={() => handleClick(id)} creates new reference each render. Use useCallback if this causes issues.


Q9: What is the spread operator for props and when to use it?

The spread operator (...) passes all object properties as individual props: <Component {...props} />. Use cases: forwarding props to child components, passing HTML attributes to base elements, combining prop objects. Caution: can pass unintended props causing console warnings or errors. Use destructuring to extract known props: const { className, ...rest } = props then <div {...rest} />. Useful in HOCs and wrapper components.


Q10: Why do inline object/array props cause performance issues?

Inline literals like style={{ color: 'red' }} or items={[1, 2, 3]} create new references every render. Even if values are identical, React sees different references and re-renders child components (especially problematic with React.memo). Solutions: define constants outside the component, use useMemo for computed values, or use useCallback for functions. This is why primitive props (strings, numbers) don't have this issue - they're compared by value.

Last updated on July 15, 2026

On this page