Next.js 16 — đánh giá thực tế sau một lần ship production
Turbopack GA là điểm nhấn. Cú lật caching âm thầm mới là rủi ro thật sự. Nâng cấp nếu dev speed là nút cổ chai; giữ nếu phụ thuộc webpack hoặc Edge Middleware.
Bởi Ethan
2.467 từ · 13 phút đọc
Nâng cấp nếu bạn cần vòng lặp dev nhanh hơn và có thể dành nửa ngày kiểm tra lại caching. Giữ nguyên nếu app có custom webpack loaders, Edge Middleware cho auth hoặc geolocation, hoặc monorepo lớn mà RAM build đã là vấn đề. Next.js 16 là bước tiến thực sự — Turbopack GA và React 19.2 mang lại tốc độ thật — nhưng cú lật mô hình caching âm thầm sẽ làm bạn bất ngờ nếu không chủ động kiểm tra.
Bài này dành cho ai
Bạn đang chạy Next.js 14 hoặc 15 và đang cân nhắc có nên nâng cấp ngay hay chờ hệ sinh thái ổn định thêm. Nếu đang bắt đầu dự án mới từ đầu năm 2026, hãy bắt đầu với 16 — mô hình caching dynamic-by-default sẽ rõ ràng hơn khi bạn đã hiểu rõ nó.
Nếu app đang chạy ổn trong production và không có vấn đề hiệu năng, không có lý do gì phải vội. Đây là nâng cấp đáng làm, không phải nâng cấp khẩn cấp.
Những gì đã thay đổi
Bốn thứ quan trọng. Phần còn lại là bảo trì.
Turbopack GA
Turbopack thoát khỏi giai đoạn beta và trở thành bundler dev được khuyến nghị. next dev --turbo đã bị xóa; Turbopack là mặc định. Nếu muốn quay về webpack, truyền --webpack tường minh.
Các con số tốc độ dev là thật. Vercel đo trên codebase thực, không phải payload nhân tạo:
- Dev restart với file-system cache: react.dev tăng 10×, nextjs.org tăng 5×, một app nội bộ lớn tăng 14×
- Server refresh (16.2): 59ms → 12.4ms — giảm 79%
next devstartup (16.2): nhanh hơn ~87% so với 16.1
Cải thiện startup thôi đã thay đổi trải nghiệm. “Chờ nào” trở thành “xong rồi.”
Điểm cần chú ý: tối ưu hóa output của Turbopack chưa ngang bằng webpack. Một số dự án thấy bundle size tăng đáng kể. Chi tiết trong phần gotchas bên dưới. Nếu bạn đang cân nhắc Vite thay cho Turbopack trong project không phải Next.js, xem so sánh Turbopack vs Vite.
Cache Components (experimental)
"use cache" là directive mới đánh dấu page, component, hoặc function để cache giữa các request, độc lập với route-level caching. Cần bật cacheComponents: true trong next.config.ts. Vẫn đang ở giai đoạn experimental, chưa vào stable API. Không nên dùng cho tính năng production, nhưng hãy biết rằng nó đang đến — nó lấp khoảng trống giữa static route và per-request rendering.
proxy.ts
File mới tại app/proxy.ts chạy trên Node.js runtime và xử lý rewrite và redirect trước khi router. Coi nó như một routing layer phía Node.
proxy.ts là sự thay thế chính thức cho middleware.ts — middleware.ts đã bị deprecated và sẽ bị xóa trong phiên bản tương lai. Điểm then chốt: proxy.ts chỉ chạy trên Node.js. Các tính năng Edge runtime (geo headers, V8 isolates) chưa được hỗ trợ trong proxy.ts. Những team đang phụ thuộc vào tính năng edge-specific phải giữ middleware.ts trước mắt và lên kế hoạch migrate khi Next.js ship edge support cho proxy.ts.
React 19.2
Next.js 16 đi kèm React 19.2. Điều quan trọng: tốc độ render RSC nhanh hơn 25–60% tùy độ phức tạp payload, được sửa upstream trong facebook/react#35776. Không cần viết code mới để có được điều này. Tốc độ render trang được cải thiện miễn phí.
Nâng cấp lên Next.js 16
Bắt đầu với codemod chính thức:
npx @next/codemod@latest upgrade
Codemod xử lý phần công việc cơ học:
- Chuyển đổi các lệnh gọi đồng bộ tới
cookies(),headers(),params, vàsearchParamssang async (await cookies(), v.v.) - Cập nhật các import đã đổi tên
- Xóa key
webpackkhỏinext.config.js— build sẽ fail ngay nếu có webpack config trong phiên bản 16
Sau codemod, vẫn còn công việc thủ công.
Route phụ thuộc vào implicit static caching. Next.js 14 mặc định render static. Phiên bản 16 lật default sang dynamic. Bất kỳ route nào được cache ngầm định — vì không bao giờ gọi cookies() và bạn chưa bao giờ đặt dynamic = 'force-dynamic' — có thể sẽ render dynamic theo mỗi request. Không có lỗi. Không có cảnh báo. DB bị gọi nhiều hơn và điểm Lighthouse giảm. Codemod không thể phát hiện điều này. Bạn phải tự so sánh output next build giữa hai phiên bản.
Parallel routes không có default.js. Mỗi thư mục @slot cần có default.js. Trong phiên bản 15, thiếu default.js có thể âm thầm sinh ra 404. Trong phiên bản 16, đây là hard build error. Chạy next build local sau codemod và xử lý từng slot error trước khi push.
Edge Middleware. Codemod không thêm, sửa hay cảnh báo về middleware.ts. middleware.ts đã bị deprecated — proxy.ts là sự thay thế chính thức — nhưng proxy.ts chưa hỗ trợ Edge runtime. Nếu có logic geolocation hoặc auth chạy trên Edge runtime, hãy giữ middleware.ts trước mắt và lên kế hoạch migrate khi edge support có mặt trong proxy.ts.
Custom webpack loaders. Xóa toàn bộ key webpack, hoặc production build sẽ fail. Nếu bạn dùng webpack loaders cho CSS modules, SVG imports, hoặc custom asset transforms, hãy tìm các Turbopack equivalent trước khi nâng cấp.
Timeline thực tế: 2–4 tiếng cho app 30 route không có webpack customization. Dự phòng cả ngày cho app lớn với Edge Middleware, parallel routes, và custom webpack loaders.
Những cạm bẫy cần tránh
Cú lật caching là regression âm thầm
Đây là thứ sẽ đưa lỗi lên production. Model full-dynamic thực ra là mental model tốt hơn — dễ lý luận hơn “static trừ khi được chứng minh ngược lại.” Nhưng con đường migration không có bất kỳ rào chắn nào.
Rủi ro cụ thể: một trang trả về static response cũ trong phiên bản 15 nay thực hiện DB query mới trên mỗi request trong phiên bản 16. Nếu DB chịu được tải thêm, người dùng không nhận ra sự khác biệt. Nếu không, bạn sẽ thấy latency spike mà không có nguyên nhân rõ ràng.
Trước khi merge PR nâng cấp, diff output next build. Mỗi trang chuyển từ ○ Static sang ƒ Dynamic cần một quyết định: dynamic rendering có phải là điều bạn muốn ở đây không, hay bạn cần opt back into caching tường minh với export const dynamic = 'force-static' hoặc unstable_cache?
Bundle size tăng do Turbopack
Turbopack compile nhanh hơn. Nhưng không phải lúc nào cũng tạo ra output nhỏ hơn. Hai trường hợp được cộng đồng báo cáo:
- Một dự án thấy First-load JS nhảy từ 0.57 MB lên 2.6 MB — tăng 4.6× — sau khi chuyển sang Turbopack
- Cal.com báo cáo tăng +72% First-load JS median sau khi bật Turbopack builds
Nguyên nhân gốc: Inner Graph Optimization của Turbopack chưa tương đương với tree-shaking và chunk deduplication của webpack. Code thư viện dùng chung bị nhân đôi trong một số cấu hình.
Điều này không ảnh hưởng đến mọi dự án. Team Turbopack đang tích cực thu hẹp khoảng cách. Nhưng nếu First-load JS là chỉ số quan trọng, chạy bundle analyzer trước và sau khi chuyển đổi. Quay lại --webpack cho production builds là escape hatch được hỗ trợ.
Monorepo lớn chạm giới hạn cứng
Với dự án có 15+ package và hàng nghìn module, hành vi hiện tại của Turbopack:
- Cold compile lần đầu: 15–25 giây (chậm hơn webpack ở lần chạy đầu)
- RAM peak: hơn 10 GB theo các báo cáo cộng đồng
- File-system cache (
.next/cache): có thể phình đến 15 GB nếu không có pruning
Warm cache sau lần compile đầu tiên rất nhanh. Nhưng cold start và RAM ceiling là ràng buộc thực sự trên CI machine lớn. Nếu chạy ephemeral CI containers với giới hạn memory cố định, hãy test Turbopack builds trên CI trước khi cam kết với việc nâng cấp.
Thêm bước xóa cache vào CI pipeline để tránh disk bloat:
# trước mỗi build
rm -rf .next/cache
Hoặc giới hạn cache size trong next.config.js khi option cấu hình đó có mặt trong một point release.
proxy.ts thay thế middleware.ts — nhưng chưa hoàn toàn
proxy.ts là sự thay thế chính thức cho middleware.ts. middleware.ts bị deprecated và sẽ bị xóa trong phiên bản tương lai. Nhưng việc migration không phải là thay thế trực tiếp.
proxy.ts chạy trên Node.js. middleware.ts chạy trên Edge runtime (dựa trên V8, V8 isolates, không có Node APIs). Các tính năng Edge runtime chưa được hỗ trợ trong proxy.ts.
Mạng edge của Vercel inject geo headers (x-vercel-ip-country, x-vercel-ip-city) ở tầng Edge runtime. Đọc các header đó trong middleware.ts hoạt động vì code chạy ở cùng layer. Đọc chúng trong proxy.ts chưa hoạt động theo cách tương tự — chưa.
Nếu middleware.ts của bạn thực hiện bất kỳ thao tác nào sau: JWT verification, cookie inspection, IP-based routing, Cloudflare KV lookups — giữ nguyên middleware.ts cho đến khi team Next.js ship edge support cho proxy.ts. Lên kế hoạch migration, nhưng đừng vội.
Async params — shim tương thích đã bị xóa
Next.js 15 có shim backward-compatibility cho phép truy cập đồng bộ vào params và searchParams trên dynamic route segments. Trong phiên bản 16, shim này bị xóa. Nếu codemod bỏ sót bất kỳ sync access nào (có thể xảy ra trong edge case với custom wrapper), bạn sẽ thấy runtime error trên dynamic routes.
Sau codemod, tìm kiếm đầy đủ các pattern truy cập đồng bộ còn lại:
grep -rn "params\." app/ --include="*.tsx" --include="*.ts" | grep -v "await"
Xem xét từng kết quả — không phải mỗi match đều là bug, nhưng đáng để quét qua.
Số liệu benchmark
Tất cả số liệu đều đã được gắn version và có nguồn. Phương pháp đo là của nhà xuất bản gốc, không được kiểm tra lại ở đây.
Tốc độ dev — Turbopack (nguồn: nextjs.org/blog/next-16-1 cho FS cache restarts; nextjs.org/blog/next-16-2-turbopack cho server refresh; nextjs.org/blog/next-16-2 cho startup)
| Chỉ số | 16.1 | 16.2 | Thay đổi |
|---|---|---|---|
next dev startup | baseline | — | nhanh hơn ~87% |
| Server refresh | 59ms | 12.4ms | −79% |
| Dev restart — react.dev | baseline | — | nhanh hơn 10× (FS cache) |
| Dev restart — nextjs.org | baseline | — | nhanh hơn 5× (FS cache) |
| Dev restart — app nội bộ lớn | baseline | — | nhanh hơn 14× (FS cache) |
RSC rendering — React 19.2 (nguồn: nextjs.org/blog/next-16-2)
| Độ phức tạp payload | Tăng tốc |
|---|---|
| Component đơn giản | 26% |
| Cây component lồng phức tạp | 60% |
Build time — Turbopack vs webpack (báo cáo cộng đồng, nguồn: thảo luận nâng cấp nextjs.org/docs)
| Dự án | webpack | Turbopack | Thay đổi |
|---|---|---|---|
| Báo cáo cộng đồng A | ~10 phút | ~10 giây | −98% |
| Báo cáo cộng đồng B | 797s | 314s | −61% |
Báo cáo build time là từ cộng đồng, không phải Vercel đo. Coi là định hướng.
Rủi ro bundle size (nguồn: github.com/vercel/next.js/discussions/86967, 85744)
| Dự án | Trước | Sau | Thay đổi |
|---|---|---|---|
| Dự án ẩn danh | 0.57 MB | 2.6 MB | +4.6× |
| Cal.com (First-load JS median) | baseline | — | +72% |
Ma trận khuyến nghị
| Tình huống | Khuyến nghị |
|---|---|
| Dự án mới từ đầu năm 2026 | Bắt đầu trên phiên bản 16 — model dynamic-default sạch hơn |
| Đang chạy 14/15, không có custom webpack, không có Edge Middleware auth | Nâng cấp — codemod + audit caching 2–4 tiếng |
| Có custom webpack loaders | Tìm Turbopack equivalent trước; rồi nâng cấp |
| Edge Middleware cho auth hoặc geolocation | Nâng cấp với validation — middleware.ts vẫn hoạt động, nhưng hãy test |
| Monorepo lớn (15+ package, CI có giới hạn memory) | Benchmark RAM Turbopack và cache size trước khi cam kết |
| Bundle size là chỉ số hàng đầu | Chạy bundle analyzer trước khi chuyển đổi — kiểm tra delta First-load JS |
| Đang dùng Vercel Pro hoặc Team | Có — FS cache và dashboard cache-hit theo route giúp lợi ích Turbopack trở nên cụ thể |
| Deploy lên Railway, Render, hoặc Coolify | Build Adapters ổn định từ phiên bản 16.2 — xem so sánh nền tảng hosting toolchew để biết thêm chi tiết |
Kết luận
Nâng cấp nếu tốc độ vòng lặp dev quan trọng với bạn và bạn có nửa ngày để audit cú lật caching. Trải nghiệm dev với Turbopack nhanh hơn rõ rệt khi FS cache đã ấm, và bạn nhận được cải thiện RSC rendering của React 19.2 miễn phí.
Giữ nguyên nếu có custom webpack loaders chưa được thay thế, bundle size là chỉ số load-bearing và bạn chưa benchmark Turbopack output, hoặc monorepo của bạn đã đang làm căng giới hạn CI memory.
Cú lật mô hình caching là phần nguy hiểm nhất của lần nâng cấp này — không phải vì đây là thiết kế tệ, mà vì nó vô hình. Mỗi team bỏ qua việc so sánh next build trước/sau đang đưa một latency regression lên production — regression đó sẽ được chẩn đoán nhiều tuần sau.
Lưu ý
Các số liệu bundle size regression được trích dẫn ở đây đến từ các thread thảo luận cộng đồng, không phải từ benchmark có kiểm soát của toolchew. Nếu hiệu năng production app của bạn phụ thuộc vào bundle size, hãy tự đo — đừng dựa vào trải nghiệm của Cal.com là đại diện cho workload của bạn.
Thay đổi mô hình caching không có công cụ diff tự động. Review thủ công output next build hiện là cách đáng tin cậy duy nhất để phát hiện các route chuyển từ static sang dynamic.
Toolchew có quan hệ affiliate với Vercel. Khuyến nghị Vercel Pro trong ma trận trên dựa trên việc FS cache và các tính năng observability thực sự hữu ích cho Turbopack workloads — không phải dựa trên quan hệ affiliate. Nếu các tính năng đó không mang lại giá trị, dòng đó sẽ không có ở đây.