Welcome to the ultimate Next.js interview guide! Whether youβre a beginner or an experienced developer, this comprehensive resource will help you ace your Next.js interview. Letβs dive in! πββοΈ
getStaticProps
FunctiongetServerSideProps
FunctiongetStaticPaths
vs getServerSideProps
next/head
Component_app.js
FileNext.js is like a supercharged version of React that makes building websites easier and faster. Itβs like having a smart assistant that helps you create web pages that load quickly and work well for both users and search engines.
Next.js is a React-based open-source web application framework developed by Vercel. It extends Reactβs capabilities by providing a robust set of features for building modern web applications, including server-side rendering, static site generation, and API routes.
Imagine youβre building an e-commerce website. With Next.js, you can:
// pages/products/[id].js
import { useRouter } from 'next/router'
import Image from 'next/image'
export default function Product({ product }) {
const router = useRouter()
if (router.isFallback) {
return <div>Loading...</div>
}
return (
<div>
<h1>{product.name}</h1>
<Image src={product.image} alt={product.name} width={500} height={500} />
<p>{product.description}</p>
<button>Add to Cart</button>
</div>
)
}
export async function getStaticPaths() {
// Fetch product IDs from an API
const products = await fetchProductIds()
const paths = products.map((product) => ({
params: { id: product.id.toString() },
}))
return { paths, fallback: true }
}
export async function getStaticProps({ params }) {
// Fetch product details
const product = await fetchProductById(params.id)
return {
props: { product },
revalidate: 60, // Revalidate every 60 seconds
}
}
This example demonstrates how Next.js combines SSG with dynamic routes and image optimization for an e-commerce product page.
Imagine youβre a chef in a restaurant. Instead of giving customers raw ingredients (like traditional React), SSR is like serving them a fully cooked meal. The server prepares the web page before sending it to the userβs browser, making it ready to eat (or view) immediately!
Server-Side Rendering in Next.js is a technique where the initial HTML content is generated on the server for each request. This process involves running React components on the server, fetching necessary data, and sending the fully rendered HTML to the client. SSR improves initial page load times and enhances SEO by providing search engines with fully rendered content.
getServerSideProps
is defined, it fetches data.getServerSideProps
π₯οΈ// pages/posts/[id].js
export default function Post({ post }) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
)
}
export async function getServerSideProps(context) {
const { params } = context
const res = await fetch(`https://api.example.com/posts/${params.id}`)
const post = await res.json()
return {
props: { post }, // Will be passed to the page component as props
}
}
In this example, getServerSideProps
fetches data for a specific post on every request, ensuring the content is always up-to-date.
SSR is ideal for pages that need to display real-time or frequently updated data, such as:
While SSR ensures up-to-date content, it can increase server load and time-to-first-byte (TTFB). Itβs important to balance SSR usage with caching strategies and consider static generation for content that doesnβt change frequently.
Think of SSG like baking a batch of cookies before a party. Instead of making cookies (web pages) for each guest (user) when they arrive, you bake them all beforehand. When guests come, you just hand them the pre-made cookies, which is much faster!
Static Site Generation (SSG) is a technique where pages are pre-rendered at build time. Next.js generates HTML files for each page during the build process, which can then be served directly from a CDN. This approach is ideal for content that doesnβt change frequently and provides excellent performance and scalability.
getStaticProps
for each page that uses it.getStaticProps
and getStaticPaths
π// pages/blog/[slug].js
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML= />
</article>
)
}
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts')
const posts = await res.json()
const paths = posts.map((post) => ({
params: { slug: post.slug },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
const post = await res.json()
return {
props: { post },
revalidate: 60 * 60, // Revalidate every hour
}
}
This example demonstrates how to generate static pages for a blog, with incremental static regeneration enabled.
Next.js also offers ISR, which allows you to update static content after youβve built your site. This is great for sites with a large number of pages or frequently updated content.
export async function getStaticProps() {
const res = await fetch('https://api.example.com/data')
const data = await res.json()
return {
props: { data },
revalidate: 60, // Regenerate page every 60 seconds if requested
}
}
Dynamic routes in Next.js are like having a magical door that can lead to many different rooms. Instead of creating a separate door (page) for each room (content), you create one special door that can adapt to show the right room based on what the visitor asks for.
Dynamic routes in Next.js allow you to create pages with paths that are determined at runtime. This feature enables you to build pages that can handle a wide range of URL parameters without explicitly defining each possible route. Dynamic routes are particularly useful for content-rich applications where the URL structure is based on data, such as blog posts, product pages, or user profiles.
[id].js
or [slug].js
.query
object in getStaticProps
, getServerSideProps
, or the useRouter
hook.getStaticPaths
to specify which paths should be pre-rendered.// pages/posts/[slug].js
import { useRouter } from 'next/router'
export default function Post({ post }) {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML= />
</article>
)
}
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { slug: post.slug },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
const post = await res.json()
return {
props: { post },
revalidate: 60 * 60, // Revalidate every hour
}
}
Next.js supports complex dynamic routes, including:
/posts/[category]/[slug]
/[year]/[month]/[day]
/posts/[...slug]
(matches /posts/a
, /posts/a/b
, /posts/a/b/c
, etc.)/posts/[[...slug]]
(also matches /posts
)/blog/[slug]
/products/[id]
/users/[username]
/docs/[...path]
/[lang]/[...path]
[productId]
instead of [id]
).fallback: true
or fallback: 'blocking'
in getStaticPaths
for large datasets.getStaticProps
and getStaticPaths
functions to reduce build times.getStaticProps
FunctionImagine youβre preparing a presentation. getStaticProps
is like gathering all the information you need before you start presenting. It helps you collect data ahead of time so that when someone views your page, all the information is ready to go!
getStaticProps
is a Next.js function that runs at build time in production and allows you to fetch external data and send it as props to a page. This function enables you to pre-render pages with dynamic content, improving performance and SEO. Itβs a crucial part of Static Site Generation (SSG) in Next.js.
getStaticProps
Works π§getStaticProps
runs at build time on the server-side.next/link
or next/router
.getStaticProps
π// pages/products.js
export default function Products({ products }) {
return (
<div>
<h1>Our Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
</div>
)
}
export async function getStaticProps() {
// Fetch data from an API
const res = await fetch('https://api.example.com/products')
const products = await res.json()
// The value of the `props` key will be
// passed to the `Products` component
return {
props: {
products,
},
revalidate: 60 * 60, // Revalidate every hour
}
}
getStaticProps
πnext build
for production.getStaticProps
π οΈrevalidate
for data that changes over time.getStaticPaths
for dynamic routes.getServerSideProps
FunctionIf getStaticProps
is like preparing a presentation in advance, getServerSideProps
is like giving an impromptu speech. It gathers information on the spot, every time someone asks, ensuring the content is always up-to-date but potentially taking a bit longer to deliver.
getServerSideProps
is a Next.js function that runs on every request, allowing you to fetch data and render pages on the server-side. This function is crucial for Server-Side Rendering (SSR) in Next.js, enabling you to create dynamic pages with real-time or user-specific content.
getServerSideProps
Works πgetServerSideProps
runs on every request on the server-side.getServerSideProps
π₯οΈ// pages/dashboard.js
export default function Dashboard({ user, posts }) {
return (
<div>
<h1>Welcome, {user.name}!</h1>
<h2>Your Recent Posts:</h2>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
)
}
export async function getServerSideProps(context) {
const { req } = context
const userId = getUserIdFromCookie(req)
// Fetch user data
const user = await fetchUserData(userId)
// Fetch user's recent posts
const posts = await fetchUserPosts(userId)
// Pass data to the page via props
return { props: { user, posts } }
}
getServerSideProps
πgetServerSideProps
πAPI routes in Next.js are like secret passages in your web application. They allow your website to communicate with databases or other services behind the scenes, without the user seeing all the complex stuff happening.
API routes provide a solution to build your API with Next.js. They allow you to create serverless API endpoints as part of your Next.js application, enabling you to handle server-side logic and data operations without setting up a separate backend server.
pages/api
directory.req
(request) and res
(response) objects to handle the API logic.// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' })
}
// pages/api/login.js
import { compare } from 'bcrypt'
import { sign } from 'jsonwebtoken'
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).end()
}
const { email, password } = req.body
try {
const user = await getUserByEmail(email)
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' })
}
const isPasswordValid = await compare(password, user.hashedPassword)
if (!isPasswordValid) {
return res.status(401).json({ message: 'Invalid credentials' })
}
const token = sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' })
res.status(200).json({ token })
} catch (error) {
console.error(error)
res.status(500).json({ message: 'Internal server error' })
}
}
getStaticPaths
vs getServerSideProps
Imagine youβre planning a road trip. getStaticPaths
is like deciding which routes to map out before you leave, while getServerSideProps
is like using a GPS that recalculates your route in real-time as you drive.
getStaticPaths
and getServerSideProps
are both Next.js data fetching methods, but they serve different purposes and have distinct use cases.
getStaticPaths
πΊοΈgetStaticProps
getServerSideProps
π₯οΈFeature | getStaticPaths |
getServerSideProps |
---|---|---|
Execution Time | Build time | Runtime (every request) |
Data Freshness | Static (can be revalidated) | Always fresh |
Performance | Faster initial load | Slower initial load |
Use Case | Static content with dynamic routes | Real-time or user-specific data |
SEO | Excellent (pre-rendered) | Good (server-rendered) |
Scalability | Highly scalable | Depends on server capacity |
getStaticPaths
π// pages/posts/[id].js
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts')
const posts = await res.json()
const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
getServerSideProps
π// pages/profile.js
export async function getServerSideProps(context) {
const { req } = context
const { userId } = parseJwtFromCookie(req.headers.cookie)
const res = await fetch(`https://api.example.com/users/${userId}`)
const userData = await res.json()
return { props: { userData } }
}
Use getStaticPaths
when:
Use getServerSideProps
when:
getStaticPaths
with fallback: true
or 'blocking'
for large datasetsgetServerSideProps
By understanding the differences and use cases for getStaticPaths
and getServerSideProps
, you can make informed decisions about data fetching in your Next.js applications, balancing performance, scalability, and real-time data needs.
Styling in Next.js is like choosing outfits for your website. Next.js gives you a variety of ways to dress up your pages, from using simple, global styles to more sophisticated, component-specific fashions.
Next.js provides multiple approaches to styling your application, offering flexibility and powerful features to manage CSS effectively. These approaches cater to different use cases and developer preferences.
pages/_app.js
// pages/_app.js
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
// components/Button.js
import styles from './Button.module.css'
export default function Button() {
return <button className={styles.error}>Error Button</button>
}
/* Button.module.css */
.error {
color: white;
background-color: red;
}
.scss
and .sass
files// pages/_app.js
import '../styles/globals.scss'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
// components/StyledButton.js
import styled from 'styled-components'
const StyledButton = styled.button`
color: ${props => props.primary ? 'white' : 'palevioletred'};
background-color: ${props => props.primary ? 'palevioletred' : 'white'};
border: 2px solid palevioletred;
border-radius: 3px;
`
export default function Button({ primary }) {
return <StyledButton primary={primary}>Click me</StyledButton>
}
// components/TailwindButton.js
export default function TailwindButton() {
return (
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Click me
</button>
)
}
next/image
for automatic image optimization.By understanding these different CSS approaches in Next.js, you can choose the best styling solution for your project, balancing flexibility, maintainability, and performance.
If traditional React is like building a house from scratch, Next.js is like using a pre-fabricated house kit. It comes with a lot of built-in features that make it easier and faster to create a complete, production-ready website.
Next.js builds upon Reactβs foundation, offering a robust framework that addresses common challenges in web development. It provides several out-of-the-box features and optimizations that significantly enhance the developer experience and application performance.
next/image
componentFeature | Next.js | Traditional React |
---|---|---|
Server-Side Rendering | Built-in | Requires additional setup |
Static Site Generation | Built-in | Requires additional tools |
Routing | File-based | Requires react-router or similar |
Code Splitting | Automatic | Manual configuration needed |
API Routes | Built-in | Separate backend needed |
Image Optimization | Automatic | Manual optimization required |
CSS Support | Built-in modules | Additional setup needed |
TypeScript Support | Out of the box | Requires configuration |
Letβs consider building an e-commerce site with Next.js vs traditional React:
// pages/products/[id].js (Next.js)
export default function Product({ product }) {
return (
<div>
<h1>{product.name}</h1>
<Image src={product.image} alt={product.name} width={500} height={500} />
<p>{product.description}</p>
<button>Add to Cart</button>
</div>
)
}
export async function getStaticProps({ params }) {
const product = await fetchProductById(params.id)
return { props: { product } }
}
export async function getStaticPaths() {
const products = await fetchAllProductIds()
const paths = products.map((id) => ({ params: { id: id.toString() } }))
return { paths, fallback: 'blocking' }
}
With Next.js, you get:
In traditional React, youβd need to:
next/image
componentBy understanding these benefits, developers can make informed decisions about when to use Next.js over traditional React, especially for projects that require superior performance, SEO, and developer experience.
Next.js Image Optimization is like having a personal photo editor for your website. It automatically resizes, compresses, and formats your images to look great on any device, without you having to do all the work manually.
Next.js provides built-in image optimization through the next/image
component. This feature automatically optimizes images for performance, applying best practices for sizing, formatting, and loading to improve Core Web Vitals and overall user experience.
import Image from 'next/image'
function ProductImage({ src, alt }) {
return (
<Image
src={src}
alt={alt}
width={500}
height={500}
layout="responsive"
placeholder="blur"
blurDataURL={`data:image/svg+xml;base64,...`}
/>
)
}
You can configure image optimization in your next.config.js
:
module.exports = {
images: {
domains: ['example.com'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
loader: 'imgix',
path: 'https://example.com/myaccount/',
},
}
width
and height
to prevent layout shift.layout="responsive"
for images that should adapt to their container.priority
prop for above-the-fold images.next/image
.By leveraging Next.js Image Optimization, developers can significantly improve their applicationβs performance and user experience with minimal effort.
next/head
ComponentThe next/head
component is like the invisible hat of your webpage. It allows you to put important information and instructions for browsers and search engines at the top of your page, even though users canβt see it directly.
The next/head
component in Next.js is used to append elements to the <head>
of the page. Itβs crucial for managing metadata, setting the page title, including external stylesheets or scripts, and controlling how your page appears in search results or when shared on social media.
next/head
π©<head>
elements based on the current page or component.import Head from 'next/head'
function IndexPage() {
return (
<div>
<Head>
<title>My Page Title</title>
<meta name="description" content="This is my page description" />
<meta property="og:title" content="My Page Title" />
<meta property="og:description" content="This is my page description" />
<meta property="og:image" content="https://example.com/og-image.jpg" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1>Welcome to my page</h1>
</div>
)
}
export default IndexPage
import Head from 'next/head'
function ProductPage({ product }) {
return (
<div>
<Head>
<title>{product.name} | My Store</title>
<meta name="description" content={product.description} />
<meta property="og:title" content={`${product.name} | My Store`} />
<meta property="og:description" content={product.description} />
<meta property="og:image" content={product.image} />
<meta property="og:type" content="product" />
<meta property="product:price:amount" content={product.price} />
<meta property="product:price:currency" content="USD" />
</Head>
{/* Product details */}
</div>
)
}
export default ProductPage
viewport
in a global _app.js
file.next/head
can significantly improve your siteβs SEO.By mastering the next/head
component, developers can ensure their Next.js applications are well-optimized for search engines and social media platforms, leading to better visibility and user engagement.
Authentication in Next.js is like having a bouncer at a club. It checks if people (users) are allowed to enter certain areas of your website and keeps track of who they are.
Authentication in Next.js involves verifying user identity and managing user sessions across both client-side and server-side rendered pages. Next.js provides flexibility in implementing authentication, allowing developers to choose from various strategies and integrate with different authentication providers.
// pages/api/login.js
import jwt from 'jsonwebtoken'
export default async function login(req, res) {
const { username, password } = req.body
// Verify credentials (simplified)
if (username === 'admin' && password === 'password') {
const token = jwt.sign({ username }, process.env.JWT_SECRET, { expiresIn: '1h' })
res.status(200).json({ token })
} else {
res.status(401).json({ message: 'Invalid credentials' })
}
}
// utils/auth.js
import { verify } from 'jsonwebtoken'
export function authenticateToken(req) {
const token = req.headers.authorization?.split(' ')[1]
if (!token) return null
try {
return verify(token, process.env.JWT_SECRET)
} catch {
return null
}
}
// pages/api/protected.js
import { authenticateToken } from '../../utils/auth'
export default function handler(req, res) {
const user = authenticateToken(req)
if (!user) {
return res.status(401).json({ message: 'Unauthorized' })
}
res.status(200).json({ message: 'Protected data', user })
}
Next.js 12+ introduced middleware, which can be used for authentication:
// middleware.js
import { NextResponse } from 'next/server'
import { authenticateToken } from './utils/auth'
export function middleware(req) {
const user = authenticateToken(req)
if (!user && req.nextUrl.pathname.startsWith('/protected')) {
return NextResponse.redirect(new URL('/login', req.url))
}
return NextResponse.next()
}
Next.js can be easily integrated with authentication providers like Auth0, Firebase, or NextAuth.js. Hereβs a basic example using NextAuth.js:
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
export default NextAuth({
providers: [
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
// Add other providers here
],
// Add custom pages, callbacks, etc.
})
// pages/_app.js
import { Provider } from 'next-auth/client'
function MyApp({ Component, pageProps }) {
return (
<Provider session={pageProps.session}>
<Component {...pageProps} />
</Provider>
)
}
export default MyApp
By implementing robust authentication in Next.js, developers can ensure that their applications are secure and that user data is protected. The flexibility of Next.js allows for various authentication strategies to be implemented based on the specific needs of the application.
_app.js
File in Next.jsThe _app.js
file in Next.js is like the main control room for your website. Itβs where you can set up things that should apply to all pages, like a consistent layout or global styles.
The _app.js
file is a special Next.js file that initializes pages. It allows you to override the default App component, control page initialization, and persist layouts between page changes. This file is crucial for adding global styles, managing state across the application, and integrating third-party libraries.
_app.js
π_app.js
Structure πimport '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
_app.js
Example πimport '../styles/globals.css'
import { ThemeProvider } from 'styled-components'
import { Provider } from 'react-redux'
import { store } from '../store'
import Layout from '../components/Layout'
function MyApp({ Component, pageProps }) {
const theme = {
colors: {
primary: '#0070f3',
},
}
return (
<Provider store={store}>
<ThemeProvider theme={theme}>
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
</Provider>
)
}
export default MyApp
_app.js
π οΈimport { Provider } from 'react-redux'
import { store } from '../store'
function MyApp({ Component, pageProps }) {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
)
}
import ErrorBoundary from '../components/ErrorBoundary'
function MyApp({ Component, pageProps }) {
return (
<ErrorBoundary>
<Component {...pageProps} />
</ErrorBoundary>
)
}
import { motion } from 'framer-motion'
function MyApp({ Component, pageProps }) {
return (
<motion.div
initial=
animate=
transition=
>
<Component {...pageProps} />
</motion.div>
)
}
_app.js
lean to avoid performance issues._app.js
for global concerns, not page-specific logic.getInitialProps
in _app.js
cautiously, as it disables automatic static optimization._document.js
for custom HTML structure if needed._app.js
as they affect all pages.By effectively utilizing _app.js
, developers can create consistent, well-structured Next.js applications with shared functionality across all pages.
Redirects and rewrites in Next.js are like traffic signs for your website. Redirects are like detour signs that send visitors to a different page, while rewrites are like secret passages that show content from one URL while keeping the original URL visible.
Next.js provides powerful built-in support for redirects and rewrites, allowing developers to manage URL structures, handle legacy URLs, and create clean, user-friendly URLs while maintaining flexibility in how content is served.
Redirects allow you to send users or search engines to a different URL than the one they originally requested.
Redirects are configured in the next.config.js
file:
module.exports = {
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true,
},
{
source: '/docs/:path*',
destination: 'https://new-docs.example.com/:path*',
permanent: false,
},
]
},
}
Rewrites allow you to map an incoming request path to a different destination path without changing the URL in the browser.
Rewrites are also configured in the next.config.js
file:
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
{
source: '/about',
destination: '/about-us',
},
]
},
}
You can use functions to create dynamic redirects or rewrites based on the request:
module.exports = {
async redirects() {
return [
{
source: '/old-products/:id',
destination: '/products/:id',
permanent: true,
},
{
source: '/blog/:slug',
has: [{ type: 'query', key: 'preview', value: 'true' }],
destination: '/blog/preview/:slug',
permanent: false,
},
]
},
async rewrites() {
return {
beforeFiles: [
{
source: '/products/:id',
destination: '/items/:id',
},
],
afterFiles: [
{
source: '/docs/:path*',
destination: 'https://docs.example.com/:path*',
},
],
fallback: [
{
source: '/:path*',
destination: 'https://old-site.example.com/:path*',
},
],
}
},
}
Link
header for HTTP redirects to improve crawling efficiency.By mastering redirects and rewrites in Next.js, developers can create more flexible, maintainable, and SEO-friendly applications while providing a seamless experience for users.