Pages and Routing

Pages are the heart of your website - they're what users actually visit. In this guide, you'll learn how pages work in Next.js and how routing connects URLs to code.

What is a Page?

A page is a React component exported from a page.tsx file that becomes accessible at a specific URL.

Simple formula:

Folder path + page.tsx = URL route

Page Anatomy

Every page file follows this pattern:

// app/about/page.tsx

export default function AboutPage() {
  return (
    <div>
      <h1>About Us</h1>
      <p>This is the about page!</p>
    </div>
  )
}

Key parts:

  1. export default - Must export the component as default
  2. Function name - Can be anything (we use descriptive names like AboutPage)
  3. Return JSX - What displays on the page

Pages in Our Codebase

Homepage

File: app/page.tsx
URL: /
Layout: LandingLayout

import LandingLayout from '@/components/layout/LandingLayout'

export default function Home() {
  return (
    <LandingLayout>
      {/* Hero section */}
      {/* Mission section */}
      {/* Call to action */}
    </LandingLayout>
  )
}

Purpose: First page visitors see, marketing-focused

About Page

File: app/about/page.tsx
URL: /about
Layout: ArticleLayout

Purpose: Information about K12worX mission and approach

Contact Page

File: app/contact/page.tsx
URL: /contact
Layout: DefaultLayout

Purpose: Contact form for reaching out

Routing Patterns

Basic Route

app/
  programs/
    page.tsx        → /programs

One folder, one page.

Nested Route

app/
  programs/
    page.tsx                    → /programs
    student-leaders/
      page.tsx                  → /programs/student-leaders

Folders nest, URLs nest!

Multiple Routes

app/
  page.tsx                      → /
  about/page.tsx                → /about
  contact/page.tsx              → /contact
  programs/page.tsx             → /programs

Each folder with page.tsx creates a route.

import Link from 'next/link'

function Navigation() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <Link href="/contact">Contact</Link>
    </nav>
  )
}

Why Link over <a>:

  • Prefetches pages for faster navigation
  • Client-side navigation (no full page reload)
  • Maintains scroll position
  • Better performance

Programmatic Navigation

'use client'

import { useRouter } from 'next/navigation'

function MyComponent() {
  const router = useRouter()

  const handleClick = () => {
    router.push('/about')
  }

  return <button onClick={handleClick}>Go to About</button>
}

We rarely use this - Link is usually better!

Page Metadata

Set page title and description:

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'About Us - K12worX',
  description: 'Learn about the K12worX Learning Jamboree mission',
}

export default function AboutPage() {
  return <div>...</div>
}

This sets the <title> tag and meta description for SEO!

Server vs Client Pages

Server Components (Default)

// app/about/page.tsx
// No "use client" directive

export default function AboutPage() {
  // Renders on server (build time for us)
  return <div>Static content</div>
}

Benefits:

  • Faster initial load
  • Better SEO
  • No JavaScript needed in browser

Limitations:

  • No useState, useEffect
  • No event handlers (onClick, etc.)
  • No browser APIs

Client Components

'use client' // Add this directive

import { useState } from 'react'

export default function ContactPage() {
  const [name, setName] = useState('')

  return (
    <form>
      <input value={name} onChange={(e) => setName(e.target.value)} />
    </form>
  )
}

When to use:

  • Need interactivity (forms, buttons)
  • Need hooks (useState, useEffect)
  • Need browser APIs

Our approach: Start with server components, add 'use client' only when needed.

Loading States

Show loading UI while page loads:

// app/about/loading.tsx

export default function Loading() {
  return <div>Loading about page...</div>
}

We don't use this much (our site is static), but good to know!

Error Handling

Catch errors in a page:

// app/about/error.tsx

'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error
  reset: () => void
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  )
}

Automatically catches errors in page.tsx!

Common Page Patterns

Pattern 1: Content Page

import ArticleLayout from '@/components/layout/ArticleLayout'

export default function ContentPage() {
  return (
    <ArticleLayout title="Page Title" description="Description">
      <section>
        <h2>Section 1</h2>
        <p>Content here...</p>
      </section>
      <section>
        <h2>Section 2</h2>
        <p>More content...</p>
      </section>
    </ArticleLayout>
  )
}

Pattern 2: Landing Page

import LandingLayout from '@/components/layout/LandingLayout'

export default function LandingPage() {
  return (
    <LandingLayout>
      <div className="hero">
        <h1 className="text-6xl">Big Bold Title</h1>
        <p className="text-lg">Subtitle</p>
      </div>
      {/* More sections */}
    </LandingLayout>
  )
}

Pattern 3: Form Page

'use client'

import DefaultLayout from '@/components/layout/DefaultLayout'
import ContactForm from '@/components/content/ContactForm'

export default function FormPage() {
  return (
    <DefaultLayout>
      <h1>Get in Touch</h1>
      <ContactForm />
    </DefaultLayout>
  )
}

Quick Reference

Create a page:

# 1. Create folder
mkdir app/newpage

# 2. Create page.tsx
touch app/newpage/page.tsx

# 3. Add component
# export default function NewPage() { return <div>...</div> }

# 4. Visit
# http://localhost:3000/newpage

Navigation:

<Link href="/about">About</Link>

Metadata:

export const metadata = {
  title: 'Page Title',
  description: 'Page description'
}

Next Up

Now let's learn about components - the reusable building blocks:

Components Explained →