Interested in our next book? Learn more about Building Large-scale JavaScript Web Apps with React

Overview of Next.js

Next.js, created by Vercel, is a full-stack React framework optimized for production. With recent updates, Next.js emphasizes the App Router architecture, React Server Components, and seamless full-stack capabilities. Let’s explore modern Next.js features and patterns.


Core Architecture

App Router, Routing & Code Organization

Next.js 13+ introduced the App Router as the recommended approach for building applications, bringing powerful routing and code organization capabilities:

app/
  layout.tsx         # Root layout (applies to all routes)
  page.tsx          # Home page (/)
  about/
    page.tsx        # About page (/about)
  blog/
    layout.tsx      # Blog layout
    page.tsx        # Blog list (/blog)
    [slug]/
      page.tsx      # Dynamic blog post (/blog/post-1)
    categories/
      [...slug]/    # Catch-all segments (/blog/categories/a/b/c)
        page.tsx
  (marketing)/      # Route groups
    page.tsx
  @modal/          # Parallel routes
    page.tsx
  not-found.tsx    # Custom 404 page
  error.tsx        # Error boundary
  loading.tsx      # Loading UI

Key routing features:

  • File-based routing: URLs mirror your file structure
  • Dynamic segments: [param] for dynamic routes
  • Catch-all routes: [...slug] for flexible paths
  • Route groups: (group) for logical organization
  • Parallel routes: @name for simultaneous views
  • Intercepting routes: (..)photo for modal-like UIs

Key features:

  • Server Components by default (zero client-side JS)
  • Nested layouts with partial rendering
  • Streaming for progressive content loading
  • Simplified data fetching with async/await
  • Built-in error handling with error.tsx
  • Loading states with loading.tsx
  • 404 pages with not-found.tsx

Code-Splitting & Bundle Optimization

Next.js automatically implements several code-splitting strategies:

  1. Route-based Splitting:

    • Each route is automatically code-split
    • Only the code needed for the current route is loaded
    • Prefetching of likely routes based on viewport
    • Automatic static optimization when possible
  2. Component-level Splitting:

// Dynamic imports for components
import dynamic from 'next/dynamic'

const DynamicChart = dynamic(() => import('@/components/Chart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false // Disable server-rendering
})

// Conditional imports
const AdminPanel = dynamic(() => 
  import('@/components/Admin').then(mod => mod.AdminPanel), {
  loading: () => <p>Loading admin panel...</p>
})
  1. Library-level Splitting:

    • Third-party modules are automatically split
    • Shared chunks for commonly used code
    • Module federation support for micro-frontends
  2. Image Optimization:

    • Automatic lazy loading of images
    • Viewport-based loading strategies
    • Format optimization (WebP/AVIF)

Hybrid Rendering

Next.js supports multiple rendering strategies in a single application:

  1. Static Site Generation (SSG): Pre-render at build time
  2. Incremental Static Regeneration (ISR): Update static content post-build
  3. Server-Side Rendering (SSR): Render on each request
  4. Client-Side Rendering (CSR): Traditional React hydration

Modern pattern:

// app/page.tsx
export default async function Home() {
  const data = await fetchData(); // Server-side fetch
  
  return (
    <main>
      <StaticContent data={data} />
      <ClientComponent />
    </main>
  )
}

Modern Data Fetching

App Router Data Patterns

FeatureApp Router Approach
Static PropsgenerateStaticParams()
Server-side PropsAsync Server Components
Client-side DataSWR/TanStack Query + use()

Example static generation:

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

Full-Stack Capabilities

Next.js 14 introduces Server Actions for secure backend operations:

// app/actions.ts
'use server'

export async function createPost(formData: FormData) {
  await db.post.create({
    data: { title: formData.get('title') }
  });
}

// app/page.tsx
import { createPost } from './actions';

export default function Page() {
  return (
    <form action={createPost}>
      <input name="title" />
      <button type="submit">Create</button>
    </form>
  )
}

Performance Optimization

Image Handling

Modern Image component usage:

import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero Image"
  width={1200}
  height={800}
  priority
  className="rounded-lg"
/>

Best practices:

  • Use priority for above-the-fold images
  • Configure remotePatterns in next.config.js
  • Prefer AVIF format with quality optimization

Font Optimization

Built-in font system:

import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export default function Layout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      {children}
    </html>
  )
}

Advanced Features

Middleware & Advanced Routing

Edge-ready middleware:

// middleware.ts
export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return validateUserSession(request);
  }
}

Dynamic route handlers:

// app/api/route.ts
export async function GET(request: Request) {
  return new Response(JSON.stringify({ data: 'Hello' }), {
    headers: { 'Content-Type': 'application/json' }
  });
}

Partial Prerendering (Experimental)

Next.js 14 introduces Partial Prerendering for dynamic static pages:

// app/dashboard/page.tsx
import { unstable_noStore as noStore } from 'next/cache';

export default function Page() {
  noStore(); // Opt-out of static rendering
  return <RealTimeDashboard />;
}

Getting Started

  1. Create new project:
npx create-next-app@latest
  1. Choose modern configuration:
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use App Router? … Yes
✔ Would you like to customize the default import alias? … No
  1. Development workflow:
npm run dev    # Local development
npm run build  # Production build
npm run start  # Start production server

Modern Next.js emphasizes:

  • App Router for file-based routing
  • React Server Components by default
  • Edge Runtime for optimal performance
  • TypeScript first-class support
  • Turbopack for faster development (beta)