Skip to content

Part 1.2 — Understanding Components and Props in React

Site Console Site Console
4 min read Updated Nov 28, 2025 Web Development 0 comments
React Components and Props Explained with TypeScript

If you imagine your React app as a system of LEGO bricks, components are the pieces.
Each brick is reusable, customizable, and fits perfectly into others — and props are the connectors that define how they interact.

Let’s break down how to write components that are clear, type-safe, and production-ready.


1. What Is a Component?

A React component is a reusable function that returns UI elements — typically JSX.
In modern React, all components should be functional (not class-based).

Here’s the simplest example:

function Welcome() {
  return <h1>Welcome to Full-Stack Academy!</h1>;
}

Usage:

<Welcome />

This renders a <h1> on the page. Simple, but powerful.


2. Props: Making Components Dynamic

Props let components receive data from their parent.

function Welcome({ name }: { name: string }) {
  return <h1>Welcome, {name}!</h1>;
}

Usage:

<Welcome name="Ava" />

Here, { name } is a prop (short for "property") — a parameter passed into the component.


3. Typing Props with Interfaces

When using TypeScript, always define a dedicated interface or type for props.
It makes the code readable and prevents bugs.

interface WelcomeProps {
  name: string;
  age?: number; // optional
}

export function Welcome({ name, age }: WelcomeProps) {
  return (
    <section>
      <h1>Hello, {name}!</h1>
      {age && <p>Age: {age}</p>}
    </section>
  );
}

Benefits:

  • Autocomplete in VS Code

  • Compile-time type checking

  • Self-documenting code


4. Default Props and Children

React components can include children — nested JSX passed between tags.

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

export function Card({ title, children }: CardProps) {
  return (
    <div className="p-4 border rounded-lg">
      <h2>{title}</h2>
      <div>{children}</div>
    </div>
  );
}

Usage:

<Card title="User Info">
  <p>Name: Ava</p>
  <p>Email: ava@example.com</p>
</Card>

Children let you nest content flexibly, making components composable.


5. Composition Over Inheritance

In React, composition replaces traditional inheritance.
Instead of extending base components, you nest smaller ones to build bigger ones.

Example:

function Avatar({ src }: { src: string }) {
  return <img className="w-12 h-12 rounded-full" src={src} alt="User avatar" />;
}

function UserProfile({ name, avatar }: { name: string; avatar: string }) {
  return (
    <div className="flex items-center space-x-2">
      <Avatar src={avatar} />
      <span>{name}</span>
    </div>
  );
}

Small, focused components = higher reusability and testability.


6. Reusable Component Patterns

Let’s create a simple reusable Button component that supports multiple variants.

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary";
}

export function Button({ label, onClick, variant = "primary" }: ButtonProps) {
  const base = "px-4 py-2 rounded text-white font-semibold";
  const styles =
    variant === "primary" ? "bg-blue-600 hover:bg-blue-700" : "bg-gray-500 hover:bg-gray-600";

  return (
    <button className={`${base} ${styles}`} onClick={onClick}>
      {label}
    </button>
  );
}

Usage:

<Button label="Save" onClick={() => console.log("Saved!")} />
<Button label="Cancel" variant="secondary" onClick={() => console.log("Canceled!")} />

TypeScript enforces valid variant values — no accidental typos.


7. Component Folder Structure

Organize components logically. A typical structure:

src/
├── components/
│   ├── ui/
│   │   ├── Button.tsx
│   │   ├── Card.tsx
│   ├── layout/
│   │   ├── Header.tsx
│   │   ├── Footer.tsx
│   ├── features/
│   │   ├── UserProfile.tsx

Keep each component small, focused, and testable.
Later, you’ll group related components by feature (e.g., users, tasks, auth).


8. Prop Drilling (and When to Avoid It)

“Prop drilling” means passing props through multiple layers just to reach a deeply nested component.

Example:

<App>
  <Dashboard user={user} />
</App>

If <Dashboard> passes user through several layers just to reach <UserCard>, that’s prop drilling.
Later (in Part 6), we’ll solve this elegantly using Context and Reducers.


9. Debugging Component Props

Use browser dev tools or VS Code’s TypeScript hints to inspect props.
Adding console.log(props) temporarily helps understand data flow during development.

In React DevTools, you can view each component’s props tree in real time — invaluable when debugging complex UIs.


10. Wrapping Up

You now know how to:

  • Define typed, reusable components

  • Pass and validate props

  • Compose layouts without inheritance

  • Build flexible UI structures that scale

These fundamentals make up 70% of daily React work.
In the next post — Part 1.3: Managing State with useState and useEffect — we’ll add behavior to our components and connect them to dynamic data.

Related

Leave a comment

Sign in to leave a comment.

Comments