Next.js Reference & Cheatsheet
Next.js Reference & Cheatsheet
Next.js is a React framework for production-ready web applications, providing file-based routing, multiple data-fetching strategies (SSR, SSG, ISR), API routes, and middleware — all built on top of React.
What You’ll Learn
- How Next.js file-based routing works in both Pages Router and App Router
- The difference between SSR, SSG, and ISR and when to use each
- How to create API routes and middleware for backend logic
- How to use Next.js built-in components like Image and Link
- How to handle authentication and redirects with middleware
Why Next.js Matters
Next.js powers some of the largest websites in the world, handling millions of requests per day. Companies choose Next.js because it solves real problems: slow page loads (with SSG/ISR), poor SEO (with SSR), and complex routing. The same patterns you learn here power tools like Durga Antivirus Pro dashboards that need both fast static pages and real-time security data, and Doda Browser extensions that fetch and display dynamic content efficiently.
flowchart LR
A[React Basics] --> B[Next.js Fundamentals]
B --> C[Pages Router]
B --> D[App Router]
C --> E[SSR / SSG / ISR]
D --> E
E --> F[API Routes & Middleware]
F --> G[Full-Stack Next.js Apps]
style B fill:#4a90d9,color:#fff
File-Based Routing — Pages Router (Classic)
Think of file-based routing like organizing files in a folder. Each file in the pages/ folder automatically becomes a URL route. This is the Pages Router, the original Next.js routing system.
pages/
├── index.js → /
├── about.js → /about
├── blog/
│ ├── index.js → /blog
│ └── [slug].js → /blog/:slug
├── api/
│ └── users.js → /api/users
└── 404.js → custom 404How it works: The file pages/blog/[slug].js creates a dynamic route. The [slug] part is a parameter — whatever the user types after /blog/ becomes available in your component. For example, visiting /blog/hello-world gives you slug = "hello-world".
File-Based Routing — App Router (Next.js 13+)
The App Router is the modern way to build Next.js apps. Instead of separate files for pages and layouts, you use a single app/ folder with special file names:
app/
├── page.js → /
├── layout.js → root layout
├── about/
│ └── page.js → /about
├── blog/
│ ├── page.js → /blog
│ └── [slug]/
│ └── page.js → /blog/:slug
└── api/
└── route.js → /apiWhy App Router? It supports server components by default, which means less JavaScript sent to the browser. Every component in app/ is a server component unless you add "use client" at the top. Server components can fetch data directly without useEffect or state management.
Data Fetching Methods
Next.js offers three main strategies for getting data into your pages. Choosing the right one depends on when you want the data to load.
getStaticProps — Static Site Generation (SSG)
Data is fetched at build time — once, when you deploy. The HTML page is pre-built and served instantly.
export async function getStaticProps() {
// This runs at build time, not on every request
const res = await fetch("https://api.example.com/posts");
const posts = await res.json();
return {
props: { posts },
revalidate: 60 // ISR: re-generate at most every 60 seconds
};
}Line by line: getStaticProps is an async function that Next.js calls during build. We fetch data from an API, parse it to JSON, and return it as props. The revalidate: 60 option enables Incremental Static Regeneration (ISR) — Next.js will re-fetch the data at most every 60 seconds, so your static page stays fresh.
Best for: Blog posts, marketing pages, documentation — content that doesn’t change frequently.
getServerSideProps — Server-Side Rendering (SSR)
Data is fetched on every request — the user waits while the server gets the data and renders the HTML.
export async function getServerSideProps(context) {
// context gives us access to params, query, req, res
const { params, req, query } = context;
const res = await fetch(`https://api.example.com/post/${params.slug}`);
const post = await res.json();
return { props: { post } };
}Line by line: context contains the request information. params.slug comes from the URL (the [slug] in the filename). We fetch fresh data using that slug, parse it, and return it. Unlike getStaticProps, this runs on every visit.
Best for: User dashboards, real-time data, pages that need authentication — content that changes often.
getStaticPaths
When using getStaticProps with dynamic routes, you also need getStaticPaths to tell Next.js which paths to pre-build:
export async function getStaticPaths() {
const res = await fetch("https://api.example.com/posts");
const posts = await res.json();
return {
paths: posts.map(p => ({ params: { slug: p.slug } })),
fallback: "blocking"
// "blocking": new paths are generated on first visit
// false: only pre-built paths work (404 for others)
// true: show fallback UI while generating
};
}Line by line: We fetch all posts, then map each one to a path object. fallback: "blocking" means if someone visits a path we didn’t pre-build, Next.js will generate it on-the-fly (the user waits briefly, but then it’s cached).
Server Components (App Router)
In the App Router, every component is a server component by default. This is a major shift from traditional React.
// app/page.js — Server Component by default
async function Page() {
// We can use async/await directly — no useEffect needed!
const res = await fetch("https://api.example.com/data");
const posts = await res.json();
return (
<div>
{posts.map(p => <Post key={p.id} {...p} />)}
</div>
);
}Why this matters: In traditional React, you’d use useEffect to fetch data, which means a loading state, extra JavaScript, and slower page loads. With server components, the fetching happens on the server, so the user gets fully-rendered HTML with no client-side loading spinners.
API Routes
Next.js lets you build backend endpoints directly in your app — no separate server needed.
// pages/api/users.js — or app/api/users/route.js in App Router
export default async function handler(req, res) {
// req.method tells us what HTTP verb was used (GET, POST, etc.)
if (req.method === "GET") {
const users = await db.findMany();
res.status(200).json(users);
} else if (req.method === "POST") {
const user = await db.create(req.body);
res.status(201).json(user);
} else {
res.status(405).json({ error: "Method not allowed" });
}
}Line by line: The handler function receives the HTTP request and response. We check the method, then respond accordingly. res.status(200).json(users) sends a JSON response with a 200 (OK) status. For POST, we create a new record and return 201 (Created).
Best for: Form submissions, authentication endpoints, webhook handlers — any server-side logic your frontend needs.
Middleware
Middleware runs before a request reaches your page. Think of it as a guard at the door — it checks who’s coming in and decides whether to let them through.
// middleware.js (lives in the root of your project)
export function middleware(request) {
// Read the token cookie — this is where authentication happens
const token = request.cookies.get("token");
// If no token and trying to access /admin, redirect to login
if (!token && request.nextUrl.pathname.startsWith("/admin")) {
return Response.redirect(new URL("/login", request.url));
}
// Otherwise, let the request through
}
export const config = {
matcher: ["/admin/:path*"] // Only run on /admin paths
};Line by line: The middleware checks for a cookie named “token”. If it’s missing and the user is visiting an admin page, we redirect to /login. The config.matcher tells Next.js which paths should trigger this middleware — this prevents it from running on every single request.
Built-in Components
Next.js provides optimized components that replace standard HTML elements:
import Image from "next/image";
import Link from "next/link";
import Head from "next/head";
// Image: automatic optimization, lazy loading, responsive
<Image
src="/hero.jpg"
alt="Hero banner"
width={1200}
height={600}
priority // Skip lazy loading — load immediately
/>
// Link: client-side navigation (no full page reload)
<Link href="/about">About Us</Link>
// Head: set page title and meta tags
<Head>
<title>My Next.js Page</title>
</Head>Why Image matters: A standard <img> tag loads images at full size, slowing down your page. Next.js Image component automatically serves responsive sizes, converts to WebP, and lazy-loads images below the fold. This is the kind of optimization that tools like DodaZIP apply to file compression — smaller output, same quality.
Common Mistakes
Forgetting
getStaticPathswith dynamic SSG routes: If you usegetStaticPropson a dynamic page like[slug].js, you MUST also exportgetStaticPaths. Without it, Next.js doesn’t know which pages to build.Using
getServerSidePropswhen SSG would work: Every SSR request hits your server and database. If your data changes once an hour, use ISR instead — your users (and your server) will thank you.Not handling
fallbackcorrectly: Settingfallback: falsemeans any unlisted path returns 404. For large sites with thousands of pages, usefallback: "blocking"so new pages are generated on demand.Putting
"use client"everywhere: Server components are faster because they send less JavaScript. Only add"use client"when you need interactivity (event handlers, hooks, browser APIs).Overusing middleware for non-auth logic: Middleware runs on every matching request. Don’t put heavy computation or database calls there — it blocks the response.
Practice Questions
What is the difference between
getStaticPropsandgetServerSideProps?getStaticPropsruns at build time (once), whilegetServerSidePropsruns on every request. Use SSG for content that doesn’t change often, SSR for real-time data.What does
fallback: "blocking"do ingetStaticPaths? It means paths not returned bygetStaticPathsare generated on first visit. The user waits for the page to build once, then it’s cached for subsequent visitors.How do you create a client component in the App Router? Add
"use client"at the top of the file. This tells Next.js to send the JavaScript bundle to the browser for interactivity.What is ISR and when would you use it? Incremental Static Regeneration (ISR) updates static pages after deployment by re-fetching data at a set interval. Use it for content that updates periodically, like a blog with hourly news.
Why use
next/imageinstead of a regular<img>tag?next/imageautomatically optimizes images (resize, WebP conversion, lazy loading, responsive sizes), reducing page weight and improving Core Web Vitals.
Challenge
Build a blog with ISR: Create a Next.js app with app/ directory. Set up a dynamic route app/blog/[slug]/page.js that uses generateStaticParams (the App Router equivalent of getStaticPaths) and fetches data from a mock API. Set revalidate to 60 seconds for ISR. Add a loading.js file for a loading state.
FAQ
Try It Yourself
Create a new Next.js project and build a simple blog:
npx create-next-app@latest my-blog
cd my-blog// app/page.js
async function HomePage() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const posts = await res.json();
return (
<div>
<h1>My ISR Blog</h1>
{posts.slice(0, 5).map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</article>
))}
</div>
);
}
export default HomePage;Expected output: A page displaying 5 blog post titles with body text, fetched from JSONPlaceholder and rendered on the server.
What’s Next
| Topic | Description |
|---|---|
| Master the library Next.js is built on | |
| Hooks, context, and performance optimization | |
| Compare Next.js API routes with Express |
Related terms: React, JavaScript, Node.js, REST API, CSS
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro