Introduction to Next.js
What is Next.js?
Next.js is a React framework that enables server-side rendering and static site generation for React applications.
Key Features:
- Server-Side Rendering (SSR)
- Static Site Generation (SSG)
- File-based Routing
- API Routes
- Built-in CSS Support
- Automatic Code Splitting
Project Setup & Installation
Creating a New Next.js Project
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
npm run dev
Project Structure
my-nextjs-app/
├── app/ # App Router (Next.js 13+)
│ ├── globals.css
│ ├── layout.js
│ ├── page.js
│ └── favicon.ico
├── pages/ # Pages Router
├── public/ # Static assets
├── components/ # Reusable components
└── package.json
Routing System
Basic Routing
// app/page.js - Home page
export default function Home() {
return <h1>Welcome to Next.js!</h1>
}
// app/about/page.js - About page
export default function About() {
return <h1>About Us</h1>
}
Dynamic Routes
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
return <h1>Blog Post: {params.slug}</h1>
}
// app/products/[id]/page.js
export default function Product({ params }) {
return <h1>Product ID: {params.id}</h1>
}
Navigation with Link
'use client'
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href="/blog/post-1">Blog Post</Link>
</nav>
)
}
Rendering Strategies
Server-Side Rendering (SSR)
// Using getServerSideProps
export default function Page({ data }) {
return <div>{data}</div>
}
export async function getServerSideProps() {
const data = await fetchData()
return {
props: { data }
}
}
Static Site Generation (SSG)
// Using getStaticProps
export default function Page({ posts }) {
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
export async function getStaticProps() {
const posts = await getPosts()
return {
props: { posts }
}
}
Incremental Static Regeneration (ISR)
export async function getStaticProps() {
const data = await fetchData()
return {
props: { data },
revalidate: 60 // Revalidate every 60 seconds
}
}
Components (Server vs Client)
Server Components (Default)
// app/components/ServerComponent.js
async function ServerComponent() {
const data = await fetchData() // Direct data fetching
return (
<div>
<h1>Server Component</h1>
<p>Data: {data}</p>
</div>
)
}
Client Components
'use client'
import { useState, useEffect } from 'react'
export default function ClientComponent() {
const [count, setCount] = useState(0)
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData)
}, [])
return (
<div>
<h1>Client Component</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
)
}
Styling Methods
CSS Modules
/* components/Button.module.css */
.button {
padding: 10px 20px;
background: #0070f3;
color: white;
border: none;
border-radius: 5px;
}
// components/Button.js
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="max-w-sm rounded shadow-lg bg-white p-6">
<h2 className="text-xl font-bold">Card Title</h2>
<p className="text-gray-600">Card content</p>
</div>
)
}
Styled JSX
export default function StyledComponent() {
return (
<div>
<h1>Styled Component</h1>
<style jsx>{`
h1 {
color: #0070f3;
}
`}</style>
</div>
)
}
Data Fetching Methods
API Routes
// app/api/users/route.js
export async function GET() {
const users = await getUsers()
return Response.json(users)
}
export async function POST(request) {
const user = await request.json()
const newUser = await createUser(user)
return Response.json(newUser)
}
Client-side Data Fetching
'use client'
import { useState, useEffect } from 'react'
export default function UserList() {
const [users, setUsers] = useState([])
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setUsers)
}, [])
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
State Management with Redux Toolkit
Store Setup
// lib/store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './features/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
Slice Creation
// lib/features/counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
},
})
export const { increment, decrement } = counterSlice.actions
export default counterSlice.reducer
Using Redux in Components
'use client'
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement } from '../lib/features/counterSlice'
export default function Counter() {
const count = useSelector(state => state.counter.value)
const dispatch = useDispatch()
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
</div>
)
}
Deployment
Vercel Deployment
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel --prod
Environment Variables
// .env.local
DATABASE_URL="your-database-url"
NEXT_PUBLIC_API_URL="https://api.example.com"
// Using environment variables
const apiUrl = process.env.NEXT_PUBLIC_API_URL
Next.js Configuration
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ['example.com'],
},
async redirects() {
return [
{
source: '/old-path',
destination: '/new-path',
permanent: true,
},
]
},
}
module.exports = nextConfig
Performance Optimization
Image Optimization
import Image from 'next/image'
export default function OptimizedImage() {
return (
<Image
src="/hero.jpg"
alt="Hero image"
width={800}
height={600}
priority
placeholder="blur"
/>
)
}
Code Splitting
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <p>Loading...</p>,
ssr: false,
})
Font Optimization
// app/layout.js
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
)
}
SEO Optimization
// app/layout.js
export const metadata = {
title: 'My Next.js App',
description: 'A complete Next.js application',
keywords: 'nextjs, react, javascript',
openGraph: {
title: 'My Next.js App',
description: 'A complete Next.js application',
images: ['/og-image.jpg'],
},
}
Conclusion
Next.js provides a powerful framework for building React applications with server-side rendering, static site generation, and excellent developer experience. These Next.js notes cover the essential concepts and practical examples to help you build production-ready applications.
Key Takeaways:
- Next.js simplifies React development with built-in routing and rendering
- Choose between SSR, SSG, or ISR based on your needs
- Use Server Components for better performance
- Optimize images and code splitting for faster loading
- Deploy easily on Vercel or other platforms
For more detailed information, refer to the official Next.js documentation.





