Luyện Phỏng Vấn IT — 2000+ Câu Hỏi Phỏng Vấn IT Có Đáp Án 2026
Next.js
Trong thư mục app/, mỗi thư mục đại diện cho một route segment. page.tsx là component hiển thị cho route đó. [param] là dynamic segment. (group) là route group không ảnh hưởng URL. [...slug] là catch-all.
File conventions: page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx.
Export metadata object hoặc generateMetadata function từ page.tsx hoặc layout.tsx.
- Tự động merge metadata từ parent layouts.
export const metadata = { title: 'Page Title', description: '...' }.generateMetadatalà async function nhận params để tạo dynamic metadata cho dynamic routes. - Thay thế cho next/head.
'use client' đặt ở đầu file để đánh dấu component là Client Component.
- Cần thiết khi component dùng: useState, useEffect, event handlers, browser APIs.
- Đặt boundary tại component cần interactivity, không cần wrap toàn app.
// components/like-button.tsx
'use client' // ← bắt buộc vì dùng useState và onClick
import { useState } from 'react'
export function LikeButton({ initialCount }: { initialCount: number }) {
const [count, setCount] = useState(initialCount)
return (
<button onClick={() => setCount(c => c + 1)}>
❤️ {count}
</button>
)
}
// app/post/page.tsx — Server Component, không cần 'use client'
import { LikeButton } from '@/components/like-button'
export default async function PostPage() {
const post = await fetchPost()
return (
<article>
<h1>{post.title}</h1>
{/* Server Component chứa Client Component — OK */}
<LikeButton initialCount={post.likes} />
</article>
)
}server-only package throw error tại build time nếu vô tình import server code vào client bundle. client-only throw nếu import client code trên server.
- Dùng:
import 'server-only'đầu file chứa database connections, API keys. - Giúp catch mistakes sớm thay vì runtime errors hay security issues.
SSR render HTML trên server cho mỗi request, trả về fully-rendered HTML cho browser. Tốt cho: SEO, trang có data thay đổi thường xuyên theo user, first paint nhanh. Trong App Router: mặc định Server Components là SSR. Pages Router: dùng getServerSideProps.
Nhược điểm: server phải xử lý nhiều, TTFB cao hơn SSG.
SSG tạo HTML tại build time, serve static files từ CDN. Cực kỳ nhanh, không cần server processing mỗi request. Phù hợp cho: blog, documentation, marketing pages, data ít thay đổi. Pages Router: không có getServerSideProps. App Router: dùng export const dynamic = 'force-static'.
Nhược điểm: phải rebuild để cập nhật content.
getStaticProps chạy tại build time, data embed vào static HTML, tốc độ cực nhanh, tốt cho data ít thay đổi. getServerSideProps chạy mỗi request, data luôn fresh, nhưng chậm hơn vì phải đợi server.
- Cả hai chỉ có trong Pages Router.
- App Router thay thế bằng Server Components với fetch cache options.
Route Handlers là cách tạo API endpoints trong App Router bằng cách đặt file route.ts trong thư mục app/.
- Mỗi HTTP method được export dưới dạng named function riêng biệt.
- Sử dụng Web APIs chuẩn (Request/Response) thay vì req/res của Node.js.
// app/api/users/route.ts → endpoint: /api/users
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const page = Number(searchParams.get('page') ?? 1)
const users = await db.users.findMany({ skip: (page - 1) * 10, take: 10 })
return NextResponse.json(users)
}
export async function POST(request: NextRequest) {
const body = await request.json()
const user = await db.users.create({ data: body })
return NextResponse.json(user, { status: 201 })
}App Router (Next.js 13+) dùng thư mục app/, hỗ trợ React Server Components mặc định, có nested layouts, loading/error states tích hợp.
- Pages Router dùng thư mục
pages/, mọi component là Client Component, có getStaticProps/getServerSideProps. - App Router là tương lai của Next.js với nhiều tính năng mới.
Layout là UI chia sẻ giữa nhiều pages, preserve state khi navigate (không remount). layout.tsx nhận children prop.
- Nested layouts: layout lồng trong layout tạo hierarchy.
- Root layout (
app/layout.tsx) bắt buộc phải có, wrap toàn app. - Khác với template.tsx: template remount mỗi navigate.
loading.tsx tự động tạo Suspense boundary bọc page hoặc layout, hiển thị loading UI ngay lập tức khi navigate.
- Streaming: Next.js stream HTML từng phần, user thấy nội dung nhanh hơn thay vì đợi toàn trang.
- Instant loading states không cần manually thêm Suspense và fallback UI.
error.tsx phải là Client Component, nhận error và reset props, hiển thị khi có lỗi trong segment. not-found.tsx hiển thị khi gọi notFound() từ server component hoặc khi URL không match route nào.
Global error.tsx ở root app/ bắt lỗi kể cả trong root layout.
Route Groups dùng ngoặc đơn (groupName) để tổ chức routes mà không ảnh hưởng URL.
Ví dụ: (auth)/login và (auth)/register share cùng layout auth nhưng URL vẫn là /login và /register. Useful để: apply layout cho subset of routes, organize large codebases theo features.
Dynamic routes trong App Router sử dụng cú pháp folder name trong ngoặc vuông, ví dụ app/blog/[slug]/page.tsx sẽ match URL /blog/anything. Next.js 15: params là Promise — cần const { slug } = await params trong async Server Component, hoặc React.use(params) trong Client Component.
- Catch-all routes dùng app/[...slug]/page.tsx để match nhiều segments như /a/b/c (params.slug là mảng).
- Optional catch-all app/[[...slug]]/page.tsx còn match root route.
- TypeScript:
{ params: Promise<{ slug: string }> }cho dynamic route.
generateStaticParams export từ dynamic route page để pre-generate static paths tại build time, tương đương getStaticPaths trong Pages Router. export async function generateStaticParams() { return posts.map(p => ({ slug: p.slug })) }.
Kết hợp với Static Generation để tạo static pages cho mọi possible value.
Server Components render hoàn toàn trên server, HTML được gửi xuống client.
- Không có JavaScript bundle gửi xuống client.
- Có thể trực tiếp access database, file system, server-only APIs.
- Không hỗ trợ: state, effects, event handlers, browser APIs.
- Mặc định trong App Router.
// app/products/page.tsx — Server Component (mặc định)
// Có thể dùng async/await trực tiếp, access DB, không cần useEffect
async function ProductsPage() {
// Chạy trên server, không ship JS xuống client
const products = await db.products.findMany()
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name} — {p.price} VND</li>
))}
</ul>
)
}
export default ProductsPageServer Component có thể import và render Client Component - đây là hướng dependency bình thường.
- Ngược lại: Client Component KHÔNG thể import Server Component vì server component sẽ bị client-ize.
- Nhưng Server Component có thể truyền Server Component làm children/props cho Client Component (pattern được phép).
Server Component khi cần: data fetching, access backend resources, keep sensitive info (API keys), reduce client bundle.
- Client Component khi cần: onClick, onChange event handlers, useState/useEffect, browser APIs, real-time updates, custom hooks.
- Default là Server, chỉ dùng 'use client' khi thực sự cần interactivity.
Context API không hoạt động trong Server Components vì context phụ thuộc React runtime trên client.
- Chỉ Client Components mới có thể dùng Context.
- Để share data trong Server Components, truyền qua props hoặc dùng Next.js built-ins.
- Tạo Context Provider là Client Component và wrap children.
// providers/theme-provider.tsx — phải là Client Component
'use client'
import { createContext, useContext, useState } from 'react'
const ThemeContext = createContext<'light' | 'dark'>('light')
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
return (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
)
}
export const useTheme = () => useContext(ThemeContext)
// app/layout.tsx — Server Component bọc Provider
import { ThemeProvider } from '@/providers/theme-provider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}Nhiều npm packages cũ dùng browser APIs hay React client features mà không có 'use client'.
- Giải pháp: tạo wrapper Client Component import library đó, thêm 'use client' ở wrapper.
- Next.js có boundary tự động cho packages với 'use client' trong package.json exports.
- Kiểm tra compatibility trước khi dùng library mới.
ISR kết hợp SSG và SSR: trang được generate tĩnh, nhưng sau revalidate seconds có thể regenerate lại. User đầu tiên sau revalidate nhận stale content, trigger background regeneration; user sau nhận fresh content.
- Pages Router:
return { revalidate: 60 }trong getStaticProps. - App Router:
export const revalidate = 60hoặcfetch(url, { next: { revalidate: 60 } }).
On-demand revalidation cho phép invalidate cache và regenerate pages ngay lập tức thay vì đợi timeout.
- Pages Router:
res.revalidate('/path')trong API route. - App Router:
revalidatePath('/path')hoặcrevalidateTag('posts')trong Server Action hoặc Route Handler.
Phù hợp khi CMS webhook trigger cần update content ngay.
Next.js extend native fetch với caching:
fetch(url)mặc địnhno-storetừ Next.js 15+ (trước đó cache).{ cache: 'force-cache' }để opt-in cache.{ next: { revalidate: 60 } }revalidate sau 60 giây.{ next: { tags: ['posts'] } }tag để invalidate theo tag.
Data cache tách biệt với Full Route Cache.
- Static rendering: render lúc build time, cache và serve từ CDN.
- Dynamic rendering: render mỗi request, không cache.
Tự động dynamic khi dùng: await cookies(), await headers(), searchParams, hay fetch với cache: 'no-store'. Explicit: export const dynamic = 'force-dynamic' hoặc 'force-static'. Opt-in static càng nhiều càng tốt cho performance.
Streaming SSR gửi HTML từng phần xuống browser ngay khi ready thay vì đợi toàn trang.
- Dùng HTTP chunked transfer encoding.
- Kết hợp với Suspense: phần ngoài Suspense stream trước, phần trong stream khi data ready.
- Giảm Time to First Byte và First Contentful Paint.
- Enabled mặc định trong App Router.
Server Actions là async functions chạy trên server, được gọi từ Client Components hoặc forms.
- Khai báo với
'use server'directive. - Cho phép mutate data trực tiếp mà không cần tạo API endpoint riêng.
// actions/post-actions.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
await db.posts.create({ title })
revalidatePath('/posts') // làm mới cache trang sau khi mutate
}
// components/new-post-form.tsx
'use client'
import { createPost } from '@/actions/post-actions'
export function NewPostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="Post title" />
<button type="submit">Create</button>
</form>
)
}Gán Server Action vào form action attribute: <form action={createPost}>.
- Form submit tự động gọi Server Action với FormData.
- Không cần JavaScript client-side (progressive enhancement).
- Trong action:
const title = formData.get('title'). - Kết hợp với useFormState (React 18) hoặc useActionState (React 19) để track state.
Sau khi mutate data trong Server Action, cần revalidate cache để UI cập nhật: revalidatePath('/posts') invalidate cached route, revalidateTag('posts') invalidate theo tag. redirect('/path') để điều hướng sau action thành công.
Server Actions có thể throw errors sẽ được catch bởi nearest error.tsx.
- Với useFormState/useActionState, return error object thay vì throw:
return { error: 'Invalid input' }. - Dùng try/catch trong action để handle expected errors (validation, not found) và return user-friendly error states.
Middleware là middleware.ts ở root, chạy trên Edge runtime trước mọi request.
- Dùng để: authentication/authorization, redirect, rewrite URL, add/modify headers, A/B testing.
- Return NextResponse để điều khiển response.
- Dùng
matcherconfig để chỉ định routes áp dụng. - Nhẹ và nhanh vì Edge runtime.
Import từ next/headers, cả hai đều là async kể từ Next.js 15+: const cookieStore = await cookies() và const headersList = await headers().
- Đọc cookie:
cookieStore.get('session'),cookieStore.set('session', value). - Đọc header:
headersList.get('user-agent'). - Trong Server Components: read-only.
- Trong Server Actions và Route Handlers: có thể read và write.
useOptimistic (React 19) kết hợp với Server Actions: cập nhật UI ngay trước khi action hoàn thành, rollback nếu fail. const [optimistic, addOptimistic] = useOptimistic(data, updateFn).
- Trong action handler:
addOptimistic(newItem)trướcawait serverAction(). - Tạo snappy UX dù có network latency.
next/image tự động: resize và optimize images theo viewport, serve WebP/AVIF modern formats, lazy load mặc định, prevent Cumulative Layout Shift (CLS) với width/height hoặc fill.
- Cần cung cấp width và height hoặc fill prop.
- Cấu hình allowed domains trong next.config.
- Tích hợp CDN support sẵn.
next/font tự động: download Google Fonts tại build time (không request runtime đến Google), host fonts trên cùng server, zero layout shift với automatic size adjustment, privacy tốt hơn.
- Dùng:
import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'] }). - Apply qua className.
- Hỗ trợ local fonts qua next/font/local.
Next.js tự động prefetch routes khi Link component vào viewport (production).
- Tải JS chunk và data cho route đó trước khi user click, tạo instant navigation.
- Disable:
<Link prefetch={false}>. - App Router prefetch RSC payload.
- Control cụ thể: prefetch attribute hoặc router.prefetch().
- Giúp navigation cảm giác instantaneous.
next/script tối ưu loading third-party scripts.
- Strategies:
beforeInteractive(trước hydration, cho critical scripts),afterInteractive(sau hydration, default),lazyOnload(idle time),worker(web worker với Partytown). - Tránh dùng thẻ script thông thường trong App Router, dùng next/script thay thế.
Middleware có thể rewrite requests đến API khác: NextResponse.rewrite(new URL('/api/proxy', request.url)).
- Hoặc trong next.config.js dùng
rewritesarray để proxy:{ source: '/api/:path', destination: 'https://backend.com/:path' }. - Hữu ích để: hide API endpoints, implement BFF (Backend For Frontend), avoid CORS issues.
Next.js tối ưu: LCP (Largest Contentful Paint) qua image optimization, priority images; CLS (Cumulative Layout Shift) qua next/image với dimensions, next/font với size-adjust; INP (Interaction to Next Paint, thay FID từ 3/2024) qua code splitting, lazy loading, Concurrent Mode.
Dùng @vercel/analytics (không phải next/analytics) để báo cáo Core Web Vitals production.
.env.local cho local development (git-ignored). .env.production, .env.development cho môi trường cụ thể. NEXT_PUBLIC_ prefix expose biến xuống client bundle - KHÔNG đặt secrets với prefix này.
- Không có prefix: chỉ server-side.
- Trong App Router: server components đọc trực tiếp
process.env.VAR. - Client: chỉ NEXT_PUBLIC_ vars.
Parallel Routes dùng @slot convention để render nhiều pages cùng lúc trong cùng layout (ví dụ modal + page).
- Intercepting Routes (
(.)path,(..)path) cho phép intercept route và hiển thị trong context hiện tại (như Instagram photo modal). - Phù hợp cho modal workflows phức tạp không mất URL sharability.
Props từ Server sang Client Component phải serializable (có thể convert to JSON): strings, numbers, booleans, arrays, plain objects.
- Không thể truyền: functions, class instances, Symbols, Date objects (trực tiếp), React elements cũng có hạn chế.
- JSX làm children có thể pass.
- Cần serialize Date thành string trước khi pass.
React Taint API (experimental) cho phép đánh dấu objects và values là sensitive để ngăn vô tình pass xuống Client Components. taintObjectReference(message, object) và taintUniqueValue(message, object, value).
Ví dụ: taint user password, API keys. Giúp prevent server-side secrets leak xuống client bundle.
Next.js stream HTML theo từng phần thay vì đợi toàn page ready.
- Server components có thể wrap trong Suspense, phần xung quanh stream trước, khi data ready phần trong Suspense stream tiếp.
- User thấy content nhanh hơn.
- TTFB tốt hơn.
- Cần design components để render độc lập và không block lẫn nhau.
Next.js có 4 loại cache:
- (1) Request Memoization: dedup fetch calls trong cùng request tree.
- (2) Data Cache: persist fetch results giữa requests và deployments.
- (3) Full Route Cache: cache rendered HTML và RSC payload trên server.
- (4) Router Cache: client-side cache của RSC payload, prefetch routes.
Partial Prerendering (experimental, Next.js 14) cho phép combine static shell và dynamic holes trong cùng một page.
- Static parts được prerender và cached, dynamic parts (trong Suspense) được stream khi request.
- User nhận static shell ngay lập tức, dynamic content load sau.
- Tốt nhất của SSG và SSR trong một trang.
Server Actions tự động có CSRF protection từ Next.js.
- Tuy nhiên vẫn cần: validate và sanitize inputs (không trust FormData), authenticate user trong action (check session), authorize permissions, rate limiting.
- Server Actions là public API endpoints dù không có URL, nên treat như REST endpoints về security.
Node.js Runtime: đầy đủ Node.js APIs, có thể dùng npm packages, phù hợp cho heavy computation.
- Edge Runtime: V8 engine không có Node.js APIs, giới hạn package support, nhưng cực kỳ nhanh (chạy gần user), cold start gần 0.
- Middleware mặc định Edge.
- Route Handlers có thể chọn:
export const runtime = 'edge'hoặc'nodejs'.
Dùng @next/bundle-analyzer để visualize bundle size.
- Cấu hình trong next.config.js với withBundleAnalyzer wrapper.
- Chạy
ANALYZE=true npm run buildđể tạo report HTML. - Giúp identify: large dependencies, duplicate code, unnecessary imports.
- Sau đó tối ưu: dynamic imports, tree-shaking, replace heavy libraries.
Turbopack là Rust-based bundler thay thế Webpack trong Next.js (stable cho dev trong Next.js 15).
- Nhanh hơn đáng kể cho local development: incremental builds, chỉ compile những gì thay đổi.
- HMR (Hot Module Replacement) cực nhanh ngay cả với large codebases.
- Production bundling vẫn đang trong development.
App Router: không có built-in i18n, dùng middleware để detect locale từ Accept-Language header, redirect đến locale-prefixed routes /en/about.
- Dùng libraries: next-intl, next-i18next.
- Tạo
[locale]dynamic segment, load messages tương ứng. - Pages Router có built-in i18n config với
i18n: { locales, defaultLocale }trong next.config.