Next.js Interview Questions and Answers

Routing, rendering modes, data fetching and deployment on Next.js.

Practise 10 random 16 peer-reviewed questions
Next.js Interview Syllabus & Preparation Strategy

Whether you are preparing for entry-level Next.js interview questions for freshers or senior software engineer interview questions addressing concurrency, scalability, and system architecture, this track provides peer-reviewed model answers with syntax walkthroughs, edge cases, and practical interview tips.

1 How do the different Next.js caches and revalidation layers work? Hard

Next.js has several cache layers. The Data Cache stores individual fetch results and can be tagged. The Full Route Cache stores rendered HTML and RSC payloads for static routes at build time. The Router Cache holds prefetched segments in the browser. Request memoisation deduplicates identical fetches within one render.

fetch is cached by default in the App Router; cache: 'no-store' or export const dynamic = 'force-dynamic' opts a segment out. Time-based revalidation uses next: { revalidate: 60 }. On-demand revalidation uses revalidatePath or revalidateTag from a Server Action or route handler after a mutation.

revalidateTag('posts');
revalidatePath('/blog');

Routes that call dynamic functions such as cookies(), headers() or searchParams become dynamic automatically. Getting this wrong produces stale data (too much caching) or slow, expensive renders (too little). Segment config plus unstable_noStore() gives finer control, and revalidateTag is essential right after writes.

2 What causes hydration mismatches and how do you fix them? Hard

Hydration is React attaching event handlers to server-rendered HTML. A mismatch happens when the server HTML differs from the client's first render, and React warns and re-renders or discards the tree. Common causes are rendering Date.now(), Math.random(), typeof window checks, localStorage reads, Intl formatting differences, invalid HTML nesting such as a div inside a p, and browser extensions injecting nodes.

In Next.js, avoid browser-only values during render. Initialise state to a deterministic value and set it in an effect, or gate rendering behind a mounted flag:

const [now, setNow] = useState<number | null>(null);
useEffect(() => setNow(Date.now()), []);

suppressHydrationWarning silences known-safe text mismatches such as timestamps, but it hides bugs, so use it sparingly and never on a whole subtree. Because server and client must run the same code path, check shared components for conditional window logic and for locale or timezone formatting that differs between environments.

3 How do you configure Webpack in Next.js? Hard

By adding a custom webpack configuration in next.config.js.

module.exports = {
  webpack: (
    config,
    { buildId, dev, isServer, defaultLoaders, webpack }
  ) => {
    // Note: we provide webpack above so you should not `require` it
    // Perform customizations to webpack config
    config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//));

    // Important: return the modified config
    return config;
  },
};
4 How do you configure a custom Babel setup in Next.js? Hard

By default, Next.js uses the high-performance Rust-based SWC compiler, which is up to 17x faster than Babel. However, if your project relies on legacy custom Babel plugins, you can configure Babel:

### Adding babel.config.js or .babelrc:
Create a babel.config.js in your project root:

module.exports = {
  presets: ['next/babel'],
  plugins: [
    ['@babel/plugin-proposal-decorators', { legacy: true }],
  ],
};

### Important Warning:
Adding a custom Babel configuration automatically opts your project out of the Next.js SWC compiler, resulting in significantly slower build times and development compilation. You should only use Babel if a required plugin is not yet supported in SWC or via SWC plugins (next.config.js -> compiler).

5 What is middleware? Hard

Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers or responding directly.

import { NextResponse } from "next/server";

// This function can be marked `async` if using `await` inside
export function middleware(request) {
  return NextResponse.redirect(new URL("/home", request.url));
}

// See "Matching Paths" below to learn more
export const config = {
  matcher: "/about/:path*",
};
6 How do you use the Edge Runtime for API routes? Hard

You can opt into the Edge Runtime to run API routes on the V8/edge environment. Add export const runtime = "edge"; at the top of a route handler and use the Web Request/Response APIs.

// app/api/hello/route.js
export const runtime = "edge";

export async function GET(request) {
  return new Response(JSON.stringify({ message: "Hello from the Edge" }), {
    headers: { "Content-Type": "application/json" },
  });
}
7 How can you implement simple rate limiting for API routes? Hard

For basic protection you can use an in-memory store (suitable for single-instance apps) or a shared store (Redis) for multiple instances. Example memory-based limiter using a Map:

// pages/api/limited.js
const hits = new Map();
const WINDOW_MS = 60_000; // 1 minute
const MAX = 60;

export default function handler(req, res) {
  const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
  const now = Date.now();
  const entry = hits.get(ip) || { count: 0, start: now };
  if (now - entry.start > WINDOW_MS) (entry.count = 0), (entry.start = now);
  entry.count += 1;
  hits.set(ip, entry);
  if (entry.count > MAX)
    return res.status(429).json({ error: "Too many requests" });
  res.status(200).json({ ok: true });
}



8 How do you handle middleware in Next.js with the Pages Router? Hard

By creating a custom server or using API routes to implement middleware logic.

// pages/api/middleware.js
export default function middleware(req, res, next) {
  // Custom middleware logic
  if (req.headers.authorization) {
    next(); // Proceed to the next handler
  } else {
    res.status(401).json({ error: "Unauthorized" });
  }
}
9 How do you handle middleware in Next.js? Hard

Middleware in Next.js enables you to run code on incoming requests before they are completed, ideal for authentication, bot detection, A/B testing, and URL rewrites:

### Creating Middleware:
Create a single middleware.ts (or .js) file at the root of your project (or inside src/):

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/request';

export function middleware(request: NextRequest) {
  const token = request.cookies.get('auth_token')?.value;

  // Protect admin dashboard routes
  if (request.nextUrl.pathname.startsWith('/admin') && !token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

// Matcher config: run only on specific routes
export const config = {
  matcher: ['/admin/:path*', '/dashboard/:path*'],
};

### Key Capabilities:

  • Operates on the lightweight Next.js Edge Runtime.
  • Can modify request headers, response headers, or set cookies.
  • Can rewrite URLs (NextResponse.rewrite()) or redirect users (NextResponse.redirect()).
10 How do middleware work in Next.js? Hard

Middleware in Next.js allows you to run code before a request is completed. You can use it to modify the request or response, redirect users, or perform authentication checks.

// app/middleware.js
import { NextResponse } from "next/server";
export function middleware(request) {
  // Perform some logic here
  if (request.nextUrl.pathname === "/") {
    return NextResponse.redirect(new URL("/home", request.url));
  }
  return NextResponse.next();
}

You can also specify which paths the middleware should apply to:

// app/middleware.js
export const config = {
  matcher: ["/about/:path*", "/blog/:path*"],
};
11 Explain the concept of authorization in middleware & routes in Next.js. Hard

Authorization in middleware and routes in Next.js involves checking if a user has the necessary permissions to access a specific route or perform an action. This can be done by verifying user roles, permissions, or tokens in the middleware function before allowing access to the route.

// app/middleware.js
import { NextResponse } from "next/server";

export function middleware(request) {
  const token = request.cookies.get("authToken");

  if (!token) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  // Additional authorization logic can go here

  return NextResponse.next();
}

export const config = {
  matcher: ["/protected/:path*"], // Apply middleware to protected routes
};

In this example, the middleware checks for an authentication token in the cookies. If the token is not present, it redirects the user to the login page. If the token is valid, it allows access to the protected routes.

// app/api/protected/route.js
import { NextResponse } from "next/server";
import jwt from "jsonwebtoken";
export async function GET(request) {
  const token = request.cookies.get("authToken");

  if (!token) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    // Handle the request for authorized users
    return NextResponse.json({
      message: "Protected data",
      userId: decoded.userId,
    });
  } catch (error) {
    return NextResponse.json({ error: "Invalid token" }, { status: 401 });
  }
}

In this example, the API route checks for the authentication token in the request cookies. If the token is not present, it returns a 401 Unauthorized response. If the token is valid, it returns the protected data.

12 What are parallel routes in Next.js App Router? Hard

Parallel routes allow you to render multiple pages simultaneously in the same layout. They are defined using slots with the @ convention.

app/
├── layout.js
├── page.js
├── @analytics/
│   └── page.js
└── @team/
    └── page.js
jsx
// app/layout.js
export default function Layout({ children, analytics, team }) {
  return (
    <div>
      <div>{children}</div>
      <div>{analytics}</div>
      <div>{team}</div>
    </div>
  );
}

This allows you to render independent pages that can load at different speeds and handle their own loading and error states.

13 How do you implement intercepting routes in App Router? Hard

Intercepting routes allow you to load a route from another part of your application while keeping the context of the current page, similar to modals.

app/
├── feed/
│   └── page.js
├── photo/
│   └── [id]/
│       └── page.js
└── @modal/
    └── (..)photo/
        └── [id]/
            └── page.js

The (..) convention indicates that you want to intercept routes at the same level. Intercepting routes use conventions like:

  • (.) - match segments on the same level
  • (..) - match segments one level above
  • (..)(..) - match segments two levels above
  • (...) - match segments from the root app directory
14 How do you handle streaming and suspense in App Router? Hard

The App Router has built-in support for streaming and React Suspense, allowing you to progressively render and stream UI to the client.

// app/page.js
import { Suspense } from "react";

async function UserProfile({ userId }) {
  const user = await getUserData(userId); // This can be slow
  return <div>Welcome, {user.name}!</div>;
}

async function UserPosts({ userId }) {
  const posts = await getUserPosts(userId); // This can also be slow
  return (
    <div>
      {posts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  );
}

export default function Page({ params }) {
  return (
    <div>
      <h1>User Dashboard</h1>
      <Suspense fallback={<div>Loading profile...</div>}>
        <UserProfile userId={params.id} />
      </Suspense>
      <Suspense fallback={<div>Loading posts...</div>}>
        <UserPosts userId={params.id} />
      </Suspense>
    </div>
  );
}

Benefits of streaming:

  • Faster initial page load
  • Better perceived performance
  • Progressive enhancement
  • SEO-friendly as search engines can index content as it streams
15 How to use middleware in Next.js API routes? Hard

In Next.js, you can use middleware to run code before your API route handlers. Middleware can be used for tasks like authentication, logging, or modifying requests and responses.

Example of using middleware for authentication:

// app/api/middleware/auth.js
export async function authMiddleware(request) {
  const token = request.headers.get("Authorization");

  if (!token || token !== "your-secret-token") {
    return new Response(JSON.stringify({ error: "Unauthorized" }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  return null; // No error, proceed to the next handler
}

You can then use this middleware in your API route:

// app/api/protected/route.js
import { authMiddleware } from "../middleware/auth";

export async function GET(request) {
  const authError = await authMiddleware(request);
  if (authError) {
    return authError; // Return the error response if unauthorized
  }

  const data = await getProtectedData();
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
}

In this example, the authMiddleware checks for a valid authorization token before allowing access to the protected API route. If the token is invalid or missing, it returns a 401 Unauthorized response.

16 How to implement optimistic UI with the App Router? Hard

Use client components for local state and immediately update UI optimistically, then call a server action or route handler to persist. On success, revalidate server data; on failure, rollback local state.

// client component
const [items, setItems] = useState(serverItems);
function add(item) {
  setItems((prev) => [item, ...prev]); // optimistic
  fetch("/api/add", { method: "POST", body: JSON.stringify(item) })
    .then(() => router.refresh())
    .catch(() => setItems((prev) => prev.filter((i) => i.id !== item.id)));
}

Frequently Asked Questions About Next.js Interviews

What do hiring managers evaluate in Next.js technical rounds?

Technical interviewers look for foundational fluency, idiomatic syntax, clarity when communicating complex logic, and awareness of performance trade-offs (e.g. memory footprint, render performance, and network latency) in production environments.

What are the best interview tips for practicing Next.js questions?

Use active recall: summarize each answer in your own words before revealing the model solution. Focus on explaining why a certain approach is chosen rather than just memorizing code syntax.