Components Explained

Components are reusable pieces of UI - the building blocks of your React application. Think of them like LEGO pieces: small, reusable, and combinable into larger structures.

What is a Component?

A component is a JavaScript function that returns JSX (HTML-like code).

Simplest component:

function Welcome() {
  return <h1>Hello!</h1>
}

That's it! A function that returns JSX is a component.

Components vs Pages

AspectPageComponent
Locationapp/*/page.tsxcomponents/ or anywhere
FilenameMust be page.tsxAny name
Creates route?YesNo
PurposeDefine a URL routeReusable UI piece
UsageOne per routeUsed many places

Key difference: Pages create routes, components are reused!

Our Component Structure

components/
├── layout/                # Layout components
│   ├── Header.tsx        # Site header
│   ├── Footer.tsx        # Site footer
│   ├── Navigation.tsx     # Nav menu
│   ├── DefaultLayout.tsx  # Standard page wrapper
│   ├── ArticleLayout.tsx  # Article page wrapper
│   └── LandingLayout.tsx  # Landing page wrapper
│
└── content/              # Content components
    ├── ContactForm.tsx   # Contact form
    ├── ActivityChronicle.tsx
    └── ...

Simple Component Example

From components/layout/Header.tsx:

export default function Header() {
  return (
    <header className="bg-white shadow-sm">
      <div className="max-w-7xl mx-auto px-4">
        <div className="flex items-center justify-between h-16">
          <Logo />
          <Navigation />
        </div>
      </div>
    </header>
  )
}

What it does:

  • Returns JSX (the header HTML)
  • Uses other components (Logo, Navigation)
  • Applies Tailwind classes for styling
  • Can be reused on every page!

Components with Props

Props pass data into components (like function parameters).

Example: Button Component

interface ButtonProps {
  text: string
  onClick: () => void
}

function Button({ text, onClick }: ButtonProps) {
  return (
    <button onClick={onClick} className="btn-primary">
      {text}
    </button>
  )
}

// Usage:
<Button text="Click me!" onClick={() => alert('Clicked!')} />
<Button text="Submit" onClick={handleSubmit} />

Props let you customize the component each time you use it!

Real Component from Our Codebase

From components/layout/Navigation.tsx:

const navItems = [
  { name: 'About', href: '/about' },
  { name: 'Model', href: '/model' },
  { name: 'Programs', href: '/programs' },
  // ...
]

export default function Navigation() {
  return (
    <nav>
      {navItems.map((item) => (
        <Link key={item.href} href={item.href}>
          {item.name}
        </Link>
      ))}
    </nav>
  )
}

What's happening:

  1. Array of nav items (data)
  2. .map() loops through them
  3. Renders a Link for each
  4. Each link gets data from the array

Result: Navigation menu generated from data!

Component Composition

Components can use other components:

function Card() {
  return (
    <div className="card">
      <CardHeader />
      <CardBody />
      <CardFooter />
    </div>
  )
}

function CardHeader() {
  return <div className="card-header">...</div>
}

function CardBody() {
  return <div className="card-body">...</div>
}

function CardFooter() {
  return <div className="card-footer">...</div>
}

Big components are made of smaller components!

Children Prop

Special prop for nested content:

function Card({ children }: { children: React.ReactNode }) {
  return (
    <div className="card">
      {children}
    </div>
  )
}

// Usage:
<Card>
  <h2>Title</h2>
  <p>Content here</p>
</Card>

Everything between <Card> and </Card> becomes children!

See this pattern in all our layouts (DefaultLayout, etc.).

When to Create a Component

Create a component when:

You use it in multiple places

  • Example: Button used 10 times → Make a Button component

It's complex and you want to organize code

  • Example: Contact form → Separate ContactForm component

It has its own logic/state

  • Example: Image gallery with state → Gallery component

Don't over-componentize:

  • If used once and simple, keep it inline
  • Don't make a component for every <div>!

File Naming Conventions

In our codebase:

  • PascalCase for component files: Header.tsx, ContactForm.tsx
  • Matches function name: function Header() in Header.tsx
  • Descriptive names: ContactForm not Form

Importing and Using Components

Importing

// Import from components folder
import Header from '@/components/layout/Header'
import ContactForm from '@/components/content/ContactForm'

// The @ is an alias for the project root

Using

export default function MyPage() {
  return (
    <div>
      <Header />
      <main>
        <ContactForm />
      </main>
    </div>
  )
}

Components are used like HTML tags!

Common Patterns

Pattern 1: Layout Component

interface LayoutProps {
  children: React.ReactNode
}

export default function DefaultLayout({ children }: LayoutProps) {
  return (
    <div className="min-h-screen flex flex-col">
      <Header />
      <main className="flex-1">
        {children}
      </main>
      <Footer />
    </div>
  )
}

Wraps pages with Header and Footer

Pattern 2: Card Component

interface CardProps {
  title: string
  description: string
  image?: string
}

export default function Card({ title, description, image }: CardProps) {
  return (
    <div className="card">
      {image && <img src={image} alt={title} />}
      <h3>{title}</h3>
      <p>{description}</p>
    </div>
  )
}

Reusable content card

Pattern 3: List Component

interface Item {
  id: string
  name: string
}

interface ListProps {
  items: Item[]
}

export default function List({ items }: ListProps) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  )
}

Renders a list from data

Best Practices

1. One Component Per File

// ✅ Good: One component per file
// Header.tsx
export default function Header() { }

// ❌ Bad: Multiple components in one file
function Header() { }
function Footer() { }

2. Descriptive Names

// ✅ Good
<ContactForm />
<NavigationMenu />
<HeroSection />

// ❌ Bad
<Form />
<Menu />
<Section />

3. Small, Focused Components

// ✅ Good: Small, focused
function Button({ text, onClick }) {
  return <button onClick={onClick}>{text}</button>
}

// ❌ Bad: Too much in one component
function MegaComponent() {
  // 500 lines of code...
}

4. TypeScript Interfaces for Props

// ✅ Good
interface ButtonProps {
  text: string
  onClick: () => void
}

function Button({ text, onClick }: ButtonProps) { }

// ❌ Bad: No types
function Button({ text, onClick }) { }

Quick Reference

Create a component:

export default function MyComponent() {
  return <div>Hello</div>
}

With props:

interface MyProps {
  title: string
}

export default function MyComponent({ title }: MyProps) {
  return <h1>{title}</h1>
}

With children:

export default function MyComponent({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

Import and use:

import MyComponent from '@/components/MyComponent'

<MyComponent title="Hello" />

Next Up

Now let's learn about layouts - the wrappers that give pages consistent structure:

Layouts Explained →