Key Features of Next.js
In an interview this question checks how deeply you know the framework, not just whether you have heard of it. It is not enough to list "SSR, routing, optimization". You need to understand how each feature works and why it exists.
Let's go through the main features of Next.js one by one.
Hybrid Rendering
The main strength of Next.js is that you choose the rendering strategy for each page yourself. Different pages in the same app can use different approaches:
- SSG static, generated at build time. For pages that rarely change.
- SSR rendered on the server on each request. For dynamic content.
- ISR static with background revalidation. A compromise between SSG and SSR.
- CSR client-side rendering, just like a regular React app.
// "About" page on Hack Frontend — static (SSG by default)
export default async function AboutPage() {
const info = await fetch('https://api.hackfrontend.com/about')
const data = await info.json()
return <About data={data} />
}
// Problems feed — updates every 5 minutes (ISR)
export default async function ProblemsPage() {
const res = await fetch('https://api.hackfrontend.com/problems', {
next: { revalidate: 300 }
})
const problems = await res.json()
return <ProblemList problems={problems} />
}
Interview tip:
A common follow-up: "When would you choose SSR over SSG?". A good answer: SSR for pages with personalized content (profile, dashboard), SSG for content that is the same for everyone (blog, documentation).
App Router and File-Based Routing
Starting from version 13, Next.js uses the App Router. The folder structure inside the app directory directly defines your routes:
Each folder is a URL segment. Special files have specific roles:
| File | Purpose |
|---|---|
page.tsx | Page UI |
layout.tsx | Shared wrapper for the page and nested routes |
loading.tsx | Loading UI (works with Suspense) |
error.tsx | Error UI (works with Error Boundary) |
not-found.tsx | 404 UI |
// app/docs/[slug]/page.tsx
// Dynamic route: /docs/javascript, /docs/react, etc.
export default async function DocPage({
params
}: {
params: { slug: string }
}) {
const doc = await getDocument(params.slug)
return <article>{doc.content}</article>
}
Server Components
In the App Router, components are server components by default. This means:
- They run only on the server
- Their code is not included in the client bundle
- They can directly access the database, file system, environment variables
// Server component — code is not sent to the browser
import { db } from '@/lib/db'
export default async function UserStats() {
const totalUsers = await db.user.count()
const totalProblems = await db.problem.count()
return (
<div>
<p>Users on Hack Frontend: {totalUsers}</p>
<p>Problems: {totalProblems}</p>
</div>
)
}
When you need interactivity, mark the component as a client component:
'use client'
import { useState } from 'react'
export default function ThemeToggle() {
const [dark, setDark] = useState(false)
return (
<button onClick={() => setDark(!dark)}>
{dark ? 'Light mode' : 'Dark mode'}
</button>
)
}
Important:
Server components cannot use useState, useEffect, onClick and other client APIs. If you need interactivity, extract it into a separate client component.
Server Actions
Server Actions let you call server functions directly from components without creating API endpoints:
// actions/subscribe.ts
'use server'
import { db } from '@/lib/db'
export async function subscribe(email: string) {
await db.subscriber.create({
data: { email }
})
}
// Client component calls a server function
'use client'
import { subscribe } from '@/actions/subscribe'
export default function SubscribeForm() {
return (
<form action={async (formData) => {
const email = formData.get('email') as string
await subscribe(email)
}}>
<input name="email" type="email" placeholder="Email" />
<button type="submit">Subscribe to Hack Frontend</button>
</form>
)
}
Nested Layouts
Layouts in Next.js preserve state across navigations. If you have a sidebar or navigation, they do not re-render when navigating between pages:
// app/docs/layout.tsx
// This layout wraps all /docs/* pages
export default function DocsLayout({
children
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
)
}
When navigating from /docs/javascript to /docs/react, the layout with the sidebar stays in place. Only the children content re-renders.
Built-in Optimization
Image Component
Automatically optimizes images: resizes, converts to WebP/AVIF, adds lazy loading:
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="/avatar.png"
width={64}
height={64}
alt="User avatar"
/>
)
}
Link Component
Prefetches the page in the background when the link enters the viewport. Navigation becomes nearly instant:
import Link from 'next/link'
export default function Nav() {
return (
<nav>
<Link href="/problems">Problems</Link>
<Link href="/docs">Documentation</Link>
</nav>
)
}
Font Optimization
next/font loads fonts without layout shift and without client-side requests to Google Fonts:
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin', 'cyrillic'] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html className={inter.className}>
<body>{children}</body>
</html>
)
}
Middleware
Middleware runs before a request is processed and lets you redirect, rewrite URLs or modify headers:
// middleware.ts (at the project root)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const locale = request.cookies.get('locale')?.value || 'en'
if (!request.nextUrl.pathname.startsWith(`/${locale}`)) {
return NextResponse.redirect(
new URL(`/${locale}${request.nextUrl.pathname}`, request.url)
)
}
}
export const config = {
matcher: ['/((?!api|_next|favicon).*)']
}
Built-in Metadata Support
Next.js provides a convenient API for managing SEO metadata:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'JavaScript Problems — Hack Frontend',
description: 'Solve problems from real frontend interviews',
openGraph: {
title: 'JavaScript Problems',
description: 'Solve problems from real frontend interviews',
type: 'website'
}
}
export default function ProblemsPage() {
return <ProblemsList />
}
The Full Picture
How to answer in an interview:
Do not list every feature. Pick 3-4 key ones and explain how they connect. For example: "Next.js uses the App Router with file-based routing where components are server components by default, which reduces the client bundle and gives direct data access without an API layer via Server Actions".
Useful Resources
- nextjs.org/docs — official Next.js documentation
- nextjs.org/learn — interactive course by Vercel
- nextjs.org/blog — blog with release announcements