Live Demo — deployed on Vercel
A comprehensive Next.js 16 portfolio application with Turbopack, React 19.2, and all major concepts including App Router, Server Components, SSR, SSG, ISR, API Routes, Proxy, and more. Built with TypeScript, Tailwind CSS, and Docker for production deployment.
- App Router Architecture - File-based routing with layouts and nested routes
- Server Components - Default server-side rendering for better performance
- Client Components - Interactive components with React hooks
- Server-Side Rendering (SSR) - Dynamic pages rendered on each request
- Static Site Generation (SSG) - Pre-rendered pages at build time
- Incremental Static Regeneration (ISR) - Automatic page regeneration
- Dynamic Routes - URL parameters with
[slug]syntax - API Route Handlers - Built-in API endpoints
- Proxy - Request interception, authentication, and redirects
- Loading States - Instant loading UI with Suspense
- Error Boundaries - Graceful error handling
- Custom 404 Pages - Beautiful not-found pages
- Turbopack Bundler - 2-5x faster builds, 10x faster Fast Refresh (default in Next.js 16)
- React 19.2 Support - Latest React features including View Transitions
- Cache Components - Explicit caching with "use cache" directive
- Modern Portfolio Design - Clean, professional interface
- Responsive Layout - Mobile-first design with Tailwind CSS
- Dark Mode Ready - Prepared for theme switching
- Smooth Animations - CSS animations and transitions
- Form Handling - Client-side form with validation
- SEO Optimized - Meta tags and Open Graph support
- TypeScript - Full type safety
- Tailwind CSS - Utility-first styling
- ESLint - Code quality enforcement
- Docker Support - Production-ready containerization
- Hot Reload - Fast development experience
nextjs-portfolio/
├── src/
│ ├── app/ # App Router directory
│ │ ├── layout.tsx # Root layout (required)
│ │ ├── page.tsx # Home page (/)
│ │ ├── loading.tsx # Root loading UI
│ │ ├── error.tsx # Root error boundary
│ │ ├── not-found.tsx # Custom 404 page
│ │ ├── globals.css # Global styles
│ │ │
│ │ ├── about/ # About page route
│ │ │ └── page.tsx # /about
│ │ │
│ │ ├── projects/ # Projects route (ISR demo)
│ │ │ └── page.tsx # /projects
│ │ │
│ │ ├── blog/ # Blog routes
│ │ │ ├── page.tsx # /blog (list)
│ │ │ ├── loading.tsx # Blog loading state
│ │ │ └── [slug]/ # Dynamic routes
│ │ │ └── page.tsx # /blog/[slug]
│ │ │
│ │ ├── contact/ # Contact page (Client Component)
│ │ │ └── page.tsx # /contact
│ │ │
│ │ └── api/ # API Routes
│ │ ├── hello/
│ │ │ └── route.ts # /api/hello
│ │ └── users/
│ │ ├── route.ts # /api/users
│ │ └── [id]/
│ │ └── route.ts # /api/users/[id]
│ │
│ ├── proxy.ts # Proxy — runs before every matched request
│ └── components/ # Reusable components
│ ├── Header.tsx # Navigation header
│ └── Footer.tsx # Site footer
│
├── next.config.js # Next.js configuration
├── tailwind.config.ts # Tailwind CSS config
├── tsconfig.json # TypeScript config
├── package.json # Dependencies
├── Dockerfile # Docker production build
├── docker-compose.yml # Docker Compose config
└── README.md # Documentation
- Node.js 20.9.0+
- npm or yarn
- Docker (optional, for containerized deployment)
- Browser: Chrome/Edge/Firefox 111+, Safari 16.4+
-
Clone or navigate to the project directory:
cd nextjs-portfolio -
Install dependencies:
npm install
-
Run the development server:
npm run dev
-
Open your browser: Navigate to http://localhost:3000
# Create optimized production build
npm run build
# Start production server
npm start# Build the Docker image
docker build -t nextjs-portfolio .
# Run the container
docker run -p 3000:3000 nextjs-portfolio# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose downAccess the application at http://localhost:3000
page.tsx- Creates a unique routelayout.tsx- Wraps multiple pages with shared UI- Layouts persist across navigation
Example:
app/
├── layout.tsx → Root layout for all pages
├── page.tsx → Homepage (/)
└── about/
└── page.tsx → About page (/about)
Use [param] syntax for dynamic segments:
// app/blog/[slug]/page.tsx
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <div>Post: {slug}</div>
}// This is a Server Component by default
export default async function Page() {
const data = await fetchData() // Runs on server
return <div>{data}</div>
}'use client' // This directive makes it a Client Component
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}// app/projects/page.tsx
export const revalidate = 60 // Revalidate every 60 seconds
export default async function Projects() {
const projects = await getProjects()
return <div>{/* render projects */}</div>
}async function getData() {
const res = await fetch('https://api.example.com/data', {
cache: 'force-cache' // SSG
// cache: 'no-store' // SSR
// next: { revalidate: 3600 } // ISR
})
return res.json()
}// app/api/hello/route.ts
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
return NextResponse.json({ message: 'Hello' })
}
export async function POST(request: Request) {
const body = await request.json()
return NextResponse.json({ received: body })
}Placement: With a
src/layout, the file must be atsrc/proxy.ts— not the project root. Placing it at the wrong level causes it to be silently ignored.
// src/proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Add custom header
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
export const config = {
matcher: '/api/:path*'
}Start the dev server first: npm run dev
# Example 1 — logging (check terminal output for [Proxy] logs)
curl -s http://localhost:3000/about
# Example 2 — redirect /old-blog → /blog
curl -I http://localhost:3000/old-blog
# Expect: HTTP/1.1 307, Location: http://localhost:3000/blog
# Example 3 — auth guard (no cookie → redirect to /login)
curl -I http://localhost:3000/admin
# Expect: HTTP/1.1 307, Location: .../login?from=%2Fadmin
# Example 3 — auth guard (with cookie → passes through)
curl -I http://localhost:3000/admin --cookie "auth-token=abc123"
# Expect: HTTP/1.1 200
# Example 4 — rate limit headers on API routes
curl -I http://localhost:3000/api/hello
# Expect: X-RateLimit-Limit: 100, X-RateLimit-Remaining: 99
# Example 5 — A/B test cookie set on homepage
curl -I http://localhost:3000/
# Expect: set-cookie: ab-test-variant=a (or b)
# Example 6 — rewrite /hidden → serves /about
curl http://localhost:3000/hidden
# Expect: 200 with /about page content
# Example 7 — bot blocking
curl -I http://localhost:3000/about -H "User-Agent: BadBot/1.0"
# Expect: HTTP/1.1 403<div className="bg-primary-600 text-white px-6 py-3 rounded-lg hover:bg-primary-700 transition-colors">
Button
</div>@layer components {
.btn-primary {
@apply bg-primary-600 text-white px-6 py-3 rounded-lg
hover:bg-primary-700 transition-colors;
}
}module.exports = {
reactStrictMode: true,
output: 'standalone', // For Docker
images: {
remotePatterns: [/* ... */]
}
}export default {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
colors: {
primary: {/* custom colors */}
}
}
}
}| File | Purpose |
|---|---|
layout.tsx |
Shared UI wrapper for pages |
page.tsx |
Unique route content |
loading.tsx |
Loading UI (Suspense boundary) |
error.tsx |
Error boundary fallback |
not-found.tsx |
Custom 404 page |
route.ts |
API endpoint handler |
src/proxy.ts |
Request/response interceptor (Next.js 16+) |
Create a .env.local file:
NEXT_PUBLIC_API_URL=http://localhost:3000/api
# Add your variables hereImportant:
NEXT_PUBLIC_*variables are exposed to the browser- Other variables are server-only
- Push code to GitHub
- Import project in Vercel
- Configure environment variables
- Deploy!
docker build -t nextjs-app .
docker run -p 3000:3000 nextjs-appnpm run build
npm start| Command | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Create production build |
npm start |
Start production server |
npm run lint |
Run ESLint |
- Server Components by default - Use Client Components only when needed
- Fetch data at the highest level - Avoid waterfalls
- Use TypeScript - Better developer experience
- Optimize images - Use
next/imagecomponent - Configure metadata - Better SEO
- Handle errors gracefully - Use error boundaries
- Show loading states - Better UX
- Use proxy wisely - Avoid heavy computations in
src/proxy.ts
# Kill process on port 3000
npx kill-port 3000rm -rf .next
npm run devnpm run build
# Fix reported issues- Next.js Documentation
- React Documentation
- TypeScript Handbook
- Tailwind CSS Docs
- Docker Documentation
This is a learning project. Feel free to:
- Fork the repository
- Add more examples
- Improve documentation
- Share your learnings
This project is for educational purposes. Feel free to use it for learning!
- App Router
- Server Components
- Client Components
- SSR (Server-Side Rendering)
- SSG (Static Site Generation)
- ISR (Incremental Static Regeneration)
- Dynamic Routes
- API Route Handlers
- Proxy
- Loading States
- Error Boundaries
- Not Found Pages
- Layouts
- TypeScript
- Tailwind CSS
- Docker Deployment
- Metadata API
- Form Handling
- Start with basics: Understand App Router and file structure
- Explore routing: Create pages and nested routes
- Learn rendering: Understand Server vs Client Components
- Add interactivity: Build forms and interactive features
- Data fetching: Practice SSR, SSG, and ISR
- Build APIs: Create Route Handlers
- Add proxy: Implement authentication logic with
proxy.ts - Handle errors: Add error boundaries and loading states
- Style it: Apply Tailwind CSS
- Deploy: Use Docker or Vercel
Happy Learning! 🚀
Built with ❤️ using Next.js 16, Turbopack, and React 19.2
Note: Next.js 16 requires Node.js 20.9.0 or higher. If you're on Node.js 18, please upgrade first.