Skip to main content
React Essentials·Lesson 2 of 5

Props & Data Flow

Props are how components talk to each other. A parent component passes data down to a child through props, creating a one-way data flow that makes your app predictable and easy to debug.

Passing Props

Props are passed as attributes in JSX and received as a single object parameter in the component function:

function Button({ label, variant }: { label: string; variant: "primary" | "secondary" }) {
  const styles =
    variant === "primary"
      ? "bg-blue-600 text-white"
      : "bg-gray-200 text-gray-800";

  return <button className={`px-4 py-2 rounded ${styles}`}>{label}</button>;
}

// Usage
<Button label="Save" variant="primary" />
<Button label="Cancel" variant="secondary" />

Props are read-only. A component must never modify the props it receives.

Defining a Props Interface

For components with more than a couple of props, define a separate interface for clarity:

interface ProductCardProps {
  name: string;
  price: number;
  imageUrl: string;
  inStock: boolean;
}

function ProductCard({ name, price, imageUrl, inStock }: ProductCardProps) {
  return (
    <div className="border rounded-lg p-4">
      <img src={imageUrl} alt={name} className="w-full h-48 object-cover" />
      <h3 className="mt-2 font-semibold">{name}</h3>
      <p className="text-lg">${price.toFixed(2)}</p>
      {!inStock && <span className="text-red-500 text-sm">Out of stock</span>}
    </div>
  );
}

The children Prop

The children prop lets a component wrap arbitrary content. Anything placed between a component's opening and closing tags becomes children:

function Card({ children, title }: { children: React.ReactNode; title: string }) {
  return (
    <div className="border rounded-lg shadow-sm">
      <div className="border-b px-4 py-2 font-semibold">{title}</div>
      <div className="p-4">{children}</div>
    </div>
  );
}

// Usage
<Card title="Settings">
  <p>Manage your account preferences here.</p>
  <button>Edit Profile</button>
</Card>

Use React.ReactNode as the type for children — it covers strings, numbers, JSX elements, arrays, and fragments.

Default Props

Use JavaScript default parameter values to define fallback props:

interface AlertProps {
  message: string;
  type?: "info" | "warning" | "error";
  dismissible?: boolean;
}

function Alert({ message, type = "info", dismissible = false }: AlertProps) {
  const colors = {
    info: "bg-blue-100 text-blue-800",
    warning: "bg-yellow-100 text-yellow-800",
    error: "bg-red-100 text-red-800",
  };

  return (
    <div className={`p-3 rounded ${colors[type]} flex justify-between`}>
      <span>{message}</span>
      {dismissible && <button className="ml-4 font-bold">&times;</button>}
    </div>
  );
}

// Uses defaults: type="info", dismissible=false
<Alert message="Your changes have been saved." />

Spreading Props

When you need to forward many props to an underlying element, use the spread operator:

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label: string;
}

function Input({ label, ...rest }: InputProps) {
  return (
    <div className="flex flex-col gap-1">
      <label className="text-sm font-medium">{label}</label>
      <input className="border rounded px-3 py-2" {...rest} />
    </div>
  );
}

// All standard input attributes are forwarded
<Input label="Email" type="email" placeholder="you@example.com" required />

This pattern is common for wrapper components that add styling or layout around a native element.

One-Way Data Flow

Data in React flows in one direction: parent to child. This is by design. If a child needs to communicate back to the parent, the parent passes a callback function as a prop:

function SearchBar({ onSearch }: { onSearch: (query: string) => void }) {
  return (
    <input
      type="text"
      placeholder="Search..."
      onChange={(e)=> onSearch(e.target.value)}
    />
  );
}

function App() {
  function handleSearch(query: string) {
    console.log("Searching for:", query);
  }

  return <SearchBar onSearch={handleSearch} />;
}

The child doesn't know what happens with the data — it just calls the function it was given. This keeps components decoupled.

Summary

  • Props are read-only data passed from parent to child.
  • Use TypeScript interfaces to define prop shapes.
  • children is a special prop for wrapping content.
  • Use default parameter values for optional props.
  • Spread props with ...rest to forward attributes.
  • Data flows one way; use callback props for child-to-parent communication.