React Server Components performance: khi nào nên dùng?

Nội dung

React Server Components performance không phải phép màu. Bài này giúp bạn biết khi nào RSC giảm JavaScript thật, khi nào làm kiến trúc phức tạp hơn và cách migration từng phần.

Bạn nghe nói React Server Components giúp website nhanh hơn, nhưng khi đưa vào dự án thật lại thấy thêm boundary, cache rule, streaming, data fetching và lỗi hydration? Đây là cảm giác rất phổ biến.

React Server Components performance tốt khi nó giảm JavaScript phía client và đưa việc đọc dữ liệu về server đúng chỗ. Nhưng RSC không tự sửa LCP, INP hay backend chậm. Bài này giúp bạn nhìn đúng trade-off trước khi migration. Cùng mổ xẻ nhé!

React Server Components performance với server client và cache
React Server Components performance phụ thuộc ranh giới server-client, cache và lượng JavaScript gửi xuống trình duyệt.

Tóm tắt nhanh

  • RSC nhanh hơn khi giúp giảm client JavaScript, tránh fetch phụ sau khi trang đã load và giữ logic nặng ở server.
  • RSC không cứu được LCP nếu ảnh hero, TTFB, font, CDN hoặc backend latency đang là bottleneck chính.
  • Boundary "use client" rất quan trọng: đánh dấu sai có thể kéo nhiều code vào client bundle.
  • Cache, streaming và data fetching quyết định trải nghiệm thật nhiều hơn việc chỉ đổi component sang server.
  • Migration nên theo route hoặc section, đo bundle, LCP, INP và request waterfall trước/sau.

React Server Components performance nhanh hơn ở đâu?

React Server Components performance cải thiện rõ nhất khi component không cần interactivity nhưng đang kéo nhiều JavaScript hoặc fetch dữ liệu ở client. Nói ngắn gọn: phần nào chỉ đọc dữ liệu và render UI tĩnh tương đối, phần đó có khả năng hợp với Server Component.

Tài liệu React mô tả Server Components là loại component render trước khi bundle, trong môi trường tách khỏi client app hoặc SSR server [1]. React cũng nêu ví dụ thư viện markdown/sanitize có thể không nằm trong bundle client khi render bằng Server Components. Điều này quan trọng vì JavaScript tải xuống, parse và execute đều tiêu tốn thời gian trên thiết bị người dùng.

Next.js App Router mặc định dùng Server Components cho layouts và pages. Tài liệu Next.js ghi rõ Server Components cho phép fetch data, render một phần UI trên server, tùy chọn cache kết quả và stream xuống client [2]. Đây là lý do nhiều dự án thấy RSC hợp với trang blog, listing, product detail, dashboard đọc dữ liệu và trang marketing có nội dung từ CMS.

Nhưng cần nói thẳng: RSC không đồng nghĩa “server render cũ” và cũng không phải thay thế Client Components. Server Components không dùng được API tương tác như state trong browser. Khi cần click handler, localStorage, custom hook client-side hoặc browser API, bạn vẫn cần Client Component.

Ba lợi ích performance thường thấy gồm:

  • Giảm JavaScript client: code chỉ chạy ở server không phải gửi toàn bộ xuống browser.
  • Giảm waterfall fetch: server có thể đọc dữ liệu trong lúc render thay vì chờ browser tải JS rồi mới gọi API.
  • Đặt logic gần data: truy vấn CMS, database hoặc backend nội bộ có thể nằm server-side, dễ cache và kiểm soát hơn.

Bạn đang đọc bài viết thuộc chuyên mục Lập trình của VietnamTutor — nơi mình phân tích frontend theo hướng thực chiến, đo được và tránh chạy theo buzzword.

React Server Components performance giảm JavaScript và waterfall fetch
RSC có lợi khi giảm JavaScript client và tránh fetch phụ sau khi trang load.

Khi nào RSC không cứu được LCP và INP?

RSC không tự cải thiện Core Web Vitals nếu bottleneck chính nằm ngoài component model. LCP vẫn có thể chậm vì ảnh hero nặng, TTFB cao, CDN cấu hình sai, font blocking hoặc backend query lâu.

web.dev nhấn mạnh code splitting có thể cải thiện performance bằng cách giảm bandwidth và CPU contention, đặc biệt với tốc độ load ban đầu và input responsiveness [5]. RSC giúp theo hướng tương tự khi giảm JS client, nhưng nếu route vẫn cần nhiều Client Components lớn, lợi ích có thể nhỏ hơn kỳ vọng.

Ví dụ, một trang sản phẩm có hero image 1.8 MB, API sản phẩm mất 900 ms và font tải chậm sẽ không nhanh lên chỉ vì bạn chuyển phần mô tả sản phẩm sang Server Component. Bạn cần đo waterfall để biết điểm nghẽn thật.

Với INP, vấn đề thường nằm ở JavaScript chạy trên main thread sau khi người dùng tương tác: bundle lớn, handler nặng, chart phức tạp, table client-side filter hoặc third-party script. RSC có thể giảm JS ban đầu, nhưng phần tương tác vẫn cần tối ưu riêng.

Nếu bạn chưa có baseline, hãy đo trước khi migration:

  • LCP element là ảnh, text hay block render từ dữ liệu?
  • TTFB có cao không?
  • Client bundle route hiện tại bao nhiêu KB?
  • Có API waterfall sau hydration không?
  • INP bị ảnh hưởng bởi handler nào?

Nếu điểm nghẽn nằm ở ảnh hoặc cache, đọc thêm tối ưu ảnh cải thiện LCPCloudflare Cache Rules cho WordPress. Khác stack, nhưng cách tư duy đo bottleneck giống nhau.

React Server Components performance không tự sửa LCP INP nếu bottleneck là ảnh backend hoặc JavaScript client
RSC chỉ là một đòn tối ưu; LCP và INP vẫn cần đo bottleneck thật.

use client boundary ảnh hưởng bundle size thế nào?

Boundary "use client" quyết định phần nào của cây component bị đưa vào client bundle. Đặt boundary quá cao có thể làm nhiều code đáng lẽ chạy server bị kéo xuống browser.

Tài liệu Next.js giải thích "use client" tạo ranh giới giữa Server và Client module graph. Khi một file được đánh dấu "use client", tất cả import và child component của nó được xem là một phần của client bundle [2]. Đây là chi tiết nhỏ nhưng ảnh hưởng lớn.

Ví dụ, nếu bạn đặt "use client" ở layout chứa cả header, menu, product grid, recommendation và footer, bạn có thể vô tình đưa nhiều logic render tĩnh xuống client. Cách tốt hơn là đẩy Client Component xuống sát phần cần tương tác: nút mở menu, bộ lọc, modal, form hoặc carousel.

// Server Component: đọc dữ liệu và render phần tĩnh
export default async function ProductPage() {
  const product = await getProduct();

  return (
    <main>
      <ProductInfo product={product} />
      <AddToCartButton productId={product.id} />
    </main>
  );
}
"use client";

// Client Component: chỉ giữ phần cần tương tác
export function AddToCartButton({ productId }: { productId: string }) {
  return <button onClick={() => addToCart(productId)}>Thêm vào giỏ</button>;
}

Thú vị nhỉ: performance ở đây không đến từ việc “mọi thứ là server”, mà từ việc ranh giới đủ nhỏ. Server render phần đọc dữ liệu, client xử lý phần người dùng bấm.

use client boundary ảnh hưởng bundle size trong React Server Components
Boundary càng cao, nguy cơ kéo nhiều code vào client bundle càng lớn.

Cache, streaming và data fetching quyết định gì?

Trong dự án RSC, cache và data fetching thường quyết định trải nghiệm thật nhiều hơn việc component nằm server hay client. Server Component vẫn chậm nếu mỗi request đều chờ database hoặc API ngoài quá lâu.

Next.js có nhiều lớp cache và revalidation. Tài liệu caching của Next.js ghi rằng fetch mặc định không được cache trong model cũ, nhưng có thể cache request riêng bằng cache: "force-cache"; đồng thời có revalidation theo thời gian, tag và path [3]. Với Next.js 16, tài liệu cũng nhắc Cache Components là hướng mới, nên bạn cần kiểm tra version/framework config trước khi áp dụng công thức cũ.

RSC hợp với dữ liệu có thể cache: nội dung CMS, danh mục sản phẩm, pricing ít đổi, navigation, footer, bài viết, case study. Ngược lại, dữ liệu cá nhân hóa theo user, dashboard realtime hoặc trạng thái giỏ hàng cần chiến lược khác.

Streaming giúp browser nhận từng phần UI sớm hơn, nhưng không thay thế cache. Nếu phần đầu trang phụ thuộc API chậm, streaming phần dưới không làm LCP tốt hơn. Bạn nên thiết kế route để phần above-the-fold đọc dữ liệu nhanh, cache được hoặc có skeleton hợp lý.

Một checklist data fetching đơn giản:

  • Dữ liệu này có giống nhau cho nhiều người dùng không?
  • Có thể cache trong 60 giây, 5 phút hoặc 1 giờ không?
  • Cần revalidate theo sự kiện, theo tag hay theo path?
  • Có API nào đang tạo waterfall không?
  • Có thể preload request trước khi component cần không?
Cache streaming và data fetching trong React Server Components performance
RSC cần cache và data fetching tốt để performance thật sự ổn định.

Migration RSC từng phần nên làm ra sao?

Migration RSC nên đi từng route hoặc từng section có dữ liệu đọc nhiều, tương tác ít và bottleneck rõ. Đừng rewrite toàn bộ app chỉ vì framework đang đẩy xu hướng server-first.

Nếu dự án đang dùng Pages Router hoặc SPA React truyền thống, bước đầu nên chọn route ít rủi ro: blog detail, category page, documentation, landing page dịch vụ hoặc trang listing không cá nhân hóa. Tránh bắt đầu bằng checkout, dashboard realtime hoặc form nhiều trạng thái.

Trước khi chuyển, lưu baseline:

  • Bundle JS của route.
  • LCP, INP và TTFB trên môi trường gần production.
  • Số request API sau hydration.
  • Waterfall trong Chrome DevTools hoặc WebPageTest.
  • Lỗi log liên quan hydration, cache và stale data.

Sau khi chuyển, so sánh cùng điều kiện. Nếu bundle giảm nhưng TTFB tăng mạnh, bạn chưa thắng. Nếu LCP tốt hơn nhưng team mất nhiều ngày debug cache stale, bạn cần tính cả chi phí vận hành.

Trong website doanh nghiệp, mình thường chọn thứ tự: nội dung CMS trước, listing tiếp theo, dashboard read-only sau đó, cuối cùng mới đụng form và interaction phức tạp. Cách này ít rủi ro hơn và giúp team học boundary dần.

Fan-out query cho bài này gồm “RSC bundle size”, “use client boundary” và “Next.js caching RSC”. Mình dùng footprint từ docs React, Next.js và web.dev làm proxy vì intent kỹ thuật rõ hơn search volume thô.

Checklist quyết định dùng RSC

Hãy dùng RSC khi bạn đo được vấn đề liên quan đến JavaScript client, fetch waterfall hoặc dữ liệu có thể xử lý tốt ở server. Nếu chưa đo, hãy đo trước. Đừng để architecture chạy trước dữ liệu.

  1. Route có nhiều nội dung đọc, ít tương tác: hợp với Server Components.
  2. Client bundle đang lớn: kiểm tra phần nào có thể không gửi xuống browser.
  3. Dữ liệu có thể cache: CMS, danh mục, bài viết, navigation, pricing ít đổi.
  4. Boundary rõ: phần tương tác nhỏ, có thể tách thành Client Component riêng.
  5. Backend latency chấp nhận được: nếu backend chậm, tối ưu data layer trước.
  6. Team hiểu cache invalidation: nếu không, stale data sẽ thành lỗi khó debug.
  7. Có CI và monitoring: đo bundle, Web Vitals, error log và request count sau migration.
  8. Không dùng RSC cho mọi thứ: chart tương tác, editor, canvas, dashboard realtime vẫn cần client strategy tốt.

Nếu bạn muốn đi sâu hơn phần nền tảng, xem thêm React Server Components Guide. Nếu đang làm Next.js production, bài Next.js production performance sẽ giúp bạn chọn rendering strategy trước khi tối ưu chi tiết.

Nguồn tham khảo

  1. React docs: Server Components
  2. Next.js docs: Server and Client Components
  3. Next.js docs: Caching and Revalidating
  4. Next.js docs: use client directive
  5. web.dev Learn Performance

Các câu hỏi thường gặp

React Server Components performance có luôn tốt hơn Client Components không?

Không. RSC tốt hơn khi giảm JavaScript client và fetch waterfall. Nếu route cần nhiều tương tác hoặc backend chậm, lợi ích có thể thấp.

RSC có thay thế SSR không?

Không theo nghĩa đơn giản. RSC là kiến trúc component server-client mới; framework như Next.js có thể kết hợp RSC, SSR, prerendering, cache và hydration.

Khi nào cần dùng Client Component?

Khi component cần state, event handler, browser API, custom hook client-side, localStorage, canvas, chart tương tác hoặc UI cập nhật tức thì trong browser.

Đặt use client ở đâu là tốt?

Đặt càng sát phần cần tương tác càng tốt. Tránh đặt ở layout lớn nếu chỉ một nút hoặc modal cần chạy client-side.

Có nên migration toàn bộ app sang RSC ngay không?

Không nên. Hãy chọn route ít rủi ro, đo baseline, migration từng phần rồi so sánh bundle, LCP, INP, TTFB và lỗi cache.

Chốt lại: RSC đáng dùng khi bạn biết mình đang tối ưu gì. React Server Components performance là câu chuyện của ranh giới server-client, cache, streaming và bundle size. Đo trước, đổi từng phần, rồi giữ những gì thật sự làm website nhanh và dễ vận hành hơn.

Tú Anh

Cây bút chính tại VietnamTutor

Bài viết cùng chuyên mục

Git reset revert restore: chọn lệnh đúng

Bài viết so sánh git reset, git revert và git restore theo mục đích sử dụng: sửa staging area, khôi phục file, undo commit chưa push

Git commit vào nhánh sai: cách chuyển an toàn

Bài viết hướng dẫn xử lý git commit vào nhánh sai theo từng tình huống: commit chưa push, đã push, nhiều commit liên tiếp hoặc branch

TypeScript cho website doanh nghiệp: API, form và lỗi

TypeScript cho website doanh nghiệp đáng dùng khi bạn cần kiểm soát API contract, form schema, CMS payload và cấu hình môi trường. Bài này giúp

Git commit nhầm file: bỏ file khỏi commit an toàn

Bài viết hướng dẫn xử lý git commit nhầm file theo từng tình huống: chưa commit, đã commit chưa push, đã push lên remote, hoặc lỡ

Git reflog: khôi phục commit đã mất an toàn

Bài viết hướng dẫn dùng git reflog để khôi phục commit đã mất sau reset, rebase, amend hoặc xóa nhánh. Bạn sẽ biết cách đọc reflog,

Git pull bị conflict: cách sửa không mất code

Bài viết hướng dẫn cách xử lý git pull bị conflict theo từng bước: kiểm tra trạng thái, sửa file xung đột, test lại và hoàn

Next.js production performance: chọn SSR, SSG, ISR hay Edge

Bài viết giúp developer và tech lead chọn cách render phù hợp để tối ưu Next.js production performance mà không làm kiến trúc phức tạp quá

Nâng cấp Laravel 13: Checklist 10 bước cần kiểm tra

Hướng dẫn nâng cấp Laravel 13 chi tiết với checklist 10 bước. Từ kiểm tra PHP 8.3, cập nhật dependencies, đến xử lý lỗi thường gặp

Hardening Laravel production: Checklist bảo mật cần làm

Checklist hardening Laravel production toàn diện. Từ cấu hình server, database, SSL đến security headers, rate limiting và monitoring.

Authentication và authorization trong Laravel: Cách phân biệt

Hướng dẫn chi tiết cách xây dựng hệ thống Authentication (xác thực) và Authorization (phân quyền) trong Laravel với Breeze, Fortify, Sanctum, Policies và Gates.

Bảo mật Laravel: 10 lỗi phổ biến và cách phòng tránh

Hướng dẫn 10 lỗi bảo mật phổ biến nhất trong Laravel và cách phòng tránh hiệu quả. Từ XSS, SQL injection đến authentication vulnerabilities.

Migration PHP Attributes Laravel 13: Hướng Dẫn Chi Tiết

Cách chuyển đổi từ protected properties sang PHP Attributes trong Laravel 13 với hướng dẫn từng bước và code examples chi tiết.