Creating New Pages
Learn how to add entirely new pages to the K12worX website, from simple static pages to dynamic routes.
What You'll Learn
- Next.js 15 App Router file structure
- Creating static pages
- Adding pages to navigation
- Dynamic routes with parameters
- Layout and metadata
NextJS Page Basics
How Pages Work
In Next.js 15, pages are created by:
- Creating a folder in
app/ - Adding a
page.tsxfile - Exporting a default component
The folder name becomes the URL!
File Structure
app/
├── page.tsx → /
├── about/
│ └── page.tsx → /about
├── contact/
│ └── page.tsx → /contact
└── blog/
├── page.tsx → /blog
└── [slug]/
└── page.tsx → /blog/post-title
Exercise 1: Create a Simple Page
Step 1: Create Folder
# In your project root
mkdir -p app/team
Step 2: Create page.tsx
Create app/team/page.tsx:
export default function TeamPage() {
return (
<div className="min-h-screen bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 className="text-4xl font-bold text-gray-900 mb-6">
Our Team
</h1>
<p className="text-lg text-gray-600">
Meet the K12worX team members working to make education accessible.
</p>
</div>
</div>
);
}
Step 3: View Your Page
Navigate to: http://localhost:3000/team
It works! That's a new page.
Step 4: Add to Navigation
Edit components/layout/Navigation.tsx:
const navItems = [
// ... existing items ...
{ name: "Team", href: "/team" },
];
Adding Metadata
Page Title and Description
Add metadata to your page:
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Our Team | K12worX',
description: 'Meet the K12worX team dedicated to educational equity',
};
export default function TeamPage() {
return (
// ... page content ...
);
}
Now:
- Browser tab shows "Our Team | K12worX"
- Meta description for SEO
Using Layouts
Option 1: Use Default Layout
Most pages should use existing layouts:
import DefaultLayout from '@/components/layout/DefaultLayout';
export default function TeamPage() {
return (
<DefaultLayout>
<h1>Our Team</h1>
<p>Content here</p>
</DefaultLayout>
);
}
Option 2: Create Custom Layout
For special pages, create app/team/layout.tsx:
export default function TeamLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="bg-gradient-to-b from-blue-50 to-white min-h-screen">
<div className="max-w-7xl mx-auto px-4 py-12">
{children}
</div>
</div>
);
}
Exercise 2: Page with Components
Create a More Complex Page
import { Metadata } from 'next';
import Link from 'next/link';
export const metadata: Metadata = {
title: 'Volunteer | K12worX',
description: 'Join our volunteer tutoring program',
};
export default function VolunteerPage() {
return (
<div className="min-h-screen bg-white">
{/* Hero Section */}
<section className="bg-blue-600 text-white py-20">
<div className="max-w-7xl mx-auto px-4 text-center">
<h1 className="text-5xl font-bold mb-6">
Become a Tutor
</h1>
<p className="text-xl mb-8">
Help K-8 students achieve their potential
</p>
<Link
href="/get-involved"
className="inline-block px-8 py-3 bg-white text-blue-600 font-semibold rounded-lg hover:bg-gray-100 transition"
>
Get Started
</Link>
</div>
</section>
{/* Benefits Section */}
<section className="py-16">
<div className="max-w-7xl mx-auto px-4">
<h2 className="text-3xl font-bold text-center mb-12">
Why Volunteer?
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="text-center p-6">
<div className="text-4xl mb-4">🎓</div>
<h3 className="text-xl font-semibold mb-2">
Make an Impact
</h3>
<p className="text-gray-600">
Help students overcome educational barriers
</p>
</div>
<div className="text-center p-6">
<div className="text-4xl mb-4">💡</div>
<h3 className="text-xl font-semibold mb-2">
Develop Skills
</h3>
<p className="text-gray-600">
Build leadership and teaching abilities
</p>
</div>
<div className="text-center p-6">
<div className="text-4xl mb-4">🤝</div>
<h3 className="text-xl font-semibold mb-2">
Join Community
</h3>
<p className="text-gray-600">
Connect with like-minded students
</p>
</div>
</div>
</div>
</section>
</div>
);
}
Dynamic Routes
Creating Dynamic Pages
For pages with variable URLs (like blog posts):
Structure:
app/blog/[slug]/page.tsx → /blog/any-post-name
Example - app/blog/[slug]/page.tsx:
interface PageProps {
params: Promise<{
slug: string;
}>;
}
export default async function BlogPostPage({ params }: PageProps) {
const { slug } = await params;
return (
<div className="max-w-4xl mx-auto px-4 py-12">
<h1 className="text-4xl font-bold mb-6">
Blog Post: {slug}
</h1>
<p>URL slug: {slug}</p>
</div>
);
}
Now:
/blog/my-first-post→ shows "my-first-post"/blog/hello-world→ shows "hello-world"
Generate Static Pages
For pre-rendering specific slugs:
export async function generateStaticParams() {
return [
{ slug: 'introduction' },
{ slug: 'getting-started' },
{ slug: 'first-tutorial' },
];
}
NextJS will pre-render these three pages at build time.
Nested Routes
Multi-Level Pages
app/
└── resources/
├── page.tsx → /resources
└── guides/
├── page.tsx → /resources/guides
└── [category]/
└── page.tsx → /resources/guides/math
Each level can have its own page and layout.
Page Templates
Template 1: Simple Content Page
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'FAQ | K12worX',
};
export default function FAQPage() {
return (
<div className="max-w-4xl mx-auto px-4 py-12">
<h1 className="text-4xl font-bold mb-8">
Frequently Asked Questions
</h1>
<div className="space-y-6">
<div>
<h2 className="text-2xl font-semibold mb-2">
What is K12worX?
</h2>
<p className="text-gray-600">
K12worX is a student-powered tutoring network...
</p>
</div>
{/* More Q&A items */}
</div>
</div>
);
}
Template 2: Grid Layout Page
export default function ResourcesPage() {
const resources = [
{ title: 'Study Guide', icon: '📚', link: '/resources/study-guide' },
{ title: 'Videos', icon: '🎥', link: '/resources/videos' },
{ title: 'Practice', icon: '✏️', link: '/resources/practice' },
];
return (
<div className="max-w-7xl mx-auto px-4 py-12">
<h1 className="text-4xl font-bold mb-12">Resources</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{resources.map((resource) => (
<Link
key={resource.title}
href={resource.link}
className="p-6 bg-white border rounded-lg hover:shadow-lg transition"
>
<div className="text-4xl mb-4">{resource.icon}</div>
<h2 className="text-xl font-semibold">{resource.title}</h2>
</Link>
))}
</div>
</div>
);
}
Best Practices
1. Consistent Structure
Use the same container pattern:
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* Content */}
</div>
2. Add Metadata
Every page should have:
- title
- description
3. Use Semantic HTML
// ✅ Good
<main>
<section>
<h1>Title</h1>
</section>
</main>
// ❌ Bad
<div>
<div>
<div>Title</div>
</div>
</div>
4. Mobile-First Styling
// ✅ Good: Mobile first, then larger
<div className="text-2xl md:text-4xl">
// ❌ Desktop first
<div className="text-4xl md:text-2xl">
Quick Reference
Create page:
app/pagename/page.tsx
Add metadata:
export const metadata: Metadata = {
title: 'Page Title',
description: 'Description',
};
Dynamic route:
app/blog/[slug]/page.tsx
Access params:
const { slug } = await params;
Generate static pages:
export async function generateStaticParams() {
return [{ slug: 'post-1' }];
}
What's Next
Now that you can create pages, let's learn to build reusable components: