Next.js: the framework that turns React into a supercharged , server-rendered , SEO-friendly powerhouse.
If React is a Swiss Army knife, Next.js is that version with a built-in flamethrower and bottle opener.
:) (How’s that for a visual? )
A Brief History of Next.js (Or: How We Got Here) Next.js was born in 2016, crafted by the fine folks at Vercel (then called ZEIT). Their mission? To make React even better by simplifying server-side rendering (SSR) and static site generation (SSG).
Before Next.js, doing SSR with React felt like assembling IKEA furniture without a manual—painful, confusing, and somehow always missing one key piece.
Here’s how Next.js evolved over the years:
Version Release Date Notable Features 1.0 2016-10-25 Server-side rendering (SSR), simple routing 2.0 2017-03-27 Static exports, custom server support 3.0 2017-08-14 Prefetching, dynamic imports 4.0 2017-12-07 Improved SSR, better error handling 5.0 2018-03-09 Webpack 4, multi-zone support 6.0 2018-06-27 Automatic static optimization 7.0 2018-11-21 Faster builds, improved prefetching 8.0 2019-02-27 Incremental Static Regeneration (ISR) 9.0 2019-07-17 API routes, automatic static generation 10.0 2020-10-27 Internationalized routing, image optimization 11.0 2021-06-15 Webpack 5, faster refresh 12.0 2021-10-26 Middleware, React 18 support 13.0 2022-10-25 App directory, RSC (React Server Components) 14.0 2023-10-26 TurboPack, huge performance boost
1. Creating a Simple Page in Next.js Every file inside the /pages
directory is a route automatically. Here’s the simplest page you can create:
1
2
3
4
// pages/index.js
export default function Home () {
return < h1 > Welcome to Next . js !< /h1>;
}
Copy 2. Dynamic Routing with URL Parameters Need dynamic routes? No problem.
1
2
3
4
5
6
7
8
// pages/post/[id].js
import { useRouter } from 'next/router' ;
export default function Post () {
const router = useRouter ();
const { id } = router . query ;
return < h1 > Post ID : { id } < /h1>;
}
Copy 3. Server-Side Rendering (SSR) Need to fetch data on each request ? Use getServerSideProps
.
1
2
3
4
5
6
7
8
9
10
// pages/ssr-example.js
export async function getServerSideProps () {
const res = await fetch ( 'https://jsonplaceholder.typicode.com/posts/1' );
const data = await res . json ();
return { props : { post : data } };
}
export default function SSRPage ({ post }) {
return < h1 > { post . title } < /h1>;
}
Copy 4. Static Site Generation (SSG) For super-fast performance, pre-render pages at build time.
1
2
3
4
5
6
7
8
9
10
// pages/ssg-example.js
export async function getStaticProps () {
const res = await fetch ( 'https://jsonplaceholder.typicode.com/posts/1' );
const data = await res . json ();
return { props : { post : data } };
}
export default function SSGPage ({ post }) {
return < h1 > { post . title } < /h1>;
}
Copy 5. API Routes (Your Built-in Backend) Want to create an API inside your Next.js project? Here you go:
1
2
3
4
// pages/api/hello.js
export default function handler ( req , res ) {
res . status ( 200 ). json ({ message : 'Hello, API!' });
}
Copy 6. Using Middleware for Custom Logic Middleware can intercept requests before they hit your pages.
1
2
3
4
5
6
7
8
// middleware.js
import { NextResponse } from 'next/server' ;
export function middleware ( req ) {
if ( ! req . cookies . authToken ) {
return NextResponse . redirect ( '/login' );
}
}
Copy 7. Optimized Images with Next.js Next.js has an amazing <Image />
component that auto-optimizes images.
1
2
3
4
5
import Image from 'next/image' ;
export default function OptimizedImage () {
return < Image src = "/example.jpg" width = { 500 } height = { 500 } alt = "Example" /> ;
}
Copy 8. Using Edge Functions (Next.js 12+) Run server-side code closer to the user!
1
2
3
4
5
6
// pages/api/edge-example.js
export default async function handler ( req ) {
return new Response ( 'Hello from the edge!' , {
status : 200 ,
});
}
Copy 9. Incremental Static Regeneration (ISR) Regenerate static pages dynamically without a full rebuild.
1
2
3
4
5
6
7
8
9
10
// pages/isr-example.js
export async function getStaticProps () {
const res = await fetch ( 'https://jsonplaceholder.typicode.com/posts/1' );
const data = await res . json ();
return { props : { post : data }, revalidate : 10 };
}
export default function ISRPage ({ post }) {
return < h1 > { post . title } < /h1>;
}
Copy 10. Internationalized Routing (i18n) Easily handle multiple languages without a headache .
1
2
3
4
5
6
7
// next.config.js
module . exports = {
i18n : {
locales : [ 'en' , 'es' , 'fr' ],
defaultLocale : 'en' ,
},
};
Copy 11. Custom _app.js
for Global State Management Want to persist global state across pages? Modify _app.js
.
1
2
3
4
5
6
7
8
9
// pages/_app.js
import { useState } from 'react' ;
import '../styles/globals.css' ;
function MyApp ({ Component , pageProps }) {
const [ user , setUser ] = useState ( null );
return < Component {... pageProps } user = { user } setUser = { setUser } /> ;
}
export default MyApp ;
Copy 12. Custom _document.js
for Modifying <html>
and <body>
Use _document.js
to modify the document structure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// pages/_document.js
import { Html , Head , Main , NextScript } from 'next/document' ;
export default function Document () {
return (
< Html lang = "en" >
< Head >
< link rel = "stylesheet" href = "/custom.css" />
< /Head>
< body >
< Main />
< NextScript />
< /body>
< /Html>
);
}
Copy 13. Fetch Data on the Client Side with useEffect
If you don’t need SSR, fetch data client-side instead.
1
2
3
4
5
6
7
8
9
10
11
12
13
import { useEffect , useState } from 'react' ;
export default function ClientFetch () {
const [ data , setData ] = useState ( null );
useEffect (() => {
fetch ( '/api/example' )
. then (( res ) => res . json ())
. then (( data ) => setData ( data ));
}, []);
return < div > { data ? JSON . stringify ( data ) : 'Loading...' } < /div>;
}
Copy 14. Creating Protected Routes (Authentication) Protect pages by checking authentication status.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// pages/protected.js
import { useRouter } from 'next/router' ;
import { useEffect } from 'react' ;
export default function ProtectedPage () {
const router = useRouter ();
useEffect (() => {
const token = localStorage . getItem ( 'authToken' );
if ( ! token ) router . push ( '/login' );
}, []);
return < h1 > Protected Content < /h1>;
}
Copy 15. Custom Express Server with Next.js Want more control? Use a custom server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// server.js
const express = require ( 'express' );
const next = require ( 'next' );
const app = next ({ dev : process . env . NODE_ENV !== 'production' });
const handle = app . getRequestHandler ();
app . prepare (). then (() => {
const server = express ();
server . get ( '*' , ( req , res ) => {
return handle ( req , res );
});
server . listen ( 3000 , () => console . log ( 'Server running on port 3000' ));
});
Copy Boost SEO with dynamic <meta>
tags.
1
2
3
4
5
6
7
8
9
10
11
12
13
import Head from 'next/head' ;
export default function SEOPage () {
return (
<>
< Head >
< title > SEO in Next . js < /title>
< meta name = "description" content = "Learn SEO in Next.js" />
< /Head>
< h1 > SEO Page < /h1>
< />
);
}
Copy 17. Styling with Tailwind CSS Next.js works great with Tailwind.
1
2
3
4
5
6
7
// Install Tailwind: npm install tailwindcss postcss autoprefixer
// Add Tailwind to globals.css
import 'tailwindcss/tailwind.css' ;
export default function TailwindExample () {
return < h1 className = "text-4xl font-bold text-blue-500" > Hello Tailwind !< /h1>;
}
Copy 18. Using next/script
for Third-Party Scripts Load third-party scripts efficiently.
1
2
3
4
5
6
7
8
9
10
import Script from 'next/script' ;
export default function ExternalScript () {
return (
<>
< h1 > Using Next . js Script < /h1>
< Script src = "https://example.com/script.js" strategy = "lazyOnload" />
< />
);
}
Copy Improve navigation speed with prefetching.
1
2
3
4
5
6
7
8
9
import Link from 'next/link' ;
export default function PrefetchExample () {
return (
< Link href = "/about" prefetch >
Go to About Page
< /Link>
);
}
Copy 20. Handling 404 Pages Customize your 404 error page.
1
2
3
4
// pages/404.js
export default function Custom404 () {
return < h1 > Oops ! Page Not Found 😢 < /h1>;
}
Copy Want to add security headers or modify responses dynamically? Middleware is your best friend.
1
2
3
4
5
6
7
8
// middleware.js
import { NextResponse } from 'next/server' ;
export function middleware ( req ) {
const res = NextResponse . next ();
res . headers . set ( 'X-Custom-Header' , 'MyHeaderValue' );
return res ;
}
Copy 22. Prefetching API Requests with SWR For blazing-fast UI updates, use SWR for API fetching with caching and revalidation.
1
2
3
4
5
6
7
8
9
10
11
12
import useSWR from 'swr' ;
const fetcher = ( url ) => fetch ( url ). then (( res ) => res . json ());
export default function Dashboard () {
const { data , error } = useSWR ( '/api/data' , fetcher );
if ( error ) return < div > Failed to load < /div>;
if ( ! data ) return < div > Loading ... < /div>;
return < h1 > Welcome , { data . user } < /h1>;
}
Copy 23. Generating Dynamic Sitemaps Make sure search engines love your Next.js app with a dynamic sitemap .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pages/sitemap.xml.js
import { getServerSideProps } from 'next' ;
export default function Sitemap () {}
export async function getServerSideProps ({ res }) {
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc></url>
</urlset>` ;
res . setHeader ( 'Content-Type' , 'text/xml' );
res . write ( sitemap );
res . end ();
return { props : {} };
}
Copy 24. Optimizing Fonts with next/font
Improve performance by loading Google Fonts the Next.js way .
1
2
3
4
5
6
7
import { Inter } from 'next/font/google' ;
const inter = Inter ({ subsets : [ 'latin' ] });
export default function Home () {
return < h1 className = { inter . className } > Optimized Fonts !< /h1>;
}
Copy 25. Caching API Responses with revalidate
Want data that refreshes every 10 seconds? Incremental Static Regeneration (ISR) has you covered.
1
2
3
4
5
6
7
8
9
export async function getStaticProps () {
const res = await fetch ( 'https://jsonplaceholder.typicode.com/posts/1' );
const data = await res . json ();
return {
props : { post : data },
revalidate : 10 ,
};
}
Copy 26. Creating a Custom 500 Error Page Make sure your error pages are just as polished as the rest of your app.
1
2
3
4
// pages/500.js
export default function Custom500 () {
return < h1 > Something went wrong ! 🚨 < /h1>;
}
Copy 27. Enabling React Strict Mode For better debugging and catching potential issues, enable strict mode in next.config.js
.
1
2
3
4
// next.config.js
module . exports = {
reactStrictMode : true ,
};
Copy Reduce initial load times by dynamically importing components.
1
2
3
4
5
6
7
8
9
import dynamic from 'next/dynamic' ;
const HeavyComponent = dynamic (() => import ( '../components/HeavyComponent' ), {
ssr : false ,
});
export default function Page () {
return < HeavyComponent /> ;
}
Copy 29. Using WebSockets in Next.js API Routes Need real-time updates ? WebSockets work inside API routes!
1
2
3
4
5
6
7
8
9
10
// pages/api/socket.js
import { Server } from 'socket.io' ;
export default function handler ( req , res ) {
if ( ! res . socket . server . io ) {
const io = new Server ( res . socket . server );
res . socket . server . io = io ;
}
res . end ();
}
Copy 30. Customizing the Build Output Directory Change the default .next
folder to something custom in next.config.js
.
1
2
3
4
// next.config.js
module . exports = {
distDir : 'build' ,
};
Copy 31. Advanced Dynamic Routing with Catch-All Routes Need a route that captures multiple segments? Catch-all routes got you covered.
1
2
3
4
5
6
7
8
9
// pages/blog/[...slug].js
import { useRouter } from 'next/router' ;
export default function BlogPost () {
const router = useRouter ();
const { slug } = router . query ;
return < h1 > Blog Post : { slug ? . join ( ' / ' )} < /h1>;
}
Copy 32. Using Edge Functions for Lightning-Fast Requests Deploy serverless functions at the edge for near-instant responses.
1
2
3
4
5
6
7
8
// pages/api/edge.js
export const config = { runtime : 'edge' };
export default async function handler ( req ) {
return new Response ( JSON . stringify ({ message : 'Hello from the edge!' }), {
headers : { 'Content-Type' : 'application/json' },
});
}
Copy 33. Optimizing API Responses with Cache-Control
Use cache headers to boost performance and reduce unnecessary requests.
1
2
3
4
5
// pages/api/cached-data.js
export default function handler ( req , res ) {
res . setHeader ( 'Cache-Control' , 's-maxage=60, stale-while-revalidate' );
res . json ({ message : 'Cached response' });
}
Copy 34. Implementing Role-Based Access Control (RBAC) Secure pages based on user roles dynamically.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// components/ProtectedRoute.js
import { useRouter } from 'next/router' ;
import { useEffect } from 'react' ;
export default function ProtectedRoute ({ user , allowedRoles , children }) {
const router = useRouter ();
useEffect (() => {
if ( ! user || ! allowedRoles . includes ( user . role )) {
router . push ( '/unauthorized' );
}
}, [ user , allowedRoles , router ]);
return <> { children } < />;
}
Copy 35. Custom Webpack Configuration Extend Next.js with a custom Webpack config .
1
2
3
4
5
6
7
8
9
// next.config.js
module . exports = {
webpack : ( config ) => {
config . module . rules . push ({
test : /\.md$/ , use : 'raw-loader' ,
});
return config ;
},
};
Copy 36. Prefetching API Data for Better UX Preload API calls before users even navigate to a page.
1
2
3
4
5
6
7
8
9
10
11
// pages/index.js
import Link from 'next/link' ;
import { useEffect } from 'react' ;
export default function Home () {
useEffect (() => {
fetch ( '/api/data' ); // Prefetch API data
}, []);
return < Link href = "/dashboard" > Go to Dashboard < /Link>;
}
Copy 37. Custom ESLint Rules in Next.js Force best practices with custom ESLint rules .
1
2
3
4
5
6
7
// .eslintrc.js
module . exports = {
rules : {
'no-console' : 'warn' ,
'react/no-unescaped-entities' : 'off' ,
},
};
Copy 38. Testing Next.js Apps with Jest and React Testing Library Unit test your Next.js components like a pro.
1
2
3
4
5
6
7
8
// __tests__/index.test.js
import { render , screen } from '@testing-library/react' ;
import Home from '../pages/index' ;
test ( 'renders welcome message' , () => {
render ( < Home /> );
expect ( screen . getByText ( /Welcome to Next.js!/i )). toBeInTheDocument ();
});
Copy 39. Implementing Webhooks for Real-Time Events Trigger actions based on external events.
1
2
3
4
5
6
7
8
9
// pages/api/webhook.js
export default async function handler ( req , res ) {
if ( req . method === 'POST' ) {
console . log ( 'Webhook received:' , req . body );
res . status ( 200 ). send ( 'OK' );
} else {
res . status ( 405 ). send ( 'Method Not Allowed' );
}
}
Copy 40. Handling Background Jobs with Serverless Functions Process long-running tasks asynchronously.
1
2
3
4
5
// pages/api/background-task.js
export default async function handler ( req , res ) {
setTimeout (() => console . log ( 'Background job done!' ), 5000 );
res . status ( 200 ). json ({ status : 'Job started' });
}
Copy