A Next.js 14 app with a public landing page, a dashboard section with a shared layout, dynamic blog post pages, and working navigation — all using the App Router.
Create Your Next.js App
Next.js is the leading React framework for production apps. The App Router (introduced in Next.js 13) uses the app/ directory and React Server Components by default. This is the modern way to build Next.js apps.
# Create a new Next.js app
npx create-next-app@latest my-app --typescript --tailwind --app --src-dir
# Prompts:
# Would you like to use ESLint? Yes
# Would you like to use import alias? Yes (@/*)
cd my-app && npm run dev
# Open http://localhost:3000Server vs Client Components
This is the most important concept in Next.js 14. By default, all components are Server Components — they render on the server and send HTML. Client Components run in the browser and can use hooks and events.
// No 'use client' = Server Component by default
// Can: fetch data, access DB directly, use async/await
// Cannot: use useState, useEffect, onClick, browser APIs
async function getPosts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5')
return res.json()
}
export default async function Home() {
const posts = await getPosts() // direct async in server component
return (
<main>
<h1>Latest Posts</h1>
{posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</main>
)
}'use client' // This makes it a Client Component
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
}Dynamic Routes and Nested Layouts
Dynamic routes use brackets: [slug]. Nested layouts let you wrap sections of your app (like a dashboard) in their own persistent UI without affecting other pages.
src/app/
├── page.tsx → /
├── layout.tsx → root layout (nav + footer)
├── blog/
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/my-post
└── dashboard/
├── layout.tsx → dashboard sidebar layout
├── page.tsx → /dashboard
└── settings/
└── page.tsx → /dashboard/settingsinterface Props {
params: { slug: string }
}
export default async function BlogPost({ params }: Props) {
// params.slug = whatever is in the URL
const { slug } = params
return (
<article>
<h1>Post: {slug}</h1>
</article>
)
}
// Generate static pages at build time
export async function generateStaticParams() {
return [{ slug: 'hello-world' }, { slug: 'getting-started' }]
}Link, Navigation, and Metadata
Use Next.js's Link component for client-side navigation. Use the Metadata API for SEO — it statically generates <head> tags without needing a separate library.
import { Metadata } from 'next'
import Link from 'next/link'
// Dynamic SEO metadata
export async function generateMetadata({ params }: Props): Promise<Metadata> {
return {
title: `${params.slug} | My Blog`,
description: `Read about ${params.slug}`,
openGraph: { title: params.slug, type: 'article' }
}
}
export default function Nav() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/blog" prefetch={true}>Blog</Link>
<Link href="/dashboard">Dashboard</Link>
</nav>
)
}What You Learned Today
- Created a Next.js 14 app with the App Router and understood the file-based routing system
- Differentiated Server Components (async, direct DB access) from Client Components (hooks, events)
- Built dynamic routes with [slug] and nested layouts for sections like dashboard
- Used the Metadata API for automatic SEO without a third-party library
Go Further on Your Own
- Add a loading.tsx file to the blog route and test the Suspense loading state
- Create a (marketing) route group that shares a layout without adding a URL segment
- Add usePathname() in a client component to highlight the active nav link
Nice work. Keep going.
Day 2 is ready when you are.
Continue to Day 2Want live instruction and hands-on projects? Join the AI bootcamp — 3 days, 5 cities.