Component Fundamentals

Components are the building blocks of React applications. They encapsulate UI elements, data, and behavior into reusable pieces. This guide explores how to create and use components effectively with TypeScript.

What are Components?

Components are independent, reusable pieces of code that return markup to be rendered to the page. They come in two types:

  • Function Components - JavaScript/TypeScript functions that return JSX (modern approach)
  • Class Components - ES6 classes that extend from React.Component (legacy approach)

This guide focuses primarily on function components, which are the recommended approach in modern React.

Creating Function Components

A function component is simply a function that returns JSX:

// Basic function component
const Greeting = () => {
  return <h1>Hello, World!</h1>;
};

// One-liner for simple components
const SimpleGreeting = () => <h1>Hello, World!</h1>;

Component Naming Conventions

In React, component names must:

  • Start with an uppercase letter (to distinguish them from HTML elements)
  • Use PascalCase (each word capitalized with no spaces)
// Correct - starts with uppercase and uses PascalCase
const UserProfile = () => {
  return <div>User Profile Component</div>;
};

// Incorrect - starts with lowercase
const userProfile = () => {
  return <div>User Profile Component</div>;
};

Component Composition

One of React's most powerful features is the ability to compose complex UIs from smaller components:

// Simple components
const Header = () => {
  return <header>My Application</header>;
};

const Sidebar = () => {
  return <aside>Navigation Links</aside>;
};

const MainContent = () => {
  return <main>Page Content</main>;
};

const Footer = () => {
  return <footer>Copyright 2025</footer>;
};

// Composed component
const Layout = () => {
  return (
    <div className="layout">
      <Header />
      <div className="content-area">
        <Sidebar />
        <MainContent />
      </div>
      <Footer />
    </div>
  );
};

This composition model makes it easy to build complex interfaces while keeping the code organized and maintainable.

Understanding Props

Props (short for "properties") are React's way of passing data from parent to child components. They function similar to function parameters.

Basic Props Usage

// Defining props with an interface
interface GreetingProps {
  name: string;
}

// Component using props
const Greeting = (props: GreetingProps) => {
  return <h1>Hello, {props.name}!</h1>;
};

// Using the component with props
const App = () => {
  return <Greeting name="Sarah" />;
};

Destructuring Props

For cleaner code, you can destructure props directly in the function parameters:

interface UserCardProps {
  username: string;
  email: string;
  isAdmin: boolean;
}

// Destructuring props in the parameter
const UserCard = ({ username, email, isAdmin }: UserCardProps) => {
  return (
    <div className="user-card">
      <h2>{username}</h2>
      <p>{email}</p>
      {isAdmin && <span className="badge">Admin</span>}
    </div>
  );
};

// Usage
const App = () => {
  return (
    <UserCard
      username="johndoe"
      email="john@example.com"
      isAdmin={true}
    />
  );
};

Optional Props

In TypeScript, you can mark props as optional using the ? operator:

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary' | 'danger'; // Optional prop
  disabled?: boolean; // Optional prop
}

const Button = ({ 
  label, 
  onClick, 
  variant = 'primary', // Default value for optional prop
  disabled = false // Default value for optional prop
}: ButtonProps) => {
  return (
    <button 
      className={`btn btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {label}
    </button>
  );
};

Children Props

React components can receive and render child elements using the special children prop:

import { ReactNode } from 'react';

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

const Card = ({ title, children }: CardProps) => {
  return (
    <div className="card">
      <div className="card-header">
        <h3>{title}</h3>
      </div>
      <div className="card-body">
        {children}
      </div>
    </div>
  );
};

// Usage with children
const App = () => {
  return (
    <Card title="Important Information">
      <p>This is some content inside the card.</p>
      <button>Learn More</button>
    </Card>
  );
};

The children prop allows components to receive and render arbitrary JSX content, making them more flexible and reusable.

Prop Spreading and Rest Props

You can use the spread operator to pass multiple props at once, or collect remaining props using rest parameters:

interface ButtonProps {
  type?: 'button' | 'submit' | 'reset';
  className?: string;
  disabled?: boolean;
  onClick?: () => void;
  label: string;
}

const Button = ({ label, ...restProps }: ButtonProps) => {
  return (
    <button {...restProps}>
      {label}
    </button>
  );
};

// Usage with prop spreading
const App = () => {
  const commonProps = {
    type: 'button' as const,
    className: 'btn-primary',
    disabled: false,
    onClick: () => console.log('Clicked'),
  };
  
  return (
    <Button 
      {...commonProps} 
      label="Action Button"
    />
  );
};

Type-Safe Event Handlers in Props

When working with event handlers in props, TypeScript helps ensure type safety:

import { ChangeEvent, FormEvent, MouseEvent } from 'react';

interface InputProps {
  onChange: (e: ChangeEvent<HTMLInputElement>) => void;
  placeholder?: string;
}

const TextInput = ({ onChange, placeholder = "" }: InputProps) => {
  return (
    <input
      type="text"
      placeholder={placeholder}
      onChange={onChange}
    />
  );
};

Props vs. State

It's important to understand the distinction between props and state:

  • Props are passed down from parent components and are immutable within the receiving component
  • State is managed within a component and can be changed by the component itself (we'll cover this in the next section)

Component Organization

As your application grows, organizing components becomes crucial:

File Structure

A common approach is to organize components by feature or route:

src/
├── components/
│   ├── common/
│   │   ├── button.tsx
│   │   ├── input.tsx
│   │   └── card.tsx
│   ├── header/
│   │   ├── logo.tsx
│   │   ├── navigation.tsx
│   │   └── header.tsx
│   └── dashboard/
│       ├── summary-card.tsx
│       ├── recent-activity.tsx
│       └── dashboard.tsx

Component Granularity

Components should be small enough to be reusable but large enough to be meaningful:

  • Atomic components: Buttons, inputs, cards (highly reusable)
  • Compound components: Forms, navigation menus, data tables (composed of atomic components)
  • Page components: Dashboard, user profile, settings (composed of compound components)

Exercises

Exercise 1: Create a Basic Component

Instructions

In this exercise, you'll create your first React component that displays a simple profile card with hardcoded information.

  1. Create a new file called profile-card.tsx in your project's components directory
  2. Import React at the top of the file (if needed in your project setup)
  3. Create a function component called ProfileCard that doesn't take any props
  4. Inside the component, return JSX that creates a card with:
    • A div with a className of "profile-card" for styling
    • An img element for the user's avatar (use any placeholder image URL)
    • An h2 element for the user's name
    • A p element for the user's job title
  5. Export the component as the default export
  6. Import and use this component in your main App component or any other component

Exercise 2: Component with Props

Instructions

Now that you have a basic component, let's enhance it to make it reusable by accepting props:

  1. In your existing profile-card.tsx file, create a TypeScript interface called ProfileCardProps that defines:
    • name: a string property for the user's name
    • jobTitle: a string property for the user's job title
    • avatarUrl: a string property for the URL of the avatar image
  2. Modify your ProfileCard component to accept these props as a parameter
  3. Use TypeScript to type the props parameter with your interface
  4. Update the JSX to use the prop values instead of hardcoded values
  5. Update any component that uses ProfileCard to pass in the required props

Exercise 3: Component Composition

Instructions

Now, let's create a component that composes multiple ProfileCard components to display a team:

  1. Create a new file called team-display.tsx in your components directory
  2. Import React and your ProfileCard component at the top of the file
  3. Create a function component called TeamDisplay
  4. Inside this component, create an array of team member objects, where each object has:
    • A unique id
    • A name property
    • A jobTitle property
    • An avatarUrl property
  5. In the returned JSX:
    • Create a container div with a className of "team-display"
    • Add an h1 element with the title "Our Team"
    • Create a div with className "team-grid" to hold the cards
    • Use JavaScript's array map method to iterate over the team members array and create a ProfileCard component for each member
    • Remember to use the id as the key prop for each mapped element
  6. Export the component as the default export
  7. Import and use this component in your main App component

Exercise 4: Optional Props with Default Values

Instructions

Let's enhance our ProfileCard component to support optional props with default values:

  1. Open your profile-card.tsx file
  2. Modify the ProfileCardProps interface to include:
    • An optional theme property (use the ? syntax) that can be either 'light' or 'dark'
    • An optional showSocial boolean property to determine whether to show social media links
  3. Update the component to provide default values for these optional props
  4. Modify the JSX to apply different styling based on the theme
  5. Add conditional rendering to show or hide a social media section based on the showSocial prop

Conclusion

Components are the fundamental building blocks of React applications. By understanding how to create components, pass props, and compose them together, you've gained essential knowledge for building React applications.

In the next section, we'll explore how to add interactivity to components using state, which allows components to respond to user interactions and update the UI accordingly.

Key takeaways from this section:

  1. Components are reusable, independent pieces of UI
  2. Function components are the modern approach to writing React components
  3. Props allow you to pass data from parent to child components
  4. TypeScript enhances component development with static type checking
  5. Component composition enables building complex UIs from simple building blocks