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
| Aspect | Page | Component |
|---|---|---|
| Location | app/*/page.tsx | components/ or anywhere |
| Filename | Must be page.tsx | Any name |
| Creates route? | Yes | No |
| Purpose | Define a URL route | Reusable UI piece |
| Usage | One per route | Used 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:
- Array of nav items (data)
.map()loops through them- Renders a
Linkfor each - 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()inHeader.tsx - Descriptive names:
ContactFormnotForm
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: