Creating Layouts and Pages
Overview
In the Next.js App Router, folders define routes and layout.tsx files define a shared UI. In this activity, you will learn how nested routes work, how nested layouts compose, and why layout persistence improves the navigation experience. The goal is to create a well-structured, maintainable layout system for a Next.js application.
Prepare
For this activity, use the portfolio app you created in Week 01.
The following diagram shows
the existing app/ layout.
app/
├── about/
│ └── page.tsx
├── api/
│ └── hello/
│ └── route.ts
├── favicon.ico
├── globals.css
├── layout.tsx
└── page.tsx
In this activity, you will extend that existing structure by adding a new projects section to demonstrate nested routes and nested layouts.
app/
├── about/
│ └── page.tsx
├── api/
│ └── hello/
│ └── route.ts
├── projects/
│ ├── layout.tsx ⬅️ new section layout
│ ├── page.tsx ⬅️ overview page
│ └── opensource/ ⬅️ open source projects
│ │ └── page.tsx
│ └── school/ ⬅️ school projects
│ └── page.tsx
├── favicon.ico
├── globals.css
├── layout.tsx
└── page.tsx
Layout Composition (Root + Nested)
Your portfolio app already uses a root layout in app/layout.tsx. A nested layout (such as app/projects/layout.tsx) only applies to the nested route within it. This keeps shared UI in one place and reduces duplicate code.
Root Layout (app/layout.tsx)
└── Projects Layout (app/projects/layout.tsx)
├── Projects Overview Page (app/projects/page.tsx)
├── Open Source Projects Page (app/projects/opensource/page.tsx)
└── School Projects Page (app/projects/school/page.tsx)
In your portfolio app, this pattern lets the root layout keep global UI sections like the header and footer, while the projects layout adds section-specific navigation.
// app/layout.tsx
import "./globals.css";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
export default function RootLayout({ children }: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="bg-gray-50 dark:bg-gray-900">
<Header /> {/* Global header component */}
{children}
<Footer /> {/* Global footer component */}
</body>
</html>
);
}
// app/projects/layout.tsx
import Link from 'next/link';
export default function ProjectsLayout({ children }: { children: React.ReactNode }) {
return (
<section>
<nav>
<Link href="/projects">Overview</Link> | {' '}
<Link href="/projects/opensource">Open Source</Link> | {' '}
<Link href="/projects/school">School</Link>
</nav>
{children}
</section>
);
}
Route Groups and Partial Rendering
Route groups are folders wrapped in parentheses, such as (public) or (portfolio). They organize routes without changing the URL path, and they are often used to group routes by purpose or by layout strategy.
app/
├── (public)/
│ ├── page.tsx → /
│ └── about/
│ └── page.tsx → /about
├── (portfolio)/
│ └── projects/
│ ├── layout.tsx → shared only for /projects/*
│ ├── page.tsx → /projects
│ ├── opensource/
│ │ └── page.tsx → /projects/opensource
│ └── school/
│ └── page.tsx → /projects/school
└── api/
└── hello/
└── route.ts → /api/hello
Partial rendering means Next.js updates only the route segment that changes during client-side navigation. For example, when moving between /projects, /projects/opensource, and /projects/school, the root layout and app/projects/layout.tsx stay mounted while only the page segment is replaced. If you navigate from /projects to /about, the projects layout no longer applies, so that nested layout is removed and the route renders with only the layouts shared by both paths.
Activity Instructions
- Open your wdd430-portfolio project folder and run npm run dev
- Confirm your existing routes and files are working before you add anything new:
- / from app/page.tsx
- /about from app/about/page.tsx
- /api/hello from app/api/hello/route.ts
- Global layout from app/layout.tsx
- Create a new folder: app/projects/. Add app/projects/page.tsx with a heading such as "Projects Overview."
- Create app/projects/layout.tsx. Add section-specific UI, including links to /projects and /projects/settings, and render {children}.
- Create app/projects/opensource/page.tsx with a different heading such as "Open Source Projects."
- Create app/projects/school/page.tsx with a different heading such as "School Projects."
- Update your global navigation (in your existing header component or directly in app/layout.tsx) to include a link to /projects.
- Test this navigation sequence and observe what persists:
- /projects
- /projects/opensource
- /projects/school
- Consider what belongs in app/layout.tsx versus app/projects/layout.tsx, and why?
Check Your Understanding
Example portfolio app