Introduction to Next.js: The React Framework for Production

Introduction to Next.js

Next.js has become the go-to framework for building production-ready React applications. Created by Vercel, it solves many of the challenges developers face when building modern web applications, offering features like server-side rendering, static site generation, and automatic code splitting out of the box.

What is Next.js?

Next.js is a React framework that provides a complete solution for building web applications. While React is a library focused on building user interfaces, Next.js extends it with a robust set of features for routing, data fetching, optimization, and deployment.

Think of it this way: React gives you the tools to build components, while Next.js gives you the complete infrastructure to build and deploy full-stack applications.

Why Choose Next.js?

Built-in Routing

Unlike traditional React apps that require libraries like React Router, Next.js uses file-system based routing. Simply create files in your app directory, and they automatically become routes.

app/
├── page.tsx           # Home page (/)
├── about/
│   └── page.tsx       # About page (/about)
├── blog/
│   ├── page.tsx       # Blog listing (/blog)
│   └── [slug]/
│       └── page.tsx   # Dynamic blog post (/blog/my-post)
└── dashboard/
    └── page.tsx       # Dashboard (/dashboard)

Multiple Rendering Strategies

Next.js supports various rendering methods, letting you choose the best approach for each page:

Server-Side Rendering (SSR): Pages are rendered on the server for each request.

Static Site Generation (SSG): Pages are pre-rendered at build time.

Incremental Static Regeneration (ISR): Static pages can be updated after deployment without rebuilding.

Client-Side Rendering (CSR): Traditional React rendering in the browser.

Performance Optimization

Next.js automatically optimizes your application:

  • Automatic Code Splitting: Only load JavaScript needed for the current page
  • Image Optimization: Built-in <Image> component with lazy loading and automatic resizing
  • Font Optimization: Automatic font loading with zero layout shift
  • Script Optimization: Control when third-party scripts load

Core Features

1. App Router (Next.js 13+)

The modern App Router uses React Server Components by default:

// app/page.tsx
export default function HomePage() {
  return (
    <div>
      <h1>Welcome to Next.js</h1>
      <p>This is a Server Component by default!</p>
    </div>
  )
}

2. Server Components vs Client Components

Server Components (default):

  • Render on the server
  • Can directly access databases and APIs
  • Don't increase client bundle size
  • Great for static content
// app/posts/page.tsx
async function getPosts() {
  const res = await fetch('https://api.example.com/posts')
  return res.json()
}

export default async function PostsPage() {
  const posts = await getPosts()
  
  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  )
}

Client Components:

  • Add 'use client' directive
  • Can use hooks and browser APIs
  • Interactive elements
// components/Counter.tsx
'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>
  )
}

3. Dynamic Routes

Create dynamic routes with brackets:

// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
  return (
    <article>
      <h1>Blog Post: {params.slug}</h1>
    </article>
  )
}

// Generates static pages for these slugs at build time
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(res => res.json())
  
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

4. Layouts

Shared layouts wrap multiple pages:

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <nav>
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/blog">Blog</a>
        </nav>
        <main>{children}</main>
        <footer>© 2025 My App</footer>
      </body>
    </html>
  )
}

Nested layouts for specific sections:

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex">
      <aside className="w-64 bg-gray-100">
        <nav>Sidebar navigation</nav>
      </aside>
      <div className="flex-1">{children}</div>
    </div>
  )
}

5. Data Fetching

Next.js makes data fetching simple and powerful:

// Server Component - fetch directly
async function getUser(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}`, {
    next: { revalidate: 3600 } // Revalidate every hour
  })
  return res.json()
}

export default async function UserProfile({ params }: { params: { id: string } }) {
  const user = await getUser(params.id)
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
    </div>
  )
}

6. API Routes

Build backend APIs within your Next.js app:

// app/api/hello/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json({ message: 'Hello from Next.js API!' })
}

export async function POST(request: Request) {
  const body = await request.json()
  // Process the data
  return NextResponse.json({ success: true, data: body })
}

7. Metadata and SEO

Easy SEO optimization with metadata:

// app/blog/[slug]/page.tsx
import { Metadata } from 'next'

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug)
  
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
    },
  }
}

Image Optimization

The Next.js Image component automatically optimizes images:

import Image from 'next/image'

export default function ProfilePage() {
  return (
    <Image
      src="/profile.jpg"
      alt="Profile picture"
      width={500}
      height={500}
      priority // Load immediately for above-the-fold images
      placeholder="blur" // Show blur while loading
    />
  )
}

Benefits:

  • Automatic responsive images
  • Lazy loading by default
  • Modern formats (WebP, AVIF)
  • Prevents Cumulative Layout Shift (CLS)

Styling Options

Next.js supports multiple styling approaches:

CSS Modules

// components/Button.module.css
.button {
  background-color: blue;
  padding: 1rem 2rem;
  border-radius: 0.5rem;
}

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

export default function Button() {
  return <button className={styles.button}>Click me</button>
}

Tailwind CSS

export default function Card() {
  return (
    <div className="bg-white rounded-lg shadow-lg p-6">
      <h2 className="text-2xl font-bold mb-4">Card Title</h2>
      <p className="text-gray-600">Card content goes here</p>
    </div>
  )
}

CSS-in-JS

Next.js supports libraries like styled-components and Emotion.

Getting Started

Create a new Next.js app:

npx create-next-app@latest my-app

You'll be prompted to choose:

  • TypeScript or JavaScript
  • ESLint
  • Tailwind CSS
  • App Router or Pages Router
  • Import aliases

Start the development server:

cd my-app
npm run dev

Your app runs at http://localhost:3000!

Project Structure

A typical Next.js 15 project:

my-app/
├── app/
│   ├── layout.tsx      # Root layout
│   ├── page.tsx        # Home page
│   ├── globals.css     # Global styles
│   └── api/            # API routes
├── components/         # Reusable components
├── public/             # Static assets
├── package.json
└── next.config.js      # Next.js configuration

Real-World Example: Blog

Here's a simple blog structure:

// app/blog/page.tsx
async function getPosts() {
  const res = await fetch('https://api.example.com/posts')
  return res.json()
}

export default async function BlogPage() {
  const posts = await getPosts()
  
  return (
    <div className="max-w-4xl mx-auto px-4 py-8">
      <h1 className="text-4xl font-bold mb-8">Blog</h1>
      <div className="space-y-8">
        {posts.map((post) => (
          <article key={post.id} className="border-b pb-8">
            <h2 className="text-2xl font-bold mb-2">
              <a href={`/blog/${post.slug}`}>{post.title}</a>
            </h2>
            <p className="text-gray-600 mb-2">{post.date}</p>
            <p className="text-gray-800">{post.excerpt}</p>
          </article>
        ))}
      </div>
    </div>
  )
}
// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`)
  return res.json()
}

export default async function BlogPostPage({ params }) {
  const post = await getPost(params.slug)
  
  return (
    <article className="max-w-3xl mx-auto px-4 py-8 prose-white dark:prose-invert">
      <h1 className="text-4xl font-bold mb-4">{post.title}</h1>
      <p className="text-gray-600 mb-8">{post.date}</p>
      <div className="prose lg:prose-xl">{post.content}</div>
    </article>
  )
}

Deployment

Deploy to Vercel (the creators of Next.js) with zero configuration:

npm install -g vercel
vercel

Or deploy to other platforms:

  • Netlify
  • AWS
  • Google Cloud
  • Self-hosted with Node.js

Best Practices

Use Server Components by default: Only add 'use client' when you need interactivity or browser APIs.

Optimize images: Always use the <Image> component for better performance.

Fetch data where you need it: Server Components can fetch data directly without prop drilling.

Use TypeScript: Next.js has excellent TypeScript support built-in.

Implement proper error handling: Use error.tsx and loading.tsx files for better UX.

Conclusion

Next.js has evolved from a simple React framework into a comprehensive solution for building modern web applications. Whether you're building a static blog, a dynamic e-commerce site, or a complex dashboard, Next.js provides the tools and optimizations you need to succeed.

The combination of Server Components, file-based routing, built-in optimizations, and excellent developer experience makes Next.js an excellent choice for your next React project.

Resources

Happy coding with Next.js! 🚀