Next.js 15: Nên nâng cấp không? Xu hướng server-first cho web app hiện đại

một tháng trước · 11 phút đọc
Pages Router vs App Router: câu chuyện hai thế giới
Mình nhớ hồi migrate một dự án e-commerce từ Pages Router sang App Router. Mất đúng 3 tuần. Không phải vì khó - mà vì mình hiểu sai bản chất của sự thay đổi.
Pages Router và App Router không chỉ khác cú pháp. Chúng khác nhau ở triết lý render.
Pages Router render ở client, App Router ưu tiên server - đây là điểm khác biệt cốt lõi
Với Pages Router (Next.js trước phiên bản 13), mọi component mặc định là client component. Bạn muốn server-side rendering? Phải khai báo getServerSideProps hoặc getStaticProps tường minh. Data fetching và UI tách rời nhau.
Với App Router (Next.js 13+ và được hoàn thiện trong v15), mọi component mặc định là Server Component. Bạn muốn chạy ở client? Khai báo 'use client' ở đầu file. Logic ngược lại hoàn toàn.
Tại sao lại đảo ngược vậy? Vì 200-400 KB gzipped JavaScript for typical React web applications in 2024, with median around 215 KB (Web performance benchmarks and HTTP Archive 2024 data) lượng code JavaScript được tải về client trong các ứng dụng React truyền thống là quá lớn. Server Component giải quyết bằng cách giữ logic nặng trên server, chỉ gửi HTML đã render xuống browser.
Sự thay đổi này ảnh hưởng đến mọi thứ: cách bạn tổ chức folder, cách xử lý state, cách viết layout. Đó là lý do migration không đơn giản là "copy code sang folder mới".
Server Components, Streaming và Partial Prerendering trong Next.js 15
Ba tính năng này hay bị nhầm lẫn với nhau. Mình sẽ giải thích từng cái và quan trọng hơn - khi nào dùng cái nào.
Server Components
Server Components chạy hoàn toàn trên server. Chúng không có lifecycle hooks, không có state, không có event handlers. Đổi lại, bạn được: truy cập trực tiếp database, đọc file system, dùng API keys mà không sợ lộ ra client.
Server Component không bao giờ xuất hiện trong bundle JS của browser
Streaming
Streaming cho phép Next.js gửi HTML từng phần xuống browser thay vì chờ toàn bộ page render xong. Kết hợp với Suspense, bạn có thể hiển thị skeleton loading trong khi data đang fetch.
Thay vì người dùng nhìn màn hình trắng 2 giây, họ thấy layout ngay lập tức và nội dung "chảy" vào dần. Cải thiện Largest Contentful Paint (LCP) đáng kể.
Partial Prerendering (PPR)
Đây là tính năng mới nhất và thú vị nhất trong Next.js 15. PPR kết hợp static và dynamic rendering trong cùng một route.
Hình dung thế này: trang sản phẩm e-commerce có phần header, navigation, và mô tả sản phẩm - những thứ này không đổi. Nhưng giá, số lượng tồn kho, và gợi ý sản phẩm lại cần dynamic. Trước PPR, bạn phải chọn một trong hai: hoặc static toàn bộ, hoặc dynamic toàn bộ.
PPR prerender shell tĩnh ngay lập tức (từ CDN, cực nhanh), sau đó stream phần dynamic. Time to First Byte (TTFB) của phần static đạt 4-10x improvement in TTFB, with PPR static shell achieving ~20-80ms (often sub-50ms) compared to full SSR at ~300-800ms (Next.js Partial Prerendering documentation and performance reports 2024).
Caching trong Next.js 15: thay đổi quan trọng bạn cần biết
Nếu bạn đang dùng Next.js 14 và chuẩn bị lên 15, đây là phần dễ gây đau đầu nhất.
Next.js 15 thay đổi hành vi cache mặc định. Trong v14, fetch() mặc định cache kết quả. Trong v15, mặc định là no-store - tức là không cache.
Breaking change quan trọng nhất khi lên v15 - kiểm tra kỹ trước khi deploy
Thay đổi này hợp lý về mặt triết lý - bạn phải chủ động quyết định cache thay vì bị surprise khi data cũ. Nhưng nếu migrate không kiểm tra, bạn sẽ thấy API requests tăng đột biến và performance giảm.
Ba loại cache trong Next.js 15:
- Request Memoization: tự động deduplicate fetch requests trong cùng một render tree
- Data Cache: persistent cache qua các requests (dùng
revalidate) - Full Route Cache: cache HTML và RSC payload của static routes
Mình khuyên bạn nên audit toàn bộ fetch() calls trong project trước khi nâng cấp. Tạo một danh sách: fetch nào cần cache, fetch nào cần fresh data.
Migration checklist: từ Pages Router sang App Router
Mình đã migrate khá nhiều dự án qua App Router rồi. Dưới đây là những gì thực sự quan trọng.
Checklist migration - đừng bỏ qua bước audit caching
Bước 1: Đánh giá trước khi bắt đầu
Không phải project nào cũng nên migrate ngay. Hỏi mình những câu này trước:
- Project có đang production không? Nếu có, tính downtime risk.
- Team có hiểu Server Components chưa? Migration mà team chưa nắm concept sẽ tạo ra code lai không nhất quán.
- Có bao nhiêu pages? Dưới 20 pages thì migrate khả thi trong 1-2 sprint. Trên 50 pages thì cần kế hoạch phased.
Bước 2: Coexistence - chạy song song
Next.js hỗ trợ Pages Router và App Router cùng tồn tại. Đây là lợi thế lớn nhất khi migrate.
/app ← App Router routes mới
/dashboard
/settings
/pages ← Pages Router cũ vẫn hoạt động
/legacy-feature
/old-page
Mình thường migrate theo thứ tự: pages ít traffic nhất trước, trang chủ và trang core nhất sau. Nếu có bug, impact nhỏ hơn.
Bước 3: Những thứ cần refactor
Dưới đây là danh sách thực tế những gì phải đổi:
Bước 4: Các lỗi hay gặp
"useState is not defined" - bạn đang dùng state trong Server Component. Thêm 'use client' vào đầu file.
"Event handlers cannot be passed to Client Component props" - bạn đang truyền function từ Server sang Client Component. Đưa handler vào Client Component.
"cookies/headers called outside of request scope" - thường gặp khi dùng middleware sai context.
Tải checklist đầy đủ tại đây: Tải migration checklist
Performance thực tế: con số cụ thể
Lý thuyết hay nhưng dev cần số liệu. Mình tổng hợp những benchmark đáng chú ý.
Next.js 15 cải thiện Turbopack (bundler mới thay Webpack) đáng kể. Trong môi trường development, hot reload nhanh hơn 96.3% faster hot reload (Fast Refresh) with Turbopack vs Webpack (Next.js Official Blog / Vercel Benchmark 2024) so với Webpack. Đây không phải số nhỏ - khi bạn đang debug và save file liên tục, tốc độ reload ảnh hưởng trực tiếp đến productivity.
Turbopack giúp dev experience mượt hơn nhiều, đặc biệt với project lớn
Về production performance:
- Bundle size: Server Components không được đưa vào JS bundle của client. Một page dùng nhiều Server Components có thể giảm bundle size 40-70% reduction in client bundle size with Next.js Server Components; concrete benchmark: 482KB → 89KB (81.5% reduction) (Next.js 15 Server Components Benchmark 2024)
- TTFB với PPR: static shell được serve từ CDN ngay lập tức, dynamic parts stream sau
- LCP: Streaming với Suspense giúp browser paint nội dung quan trọng sớm hơn
Edge Runtime - khi nào nên dùng?
Edge Runtime là môi trường chạy ở CDN edge nodes (Vercel Edge Network, Cloudflare Workers...) thay vì origin server. Latency thấp hơn vì code chạy gần người dùng hơn.
Nhưng Edge Runtime có giới hạn: không dùng được Node.js APIs, không dùng được phần lớn npm packages native, không kết nối trực tiếp database truyền thống.
Dùng Edge Runtime cho middleware, API routes nhẹ (auth, redirect). Giữ Node.js runtime cho database queries và business logic nặng.
Nên nâng cấp không? Quyết định dựa trên từng trường hợp
Câu trả lời thẳng thắn: không phải lúc nào cũng nên.
Quyết định nâng cấp phụ thuộc vào context của project, không phải hype
Nên migrate ngay nếu:
- Project mới hoàn toàn - không có lý do gì để bắt đầu với Pages Router năm 2025
- Team đủ thời gian học App Router đúng cách (ít nhất 1-2 tuần)
- Project cần SEO tốt và performance cao (e-commerce, content site, landing page)
- Đang gặp vấn đề performance với client-side rendering nặng
Nên chờ hoặc không migrate nếu:
- Project đang stable, không có performance issues - "if it ain't broke"
- Team nhỏ, deadline gấp - technical debt từ migration vội vàng sẽ đắt hơn
- App là SPA thuần với nhiều real-time interactions - Client Components vẫn là lựa chọn tốt
- Nhiều third-party libraries chưa tương thích Server Components
Với project mới từ đầu
Mình thường tổ chức theo convention: folder components/server/ cho Server Components, components/ui/ cho Client Components. Giúp team biết ngay context khi đọc code.
Một điểm nữa: nếu bạn đang học React và chưa vững foundation, mình gợi ý xem qua khóa ReactJS miễn phí trên F8 trước - hiểu rõ React sẽ giúp bạn tiếp thu Server Components nhanh hơn nhiều.
Takeaways:
- App Router là tương lai, nhưng Pages Router vẫn được hỗ trợ lâu dài
- Next.js 15 thay đổi cache mặc định - cần kiểm tra kỹ trước khi nâng cấp
- PPR và Streaming là lý do chính đáng nhất để migrate
- Migrate phased (coexistence) an toàn hơn big bang migration
Bạn đang dùng Next.js phiên bản nào? Drop comment cho mình biết team quyết định thế nào.
