TechSetupGuides
Intermediatenextjsreacttypescriptvercelserver-componentsapp-routerssrssgfull-stackweb-framework

Next.js - The React Framework for Production

The React Framework for building full-stack web applications with server components, app router, and optimized production deployments.

  1. Step 1

    Overview

    Next.js is a React framework for building full-stack web applications. You use React Components to build user interfaces, and Next.js for additional features and optimizations. It automatically configures bundlers, compilers, and provides file-system based routing, server-side rendering (SSR), static site generation (SSG), and React Server Components out of the box.

  2. Step 2

    Technology Stack

    Next.js is built with the following technologies:

    Language: TypeScript/JavaScript
    License: MIT
    Stars: ~124,000
    Owner: vercel
    Repo: https://github.com/vercel/next.js
    Website: https://nextjs.org
    
    Core Dependencies:
    - React (>=19) - UI library
    - Node.js (>=20.9) - Runtime environment
    - Turbopack - Next-generation bundler (default in Next.js 15)
    - Babel/SWC - JavaScript compiler
    
    Key Features:
    - App Router with file-system based routing
    - React Server Components (RSC)
    - Server Actions for mutations
    - Automatic code splitting
    - Built-in CSS and Sass support
    - Image optimization
    - Font optimization
    - TypeScript support
    - API Routes & Route Handlers
    - Middleware for request interception
    - Incremental Static Regeneration (ISR)
    - Static Site Generation (SSG)
    - Server-Side Rendering (SSR)
    - Edge Runtime support
    - Streaming and Suspense
    - Turbopack for fast HMR
  3. Step 3

    Installation

    Create a new Next.js app using create-next-app, which sets up everything automatically.

    # Interactive setup
    npx create-next-app@latest
    
    # With specific options
    npx create-next-app@latest my-app --typescript --tailwind --app --turbopack
    
    # Using other package managers
    pnpm create next-app@latest
    yarn create next-app
    bun create next-app
    
    # Navigate to project
    cd my-app
    
    # Start development server
    npm run dev
  4. Step 4

    Project Structure

    Next.js 15 uses the App Router by default. Here's the standard project structure:

    my-app/
    ├── app/                    # App Router (replaces pages/)
    │   ├── layout.tsx          # Root layout (required)
    │   ├── page.tsx            # Homepage route
    │   ├── globals.css         # Global styles
    │   ├── about/
    │   │   └── page.tsx        # /about route
    │   ├── blog/
    │   │   ├── page.tsx        # /blog route
    │   │   └── [slug]/
    │   │       └── page.tsx    # /blog/[slug] dynamic route
    │   └── api/
    │       └── route.ts        # API route handler
    ├── components/             # React components
    ├── lib/                    # Utility functions
    ├── public/                 # Static assets
    ├── next.config.ts          # Next.js configuration
    ├── tsconfig.json          # TypeScript config
    └── package.json           # Dependencies
  5. Step 5

    Basic Configuration

    Configure Next.js through next.config.ts (or next.config.js).

    // next.config.ts
    import type { NextConfig } from 'next'
    
    const nextConfig: NextConfig = {
      // Enable React Compiler (experimental)
      experimental: {
        reactCompiler: true,
      },
      // Image optimization domains
      images: {
        remotePatterns: [
          {
            protocol: 'https',
            hostname: 'example.com',
          },
        ],
      },
      // Redirects
      async redirects() {
        return [
          {
            source: '/old-page',
            destination: '/new-page',
            permanent: true,
          },
        ]
      },
    }
    
    export default nextConfig
  6. Step 6

    Creating Pages (App Router)

    In the App Router, each folder in app/ represents a route segment. Use page.tsx to make a route publicly accessible.

    // app/page.tsx (Homepage at /)
    export default function HomePage() {
      return (
        <main>
          <h1>Welcome to Next.js</h1>
          <p>The React Framework for Production</p>
        </main>
      )
    }
    
    // app/about/page.tsx (Route at /about)
    export default function AboutPage() {
      return <h1>About Us</h1>
    }
    
    // app/blog/[slug]/page.tsx (Dynamic route at /blog/[slug])
    type Props = {
      params: Promise<{ slug: string }>
    }
    
    export default async function BlogPost({ params }: Props) {
      const { slug } = await params
      return <h1>Blog Post: {slug}</h1>
    }
  7. Step 7

    Layouts

    Layouts wrap multiple pages and persist across navigation. The root layout (app/layout.tsx) is required.

    // app/layout.tsx (Root layout - required)
    import './globals.css'
    
    export const metadata = {
      title: 'My Next.js App',
      description: 'Built with Next.js 15',
    }
    
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode
    }) {
      return (
        <html lang="en">
          <body>
            <nav>
              <a href="/">Home</a>
              <a href="/about">About</a>
            </nav>
            {children}
          </body>
        </html>
      )
    }
    
    // app/blog/layout.tsx (Nested layout for /blog/*)
    export default function BlogLayout({
      children,
    }: {
      children: React.ReactNode
    }) {
      return (
        <div>
          <aside>Blog Sidebar</aside>
          <main>{children}</main>
        </div>
      )
    }
  8. Step 8

    React Server Components

    By default, all components in the App Router are React Server Components. They run only on the server, can access databases directly, and don't ship JavaScript to the client.

    // app/posts/page.tsx (Server Component - default)
    import { db } from '@/lib/db'
    
    // Async server component
    export default async function PostsPage() {
      // Fetch data directly on the server
      const posts = await db.query('SELECT * FROM posts')
      
      return (
        <div>
          <h1>Posts</h1>
          <ul>
            {posts.map(post => (
              <li key={post.id}>{post.title}</li>
            ))}
          </ul>
        </div>
      )
    }
    
    // No useEffect, no loading states - data is ready server-side
  9. Step 9

    Client Components

    Use 'use client' directive for components that need interactivity, hooks, or browser APIs.

    // components/Counter.tsx (Client Component)
    'use client'
    
    import { useState } from 'react'
    
    export default function Counter() {
      const [count, setCount] = useState(0)
      
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>
            Increment
          </button>
        </div>
      )
    }
    
    // Use cases for 'use client':
    // - useState, useEffect, other React hooks
    // - Event handlers (onClick, onChange, etc.)
    // - Browser APIs (window, document, localStorage)
    // - Custom hooks that use the above
  10. Step 10

    Data Fetching

    Server Components can fetch data using async/await. Next.js extends the native fetch API with automatic caching.

    // Server Component data fetching
    export default async function Page() {
      // Cached by default (revalidate every hour)
      const data = await fetch('https://api.example.com/data', {
        next: { revalidate: 3600 }
      })
      const json = await data.json()
      
      return <div>{json.title}</div>
    }
    
    // Force no cache (always fresh)
    const dynamicData = await fetch('https://api.example.com/live', {
      cache: 'no-store'
    })
    
    // Static (cached indefinitely)
    const staticData = await fetch('https://api.example.com/static', {
      cache: 'force-cache'
    })
    
    // Direct database access (common pattern)
    import { db } from '@/lib/db'
    
    const posts = await db.post.findMany()
  11. Step 11

    Route Handlers (API Routes)

    Create API endpoints using route.ts files with HTTP method exports.

    // app/api/posts/route.ts
    import { NextRequest, NextResponse } from 'next/server'
    
    // GET /api/posts
    export async function GET(request: NextRequest) {
      const posts = await db.post.findMany()
      return NextResponse.json(posts)
    }
    
    // POST /api/posts
    export async function POST(request: NextRequest) {
      const body = await request.json()
      const post = await db.post.create({ data: body })
      return NextResponse.json(post, { status: 201 })
    }
    
    // app/api/posts/[id]/route.ts (Dynamic route)
    type Params = { params: Promise<{ id: string }> }
    
    export async function GET(
      request: NextRequest,
      { params }: Params
    ) {
      const { id } = await params
      const post = await db.post.findUnique({ where: { id } })
      return NextResponse.json(post)
    }
  12. Step 12

    Server Actions

    Server Actions are asynchronous functions that run on the server. Use them for mutations, form submissions, and data updates without creating API routes.

    // app/actions.ts
    'use server'
    
    import { revalidatePath } from 'next/cache'
    
    export async function createPost(formData: FormData) {
      const title = formData.get('title') as string
      const content = formData.get('content') as string
      
      await db.post.create({
        data: { title, content }
      })
      
      // Revalidate the posts page to show new post
      revalidatePath('/posts')
    }
    
    // app/posts/new/page.tsx
    import { createPost } from '@/app/actions'
    
    export default function NewPost() {
      return (
        <form action={createPost}>
          <input name="title" placeholder="Title" required />
          <textarea name="content" placeholder="Content" required />
          <button type="submit">Create Post</button>
        </form>
      )
    }
  13. Step 13

    Navigation and Linking

    Use the Link component for client-side navigation with automatic prefetching.

    // components/Navigation.tsx
    import Link from 'next/link'
    
    export default function Navigation() {
      return (
        <nav>
          <Link href="/">Home</Link>
          <Link href="/about">About</Link>
          <Link href="/blog">Blog</Link>
        </nav>
      )
    }
    
    // Dynamic links
    <Link href={`/blog/${post.slug}`}>{post.title}</Link>
    
    // Programmatic navigation (Client Component)
    'use client'
    import { useRouter } from 'next/navigation'
    
    export default function Button() {
      const router = useRouter()
      
      return (
        <button onClick={() => router.push('/about')}>
          Go to About
        </button>
      )
    }
  14. Step 14

    Image Optimization

    The Next.js Image component automatically optimizes images with lazy loading, responsive sizes, and modern formats.

    // components/ProductImage.tsx
    import Image from 'next/image'
    
    export default function ProductImage() {
      return (
        <div>
          {/* Local image (in public/ folder) */}
          <Image
            src="/hero.jpg"
            alt="Hero image"
            width={800}
            height={600}
            priority  // Disable lazy loading for above-fold images
          />
          
          {/* Remote image */}
          <Image
            src="https://example.com/image.jpg"
            alt="Remote image"
            width={400}
            height={300}
            placeholder="blur"  // Show blur while loading
            blurDataURL="data:image/..."  // Base64 blur image
          />
          
          {/* Fill container (requires parent with position: relative) */}
          <div style={{ position: 'relative', height: '300px' }}>
            <Image
              src="/background.jpg"
              alt="Background"
              fill
              style={{ objectFit: 'cover' }}
            />
          </div>
        </div>
      )
    }
  15. Step 15

    Metadata and SEO

    Define metadata for each page using the metadata export or generateMetadata function.

    // app/page.tsx (Static metadata)
    import type { Metadata } from 'next'
    
    export const metadata: Metadata = {
      title: 'Home - My App',
      description: 'Welcome to my Next.js application',
      openGraph: {
        title: 'Home - My App',
        description: 'Welcome to my Next.js application',
        images: ['/og-image.jpg'],
      },
    }
    
    // app/blog/[slug]/page.tsx (Dynamic metadata)
    type Props = {
      params: Promise<{ slug: string }>
    }
    
    export async function generateMetadata({ params }: Props): Promise<Metadata> {
      const { slug } = await params
      const post = await db.post.findUnique({ where: { slug } })
      
      return {
        title: post.title,
        description: post.excerpt,
        openGraph: {
          title: post.title,
          description: post.excerpt,
          images: [post.coverImage],
        },
      }
    }
  16. Step 16

    Loading States

    Create loading.tsx files to show loading UI while page content streams in.

    // app/blog/loading.tsx
    export default function Loading() {
      return (
        <div>
          <p>Loading blog posts...</p>
          <div className="spinner" />
        </div>
      )
    }
    
    // app/blog/page.tsx (This page triggers the loading state)
    export default async function BlogPage() {
      // Simulating slow data fetch
      await new Promise(resolve => setTimeout(resolve, 2000))
      const posts = await db.post.findMany()
      
      return (
        <div>
          {posts.map(post => (
            <article key={post.id}>{post.title}</article>
          ))}
        </div>
      )
    }
  17. Step 17

    Error Handling

    Create error.tsx files to handle errors and provide fallback UI.

    // app/blog/error.tsx
    'use client' // Error components must be Client Components
    
    export default function Error({
      error,
      reset,
    }: {
      error: Error & { digest?: string }
      reset: () => void
    }) {
      return (
        <div>
          <h2>Something went wrong!</h2>
          <p>{error.message}</p>
          <button onClick={() => reset()}>
            Try again
          </button>
        </div>
      )
    }
    
    // app/not-found.tsx (Custom 404 page)
    export default function NotFound() {
      return (
        <div>
          <h1>404 - Page Not Found</h1>
          <p>The page you're looking for doesn't exist.</p>
        </div>
      )
    }
  18. Step 18

    Middleware

    Middleware runs before requests are completed. Use it for authentication, redirects, and request modifications.

    // middleware.ts (in project root)
    import { NextResponse } from 'next/server'
    import type { NextRequest } from 'next/server'
    
    export function middleware(request: NextRequest) {
      // Check authentication
      const token = request.cookies.get('token')
      
      if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
        return NextResponse.redirect(new URL('/login', request.url))
      }
      
      // Add custom header
      const response = NextResponse.next()
      response.headers.set('x-custom-header', 'value')
      return response
    }
    
    // Run middleware on specific paths
    export const config = {
      matcher: [
        '/dashboard/:path*',
        '/api/:path*',
      ],
    }
  19. Step 19

    Environment Variables

    Use environment variables for configuration. Prefix with NEXT_PUBLIC_ to expose to the browser.

    # .env.local (gitignored - for local development)
    DATABASE_URL=postgresql://localhost:5432/mydb
    API_SECRET=your-secret-key
    
    # Public variables (accessible in browser)
    NEXT_PUBLIC_API_URL=https://api.example.com
    NEXT_PUBLIC_SITE_NAME=My App
    
    # .env.production (for production builds)
    DATABASE_URL=postgresql://prod-server:5432/mydb
    
    # Usage in code:
    // Server-side (Server Components, API Routes)
    const dbUrl = process.env.DATABASE_URL
    const secret = process.env.API_SECRET
    
    // Client-side (any component)
    const apiUrl = process.env.NEXT_PUBLIC_API_URL
  20. Step 20

    Static Site Generation (SSG)

    Generate static pages at build time using generateStaticParams for dynamic routes.

    // app/blog/[slug]/page.tsx
    
    // Generate static pages for all blog posts at build time
    export async function generateStaticParams() {
      const posts = await db.post.findMany()
      
      return posts.map(post => ({
        slug: post.slug,
      }))
    }
    
    // This page will be pre-rendered at build time
    export default async function BlogPost({
      params,
    }: {
      params: Promise<{ slug: string }>
    }) {
      const { slug } = await params
      const post = await db.post.findUnique({ where: { slug } })
      
      return (
        <article>
          <h1>{post.title}</h1>
          <div>{post.content}</div>
        </article>
      )
    }
  21. Step 21

    Incremental Static Regeneration (ISR)

    Revalidate static pages at runtime without rebuilding the entire site.

    // app/blog/[slug]/page.tsx
    
    // Revalidate this page every 60 seconds
    export const revalidate = 60
    
    export default async function BlogPost({
      params,
    }: {
      params: Promise<{ slug: string }>
    }) {
      const { slug } = await params
      const post = await db.post.findUnique({ where: { slug } })
      
      return <article>{post.title}</article>
    }
    
    // On-demand revalidation using Server Actions
    import { revalidatePath, revalidateTag } from 'next/cache'
    
    // Revalidate specific path
    await revalidatePath('/blog/my-post')
    
    // Revalidate by cache tag
    await revalidateTag('posts')
    
    // Use tags with fetch
    fetch('https://api.example.com/posts', {
      next: { tags: ['posts'] }
    })
  22. Step 22

    Deployment to Vercel

    Vercel is the recommended platform for Next.js deployment with zero configuration.

    # Install Vercel CLI
    npm i -g vercel
    
    # Login to Vercel
    vercel login
    
    # Deploy to preview
    vercel
    
    # Deploy to production
    vercel --prod
    
    # Or connect Git repository:
    # 1. Push code to GitHub/GitLab/Bitbucket
    # 2. Import project at vercel.com/new
    # 3. Auto-deploys on git push
    
    # Environment variables in Vercel:
    # Dashboard → Project → Settings → Environment Variables
    # - Add DATABASE_URL, API_SECRET, etc.
    # - Select Environment: Production, Preview, Development
  23. Step 23

    Build Commands

    Common commands for building and running Next.js applications.

    # Development server (with Turbopack)
    npm run dev
    # or
    npm run dev --turbopack
    
    # Production build
    npm run build
    
    # Start production server
    npm run start
    
    # Type checking
    npm run build  # TypeScript errors will fail the build
    
    # Linting
    npm run lint
    
    # Analyze bundle size
    npm run build -- --analyze
  24. Step 24

    Performance Optimization

    Best practices for optimizing Next.js applications.

    1. Use Server Components by default - Zero client-side JavaScript
    2. Implement proper caching strategies - Use revalidate and cache tags
    3. Use next/image for all images - Automatic optimization and lazy loading
    4. Use next/font for font optimization - No layout shift
    5. Leverage static generation when possible - Pre-render at build time
    6. Implement code splitting - Automatic with dynamic imports
    7. Enable compression - Automatic in production
    8. Use Edge Runtime for fast response times
    9. Implement proper loading states - Use loading.tsx and Suspense
    10. Monitor Core Web Vitals - Use Vercel Analytics or Google Lighthouse
    11. Minimize client-side JavaScript - Only use 'use client' when necessary
    12. Use streaming for long data fetches - Suspense boundaries
    13. Optimize third-party scripts - Use next/script component
    14. Enable Turbopack for faster builds - Default in Next.js 15
  25. Step 25

    Key Features

    Next.js capabilities for modern web development:

    1. File-System Routing: Automatic routing from folder structure
    2. Server Components: React components that run only on server
    3. App Router: Modern routing with nested layouts
    4. Server Actions: Server-side mutations without API routes
    5. Streaming: Progressive rendering with Suspense
    6. Image Optimization: Automatic image optimization and lazy loading
    7. Font Optimization: Automatic font optimization with next/font
    8. Code Splitting: Automatic per-route code splitting
    9. TypeScript: Built-in TypeScript support
    10. Fast Refresh: Instant feedback on edits
    11. Turbopack: Fast bundler with HMR
    12. SSR/SSG/ISR: Multiple rendering strategies
    13. API Routes: Backend API endpoints
    14. Middleware: Request interception and modification
    15. Edge Runtime: Deploy to edge for low latency
  26. Step 26

    Use Cases

    Next.js is ideal for:

    1. Marketing Websites: SEO-optimized static/hybrid sites
    2. E-commerce Platforms: Fast, SEO-friendly product pages
    3. SaaS Applications: Full-stack applications with auth
    4. Blogs and Content Sites: Static generation with dynamic updates
    5. Dashboards: Server-rendered data visualizations
    6. Documentation Sites: Static with search and navigation
    7. Landing Pages: Optimized for performance and SEO
    8. Web Applications: Full-stack React applications
    9. Mobile Web Apps: Progressive Web Apps (PWA)
    10. API-First Applications: Backend and frontend in one
    11. Multi-tenant Applications: Dynamic routing and data
    12. Real-time Applications: With WebSocket support
    13. JAMstack Sites: Static generation with API integration
    14. Headless CMS Frontends: Integrate with any CMS
  27. Step 27

    Migration from Pages Router

    Migrating from Next.js Pages Router to App Router:

    Step 1: Create app/ directory alongside pages/
    Step 2: Move pages/ routes to app/ incrementally
    Step 3: Convert getServerSideProps → async Server Components
    Step 4: Convert getStaticProps → async Server Components with revalidate
    Step 5: Convert getStaticPaths → generateStaticParams
    Step 6: Convert _app.tsx → layout.tsx
    Step 7: Convert _document.tsx → layout.tsx (html/body)
    Step 8: Convert API routes → route.ts with HTTP method exports
    Step 9: Add 'use client' to interactive components
    Step 10: Update imports (next/router → next/navigation)
    Step 11: Test thoroughly, then remove pages/ directory
    
    Both routers can coexist during migration.
  28. Step 28

    Resources

    Additional resources for learning Next.js:

    Documentation: https://nextjs.org/docs
    GitHub: https://github.com/vercel/next.js
    Learning: https://nextjs.org/learn
    Examples: https://github.com/vercel/next.js/tree/canary/examples
    Blog: https://nextjs.org/blog
    Showcase: https://nextjs.org/showcase
    Discord: https://nextjs.org/discord
    Vercel: https://vercel.com
    React Docs: https://react.dev
    App Router Docs: https://nextjs.org/docs/app
  29. Step 29

    File Locations

    Key files and directories in a Next.js project:

    app/                - App Router pages and routes
    app/layout.tsx      - Root layout (required)
    app/page.tsx        - Homepage
    app/loading.tsx     - Loading UI
    app/error.tsx       - Error UI
    app/not-found.tsx   - 404 page
    public/             - Static assets
    components/         - React components
    lib/                - Utility functions
    middleware.ts       - Middleware (root level)
    next.config.ts      - Next.js configuration
    tsconfig.json       - TypeScript config
    .env.local          - Local environment variables
    .next/              - Build output (gitignored)
    node_modules/       - Dependencies

Feature requests

Sign in to suggest features or vote on existing ones.

No feature requests yet.

Discussion

0 people marked this as worked·Sign in to mark your own.

Sign in to join the discussion.

No comments yet.