Next.js Complete Guide: From Beginner to Expert with Code Structure
Next.js Complete Guide: From Beginner to Expert with Code Structure
Next.js has become the go-to framework for building production-ready React applications. This comprehensive guide will take you from absolute beginner to expert level, covering everything you need to know about Next.js with practical code examples and best practices.
Table of Contents
1. [Getting Started (Beginner)](#getting-started)
2. [Core Concepts](#core-concepts)
3. [Routing & Navigation](#routing--navigation)
4. [Data Fetching](#data-fetching)
5. [Styling & UI](#styling--ui)
6. [Optimization (Intermediate)](#optimization)
7. [Advanced Patterns (Expert)](#advanced-patterns)
8. [Production Code Structure](#production-code-structure)
9. [Deployment & Performance](#deployment--performance)
---
Getting Started (Beginner)
What is Next.js?
Next.js is a React framework that provides:
Installation
1# Create a new Next.js app2npx create-next-app@latest my-app3 4# Or with TypeScript5npx create-next-app@latest my-app --typescript6 7# Or with specific options8npx create-next-app@latest my-app --typescript --tailwind --appProject Structure (Basic)
1my-app/2├── app/ # App Router (Next.js 13+)3│ ├── layout.js # Root layout4│ ├── page.js # Home page5│ └── globals.css # Global styles6├── public/ # Static files7├── next.config.js # Next.js configuration8└── package.json # DependenciesYour First Page
1// app/page.js2export default function Home() {3 return (4 <div>5 <h1>Welcome to Next.js!</h1>6 <p>This is your first page.</p>7 </div>8 );9}---
Core Concepts
Pages vs App Router
Next.js offers two routing systems:
Pages Router (Traditional)
1pages/2├── index.js # Home page (/)3├── about.js # About page (/about)4└── blog/5 └── [slug].js # Dynamic route (/blog/:slug)App Router (Next.js 13+)
1app/2├── page.js # Home page (/)3├── about/4│ └── page.js # About page (/about)5└── blog/6 └── [slug]/7 └── page.js # Dynamic route (/blog/:slug)Server Components vs Client Components
Server Component (Default)
1// app/components/ServerComponent.js2// Runs on the server - no 'use client' directive3export default function ServerComponent() {4 // Can directly access databases, APIs, etc.5 const data = fetch('https://api.example.com/data');6 7 return <div>{/* Rendered on server */}</div>;8}Client Component
1// app/components/ClientComponent.js2'use client'; // Required for client-side interactivity3 4import { useState } from 'react';5 6export default function ClientComponent() {7 const [count, setCount] = useState(0);8 9 return (10 <button onClick={() => setCount(count + 1)}>11 Count: {count}12 </button>13 );14}---
Routing & Navigation
Basic Routing
1// app/about/page.js2export default function About() {3 return <h1>About Page</h1>;4}Dynamic Routes
1// app/blog/[slug]/page.js2export default function BlogPost({ params }) {3 return <h1>Blog Post: {params.slug}</h1>;4}Nested Dynamic Routes
1// app/shop/[...slug]/page.js2// Matches /shop, /shop/a, /shop/a/b, etc.3export default function Shop({ params }) {4 return <div>Shop: {params.slug.join('/')}</div>;5}Navigation
1'use client';2 3import Link from 'next/link';4import { useRouter } from 'next/navigation';5 6export default function Navigation() {7 const router = useRouter();8 9 return (10 <nav>11 {/* Client-side navigation */}12 <Link href="/about">About</Link>13 <Link href="/blog/post-1">Blog Post</Link>14 15 {/* Programmatic navigation */}Route Groups
1// app/(marketing)/about/page.js2// app/(marketing)/contact/page.js3// app/(shop)/products/page.js4// Groups don't affect URL structure---
Data Fetching
Server Components - Direct Fetching
1// app/posts/page.js2export default async function Posts() {3 // Fetch data directly in Server Component4 const res = await fetch('https://api.example.com/posts', {5 cache: 'no-store' // Always fetch fresh data6 });7 const posts = await res.json();8 9 return (10 <ul>11 {posts.map(post => (12 <li key={post.id}>{post.title}</li>13 ))}14 </ul>15 );Caching Strategies
1// Time-based revalidation2fetch(url, { next: { revalidate: 3600 } }); // Revalidate every hour3 4// Force dynamic5fetch(url, { cache: 'no-store' });6 7// Force static8fetch(url, { cache: 'force-cache' });Client-Side Data Fetching
1'use client';2 3import { useEffect, useState } from 'react';4 5export default function ClientPosts() {6 const [posts, setPosts] = useState([]);7 const [loading, setLoading] = useState(true);8 9 useEffect(() => {10 fetch('/api/posts')11 .then(res => res.json())12 .then(data => {13 setPosts(data);14 setLoading(false);15 });Using SWR (Recommended for Client Fetching)
1npm install swr1'use client';2 3import useSWR from 'swr';4 5const fetcher = (url) => fetch(url).then(res => res.json());6 7export default function Posts() {8 const { data, error, isLoading } = useSWR('/api/posts', fetcher);9 10 if (error) return <div>Failed to load</div>;11 if (isLoading) return <div>Loading...</div>;12 13 return (14 <ul>15 {data.map(post => (---
Styling & UI
CSS Modules
1// app/components/Button.module.css2.button {3 background: blue;4 color: white;5 padding: 10px 20px;6}1// app/components/Button.js2import styles from './Button.module.css';3 4export default function Button() {5 return <button className={styles.button}>Click me</button>;6}Tailwind CSS
1// app/components/Card.js2export default function Card({ title, children }) {3 return (4 <div className="bg-white rounded-lg shadow-md p-6">5 <h2 className="text-2xl font-bold mb-4">{title}</h2>6 {children}7 </div>8 );9}Styled Components
1npm install styled-components1'use client';2 3import styled from 'styled-components';4 5const StyledButton = styled.button`6 background: ${props => props.primary ? 'blue' : 'gray'};7 color: white;8 padding: 10px 20px;9 border: none;10 border-radius: 4px;11`;12 13export default function Button({ primary, children }) {14 return <StyledButton primary={primary}>{children}</StyledButton>;15}---
Optimization (Intermediate)
Image Optimization
1import Image from 'next/image';2 3export default function OptimizedImage() {4 return (5 <Image6 src="/hero.jpg"7 alt="Hero image"8 width={800}9 height={600}10 priority // Load immediately11 placeholder="blur" // Blur placeholder12 />13 );14}Font Optimization
1// app/layout.js2import { Inter } from 'next/font/google';3 4const inter = Inter({ subsets: ['latin'] });5 6export default function RootLayout({ children }) {7 return (8 <html lang="en" className={inter.className}>9 <body>{children}</body>10 </html>11 );12}Code Splitting
1'use client';2 3import dynamic from 'next/dynamic';4 5// Lazy load component6const HeavyComponent = dynamic(() => import('./HeavyComponent'), {7 loading: () => <p>Loading...</p>,8 ssr: false // Disable SSR if needed9});10 11export default function Page() {12 return <HeavyComponent />;13}Metadata & SEO
1// app/about/page.js2export const metadata = {3 title: 'About Us',4 description: 'Learn more about our company',5 openGraph: {6 title: 'About Us',7 description: 'Learn more about our company',8 images: ['/og-image.jpg'],9 },10};11 12export default function About() {13 return <h1>About</h1>;14}---
Advanced Patterns (Expert)
Server Actions
1// app/actions.js2'use server';3 4export async function createPost(formData) {5 const title = formData.get('title');6 const content = formData.get('content');7 8 // Save to database9 await db.posts.create({ title, content });10 11 revalidatePath('/posts');12 redirect('/posts');13}1// app/posts/new/page.js2import { createPost } from '../actions';3 4export default function NewPost() {5 return (6 <form action={createPost}>7 <input name="title" required />8 <textarea name="content" required />9 <button type="submit">Create Post</button>10 </form>11 );12}Middleware
1// middleware.js2import { NextResponse } from 'next/server';3 4export function middleware(request) {5 // Authentication check6 const token = request.cookies.get('token');7 8 if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {9 return NextResponse.redirect(new URL('/login', request.url));10 }11 12 return NextResponse.next();13}14 15export const config = {API Routes
1// app/api/posts/route.js2export async function GET(request) {3 const posts = await db.posts.findMany();4 return Response.json(posts);5}6 7export async function POST(request) {8 const body = await request.json();9 const post = await db.posts.create({ data: body });10 return Response.json(post, { status: 201 });11}Streaming & Suspense
1// app/dashboard/page.js2import { Suspense } from 'react';3 4async function UserProfile() {5 const user = await fetchUser();6 return <div>{user.name}</div>;7}8 9async function UserPosts() {10 const posts = await fetchPosts();11 return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;12}13 14export default function Dashboard() {15 return (Error Boundaries
1// app/error.js2'use client';3 4export default function Error({ error, reset }) {5 return (6 <div>7 <h2>Something went wrong!</h2>8 <button onClick={() => reset()}>Try again</button>9 </div>10 );11}Loading States
1// app/loading.js2export default function Loading() {3 return <div>Loading...</div>;4}---
Production Code Structure
Recommended Folder Structure
1my-app/2├── app/3│ ├── (auth)/4│ │ ├── login/5│ │ └── register/6│ ├── (dashboard)/7│ │ ├── dashboard/8│ │ └── settings/9│ ├── api/10│ │ ├── auth/11│ │ └── posts/12│ ├── components/13│ │ ├── ui/14│ │ ├── forms/15│ │ └── layout/Component Organization
1// app/components/ui/Button.js2'use client';3 4export default function Button({ 5 children, 6 variant = 'primary',7 onClick 8}) {9 const baseStyles = 'px-4 py-2 rounded font-medium';10 const variants = {11 primary: 'bg-blue-600 text-white hover:bg-blue-700',12 secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',13 };14 15 return (Utility Functions
1// app/lib/utils.js2export function formatDate(date) {3 return new Intl.DateTimeFormat('en-US', {4 year: 'numeric',5 month: 'long',6 day: 'numeric',7 }).format(new Date(date));8}9 10export function cn(...classes) {11 return classes.filter(Boolean).join(' ');12}Database Layer
1// app/lib/db.js2import { PrismaClient } from '@prisma/client';3 4const globalForPrisma = globalThis;5 6export const db = globalForPrisma.prisma || new PrismaClient();7 8if (process.env.NODE_ENV !== 'production') {9 globalForPrisma.prisma = db;10}Environment Variables
1// .env.local2DATABASE_URL="postgresql://..."3NEXT_PUBLIC_API_URL="https://api.example.com"4SECRET_KEY="your-secret-key"1// app/lib/config.js2export const config = {3 apiUrl: process.env.NEXT_PUBLIC_API_URL,4 secretKey: process.env.SECRET_KEY,5};---
Deployment & Performance
Build Optimization
1// next.config.js2/** @type {import('next').NextConfig} */3const nextConfig = {4 // Enable SWC minification5 swcMinify: true,6 7 // Optimize images8 images: {9 domains: ['example.com'],10 },11 12 // Compression13 compress: true,14 15 // Production source mapsPerformance Monitoring
1// app/instrumentation.js2export async function register() {3 if (process.env.NEXT_RUNTIME === 'nodejs') {4 await import('./lib/monitoring');5 }6}Deployment Checklist
Vercel Deployment
1# Install Vercel CLI2npm i -g vercel3 4# Deploy5vercel6 7# Production deployment8vercel --prod---
Best Practices Summary
Do's ✅
Don'ts ❌
---
Resources
---
Conclusion
Next.js is a powerful framework that simplifies React development while providing excellent performance out of the box. By following this guide, you've learned:
1. Basics: Setup, routing, and core concepts
2. Intermediate: Data fetching, styling, and optimization
3. Advanced: Server actions, middleware, and streaming
4. Expert: Production structure and deployment
Start building your Next.js application today and experience the power of modern web development!
Happy coding! 🚀
Enjoyed this article?
Support our work and help us create more free content for developers.
Stay Updated
Get the latest articles and updates delivered to your inbox.