Lesson 30 of 45 beginner

Components and Props

A component is a function from inputs to UI, and the inputs only flow down

Guess first

Pick an answer. You will see if you were right at the end of the lesson.

A child component needs to tell its parent that a button was clicked. What is the normal way?

Open interactive version (quiz + challenge)

Real-world analogy

Think of a stamp that prints a name badge. You hand the stamp a name and a role, and it prints one badge. The stamp does not scribble on the paper you gave it, and each badge is separate. If the pile is reordered, each badge needs its own label, not a position number. Otherwise you glue the wrong photo to the wrong badge. The stamp is the component, the name and role are props, and the label on each badge is the key.

What is it?

A React component is a function that receives props and returns JSX, a description of the UI. React calls the function and compares the new description with the old one. It updates only the parts of the page that changed. Props are the inputs and they flow from parent to child. Data goes down as props and events go up as callbacks, so you can trace where a value came from.

Real-world relevance

A user directory page renders a list of cards. Each card is one component that gets a user, an optional highlight flag and an onSelect callback. The same card is reused on the search page and the team page. When the design changes, you edit one file. A wrong prop name or a missing field shows up as a TypeScript error in the editor. It does not show up as a blank card in production.

Key points

Quick check

Answer from memory — scrolling up first makes it stick less.

Why is key={index} a poor choice for a list that can be reordered?

What does the ? in the type { highlight?: boolean } mean?

Code example

import type { ReactNode } from 'react';

type User = { id: number; name: string; role?: string };

type UserCardProps = {
  user: User;
  highlight?: boolean;
  onSelect: (id: number) => void;
  children?: ReactNode;
};

export function UserCard({ user, highlight = false, onSelect, children }: UserCardProps) {
  return (
    <li className={highlight ? 'card highlight' : 'card'}>
      <h3>{user.name}</h3>
      <p>{user.role ?? 'Member'}</p>
      <button type="button" onClick={() => onSelect(user.id)}>Select</button>
      {children}
    </li>
  );
}

export function UserList({ users }: { users: User[] }) {
  const handleSelect = (id: number) => console.log('selected', id);
  return (
    <ul>
      {users.map(u => (
        <UserCard key={u.id} user={u} onSelect={handleSelect} highlight={u.role === 'admin'} />
      ))}
    </ul>
  );
}

Line-by-line walkthrough

  1. 1. This is one file with two components. The User type has an optional role, marked with a question mark.
  2. 2. UserCardProps types every prop: highlight is optional, onSelect is a function that takes an id, and children can be any renderable content.
  3. 3. UserCard destructures its props and gives highlight a default of false, so callers can leave it out.
  4. 4. The button calls onSelect with the user id when clicked. The card never changes the user object it received.
  5. 5. UserList defines one handler and passes it down as a callback prop to every card.
  6. 6. Each card in map gets key={u.id}, a stable id that stays with the user even if the list is reordered.

Spot the bug

type Row = { id: number; label: string };

function Rows({ rows }: { rows: Row[] }) {
  return (
    <ul>
      {rows.map((row, index) => (
        <li key={index}>
          <input defaultValue={row.label} />
        </li>
      ))}
    </ul>
  );
}
Need a hint?
What does React match when the first row is removed and the rows shift up?
Show answer
The key is the array position, so after a delete or a reorder React reuses the old element at each position. Text typed into an input then stays at the position, not with its row. Fix: use a stable id from the data, key={row.id}.

Explain like I'm 5

A component is like a recipe card. You hand it ingredients and it says what to make, without changing the ingredients you gave it. If the cook wants to tell you something, they ring the bell you gave them.

Fun fact

JSX is not required. The compiler turns each tag into a plain function call. You could write the same page with no tags at all. Nobody does, because it is hard to read.

Hands-on challenge

Say it out loud in 30 seconds: "How does data move between React components?" Then check: did you mention props flowing down, callbacks sending events up, props being read-only, and stable keys in lists?

More resources

Open interactive version (quiz + challenge) Download the whole course as PDF / Word

Lock it in

← Back to course: .NET + React Playbook