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é!

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.

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 LCP và Cloudflare Cache Rules cho WordPress. Khác stack, nhưng cách tư duy đo bottleneck giống nhau.

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.

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?

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.
- Route có nhiều nội dung đọc, ít tương tác: hợp với Server Components.
- Client bundle đang lớn: kiểm tra phần nào có thể không gửi xuống browser.
- Dữ liệu có thể cache: CMS, danh mục, bài viết, navigation, pricing ít đổi.
- Boundary rõ: phần tương tác nhỏ, có thể tách thành Client Component riêng.
- Backend latency chấp nhận được: nếu backend chậm, tối ưu data layer trước.
- Team hiểu cache invalidation: nếu không, stale data sẽ thành lỗi khó debug.
- Có CI và monitoring: đo bundle, Web Vitals, error log và request count sau migration.
- 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
- React docs: Server Components
- Next.js docs: Server and Client Components
- Next.js docs: Caching and Revalidating
- Next.js docs: use client directive
- 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.
