Props

Passing data to components with props

What Are Props?

Props (short for "properties") are the way you pass data from a parent component to a child component. They're like function arguments—you can pass any JavaScript value: strings, numbers, objects, arrays, functions, or even other components.

Key Concept

Props are read-only. A component should never modify its own props. Think of props as a component's configuration—they flow down from parent to child (one-way data flow).

Passing Props

Pass props to a component just like HTML attributes:

// Parent component passing props
function App() {
  return (
    <div>
      {/* Passing different types of props */}
      <UserCard 
        name="Alice"              {/* string */}
        age={25}                  {/* number */}
        isAdmin={true}            {/* boolean */}
        hobbies={['coding', 'reading']}  {/* array */}
        address={{ city: 'NYC', zip: '10001' }}  {/* object */}
        onUpdate={() => console.log('Updated!')} {/* function */}
      />
    </div>
  );
}

Receiving Props

Props are received as an object in the component's function parameter:

// Method 1: Access via props object
function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      <p>Admin: {props.isAdmin ? 'Yes' : 'No'}</p>
    </div>
  );
}

// Method 2: Destructuring (recommended)
function UserCard({ name, age, isAdmin }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Admin: {isAdmin ? 'Yes' : 'No'}</p>
    </div>
  );
}

// Method 3: Destructuring with rename
function UserCard({ name: userName, age: userAge }) {
  return <h2>{userName} is {userAge} years old</h2>;
}

Default Props

Set default values for props that might not be provided:

// Using default parameters (modern approach)
function Button({ 
  text = 'Click me', 
  variant = 'primary',
  size = 'medium',
  disabled = false 
}) {
  return (
    <button 
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
    >
      {text}
    </button>
  );
}

// Usage - all props are optional now
<Button />                           // Uses all defaults
<Button text="Submit" />              // Custom text, default variant
<Button variant="danger" size="large" />  // Custom variant and size

The Children Prop

The special children prop contains whatever you put between a component's opening and closing tags:

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

// Usage - content between tags becomes children
function App() {
  return (
    <Card title="Welcome">
      <p>This paragraph is passed as children.</p>
      <button>And so is this button!</button>
    </Card>
  );
}

// Another example: Layout component
function Layout({ children }) {
  return (
    <div className="layout">
      <Header />
      <main>{children}</main>
      <Footer />
    </div>
  );
}

Passing Functions as Props

Functions are commonly passed as props for handling events:

function Parent() {
  const handleClick = (message) => {
    alert(message);
  };

  const handleDelete = (id) => {
    console.log('Deleting item:', id);
  };

  return (
    <div>
      <Button onClick={() => handleClick('Hello!')} />
      <TodoItem 
        id={1} 
        text="Learn React"
        onDelete={handleDelete}
      />
    </div>
  );
}

function Button({ onClick }) {
  return <button onClick={onClick}>Click Me</button>;
}

function TodoItem({ id, text, onDelete }) {
  return (
    <div>
      <span>{text}</span>
      <button onClick={() => onDelete(id)}>Delete</button>
    </div>
  );
}

Spreading Props

Use the spread operator to pass all properties of an object as props:

function App() {
  const userProps = {
    name: 'Alice',
    age: 25,
    email: 'alice@example.com',
    isAdmin: true
  };

  // Instead of passing each prop individually...
  // <UserCard name={userProps.name} age={userProps.age} ... />

  // Spread them all at once
  return <UserCard {...userProps} />;
}

// Forwarding props to child elements
function CustomInput({ label, ...inputProps }) {
  return (
    <div>
      <label>{label}</label>
      <input {...inputProps} />
    </div>
  );
}

// Usage - all extra props go to the input
<CustomInput 
  label="Email"
  type="email"
  placeholder="Enter email"
  required
/>

Props vs State

Props State
Passed from parent Managed within component
Read-only (immutable) Can be changed (mutable)
Like function arguments Like local variables
Flow downward only Local to component

PropTypes (Runtime Validation)

import PropTypes from 'prop-types';

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

UserCard.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
  email: PropTypes.string.isRequired,
  isAdmin: PropTypes.bool,
  hobbies: PropTypes.arrayOf(PropTypes.string),
  address: PropTypes.shape({
    city: PropTypes.string,
    zip: PropTypes.string
  })
};

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

💡 Tip: Use TypeScript Instead

For better type safety, use TypeScript instead of PropTypes. It catches errors at compile time rather than runtime.

TypeScript Props

// Define props type with interface
interface UserCardProps {
  name: string;
  age?: number;  // optional
  email: string;
  isAdmin?: boolean;
  onUpdate: (id: number) => void;
}

// Use the interface in the component
function UserCard({ 
  name, 
  age = 0, 
  email, 
  isAdmin = false,
  onUpdate 
}: UserCardProps) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <button onClick={() => onUpdate(1)}>Update</button>
    </div>
  );
}

// With children
interface CardProps {
  title: string;
  children: React.ReactNode;
}

function Card({ title, children }: CardProps) {
  return (
    <div>
      <h3>{title}</h3>
      {children}
    </div>
  );
}

🎯 Props Best Practices

  • ✓ Keep props simple and focused
  • ✓ Use destructuring for cleaner code
  • ✓ Provide default values for optional props
  • ✓ Use TypeScript for type safety
  • ✓ Name callback props with "on" prefix (onClick, onSubmit)
  • ✓ Don't mutate props—treat them as read-only
  • ✓ Avoid passing too many props (consider restructuring)