What is Next.js?

Next.js is a full-stack React framework that gives you building blocks to create web applications. It handles the tooling and configuration needed for React, and provides additional structure, features, and optimizations for your application.

🎯 Key Benefits

⚡ Performance

  • Automatic code splitting
  • Image optimization
  • Font optimization
  • Script optimization

🔍 SEO

  • Server-side rendering
  • Static site generation
  • Meta tag management
  • Sitemap generation

🛠️ Developer Experience

  • Zero configuration
  • Fast refresh
  • Built-in CSS support
  • TypeScript support

🌐 Full-stack

  • API routes
  • Middleware
  • Database integration
  • Authentication

React vs Next.js

React (Client-Side)

📱 Single Page Application

  • Rendering: Client-side only
  • Initial Load: Shows blank page until JS loads
  • SEO: Limited without additional setup
  • Routing: Requires React Router
  • Performance: Depends on bundle size

✅ Best For:

  • Interactive dashboards
  • Web applications
  • Admin panels
  • Complex user interfaces
Next.js (Full-Stack)

🌐 Multiple Rendering Options

  • Rendering: SSR, SSG, or Client-side
  • Initial Load: Shows content immediately
  • SEO: Excellent out of the box
  • Routing: File-based routing built-in
  • Performance: Optimized by default

✅ Best For:

  • Marketing websites
  • E-commerce sites
  • Blogs and documentation
  • Full-stack applications

Getting Started with Next.js

🚀 Creating a Next.js App

Installation
# Create a new Next.js app
npx create-next-app@latest my-app

# With TypeScript
npx create-next-app@latest my-app --typescript

# With Tailwind CSS
npx create-next-app@latest my-app --tailwind

# Navigate to project
cd my-app

# Start development server
npm run dev

📁 Project Structure

Next.js Project Structure
my-app/
├── pages/           # File-based routing
│   ├── index.js     # Home page (/)
│   ├── about.js     # About page (/about)
│   └── api/         # API routes
│       └── hello.js # API endpoint (/api/hello)
├── public/          # Static files
│   ├── favicon.ico
│   └── images/
├── styles/          # CSS files
│   ├── globals.css
│   └── Home.module.css
├── components/      # Reusable components
├── lib/             # Utility functions
├── next.config.js   # Next.js configuration
└── package.json

File-based Routing

Next.js uses the file system for routing. Pages are associated with routes based on their file names.

🗂️ Routing Examples

File Structure to Routes
pages/
├── index.js          → /
├── about.js          → /about
├── contact.js        → /contact
├── blog/
│   ├── index.js      → /blog
│   ├── [slug].js     → /blog/my-post
│   └── [year]/
│       └── [month].js → /blog/2024/january
├── products/
│   ├── [id].js       → /products/123
│   └── [...slug].js  → /products/category/subcategory
└── 404.js            → Custom 404 page

🔗 Dynamic Routes

Dynamic Route Example
// pages/blog/[slug].js
import { useRouter } from 'next/router'

export default function BlogPost() {
  const router = useRouter()
  const { slug } = router.query

  return (
    <div>
      <h1>Blog Post: {slug}</h1>
    </div>
  )
}

// pages/products/[...params].js - Catch-all routes
export default function Product() {
  const router = useRouter()
  const { params } = router.query
  // params could be ['category', 'subcategory', 'item']

  return (
    <div>
      <h1>Product: {params?.join(' / ')}</h1>
    </div>
  )
}

Rendering Methods

Next.js supports multiple rendering methods, allowing you to choose the best approach for each page.

📊 Static Site Generation (SSG)

Pages are generated at build time and served as static files.

SSG Example
// pages/posts/index.js
export default function Posts({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </div>
      ))}
    </div>
  )
}

// This function runs at build time
export async function getStaticProps() {
  const posts = await fetchPosts()
  
  return {
    props: {
      posts,
    },
    // Regenerate page every 60 seconds
    revalidate: 60,
  }
}

🖥️ Server-Side Rendering (SSR)

Pages are generated on each request.

SSR Example
// pages/profile.js
export default function Profile({ user }) {
  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <p>Last login: {user.lastLogin}</p>
    </div>
  )
}

// This function runs on every request
export async function getServerSideProps(context) {
  const { req } = context
  const user = await getUserFromRequest(req)
  
  return {
    props: {
      user,
    },
  }
}

🎯 Incremental Static Regeneration (ISR)

Combine benefits of static and dynamic rendering.

ISR Example
export async function getStaticProps() {
  const posts = await fetchPosts()
  
  return {
    props: {
      posts,
    },
    // Regenerate at most once per hour
    revalidate: 3600,
  }
}

export async function getStaticPaths() {
  const paths = await getPostPaths()
  
  return {
    paths,
    // Enable ISR for new pages
    fallback: 'blocking',
  }
}

📱 Client-Side Rendering (CSR)

Standard React behavior - render on the client.

CSR Example
import { useState, useEffect } from 'react'

export default function Dashboard() {
  const [data, setData] = useState(null)
  
  useEffect(() => {
    fetch('/api/dashboard-data')
      .then(res => res.json())
      .then(setData)
  }, [])
  
  if (!data) return <div>Loading...</div>
  
  return <div>{/* Dashboard content */}</div>
}

API Routes

Next.js allows you to create API endpoints as serverless functions, enabling full-stack development within a single project.

🔧 Creating API Routes

API Route Examples
// pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js API!' })
}

// pages/api/users/[id].js - Dynamic API route
export default async function handler(req, res) {
  const { id } = req.query
  const { method } = req

  switch (method) {
    case 'GET':
      const user = await getUserById(id)
      res.status(200).json(user)
      break
      
    case 'PUT':
      const updatedUser = await updateUser(id, req.body)
      res.status(200).json(updatedUser)
      break
      
    case 'DELETE':
      await deleteUser(id)
      res.status(204).end()
      break
      
    default:
      res.setHeader('Allow', ['GET', 'PUT', 'DELETE'])
      res.status(405).end(`Method ${method} Not Allowed`)
  }
}

// pages/api/posts.js - Handle form submissions
export default async function handler(req, res) {
  if (req.method === 'POST') {
    try {
      const { title, content } = req.body
      const post = await createPost({ title, content })
      res.status(201).json(post)
    } catch (error) {
      res.status(500).json({ error: 'Failed to create post' })
    }
  } else {
    res.status(405).json({ error: 'Method not allowed' })
  }
}

Built-in Optimizations

🖼️ Image Optimization

Next.js Image
import Image from 'next/image'

export default function Profile() {
  return (
    <div>
      <Image
        src="/profile.jpg"
        alt="Profile picture"
        width={300}
        height={300}
        priority // Load immediately
        placeholder="blur" // Show blur while loading
      />
    </div>
  )
}

📝 Head Management

SEO with Head
import Head from 'next/head'

export default function Post({ post }) {
  return (
    <>
      <Head>
        <title>{post.title} | My Blog</title>
        <meta name="description" content={post.excerpt} />
        <meta property="og:title" content={post.title} />
        <meta property="og:description" content={post.excerpt} />
        <meta property="og:image" content={post.image} />
      </Head>
      
      <article>
        <h1>{post.title}</h1>
        <div>{post.content}</div>
      </article>
    </>
  )
}

🎨 CSS Support

CSS Modules
// styles/Button.module.css
.button {
  background: blue;
  color: white;
  border: none;
  padding: 10px 20px;
}

.primary {
  background: green;
}

// components/Button.js
import styles from '../styles/Button.module.css'

export default function Button({ children, primary }) {
  const className = primary 
    ? `${styles.button} ${styles.primary}`
    : styles.button
    
  return (
    <button className={className}>
      {children}
    </button>
  )
}

📦 Automatic Code Splitting

Dynamic Imports
import { useState } from 'react'
import dynamic from 'next/dynamic'

// Load component only when needed
const DynamicChart = dynamic(
  () => import('../components/Chart'),
  { 
    loading: () => <p>Loading chart...</p>,
    ssr: false // Don't render on server
  }
)

export default function Dashboard() {
  const [showChart, setShowChart] = useState(false)
  
  return (
    <div>
      <button onClick={() => setShowChart(true)}>
        Show Chart
      </button>
      
      {showChart && <DynamicChart />}
    </div>
  )
}

When to Use Next.js

✅ Great for Next.js
  • Marketing websites - Need SEO and fast loading
  • E-commerce - Product pages need to be indexed
  • Blogs & Documentation - Static content with good SEO
  • Corporate websites - Professional sites with content
  • Full-stack apps - Need both frontend and backend
  • Portfolios - Personal or business showcases
❌ Consider React instead
  • Internal dashboards - Private apps don't need SEO
  • Admin panels - Behind authentication anyway
  • Desktop-like apps - Complex interactions, less content
  • Real-time apps - Chat, games, collaborative tools
  • Simple SPAs - When SSR/SSG adds no value
  • Mobile apps - React Native might be better

What's Next?

Next.js is a powerful framework that can handle everything from static sites to full-stack applications. It's an excellent choice when you need SEO, performance, and developer experience.

🚀 Continue Learning

  1. Popular Libraries - UI libraries and ecosystem tools
  2. Transition Guide - Complete migration strategies
  3. Learning Path - Structured approach to mastering React
  4. Resources - External learning materials and documentation