docs/pro/react-server-components/purpose-and-benefits.md
React Server Components with streaming is beneficial for most applications, but it's especially powerful for applications with waterfall loading patterns where data dependencies chain together. For example, when you need to load a user profile before loading their posts, or fetch categories before products. Here's why:
When a user visits the page, they'll experience the following sequence:
The initial HTML shell is sent immediately, including:
<h1> and footer)Selective Hydration:
React Server Components significantly reduce client-side JavaScript by:
Server-Only Code Elimination:
Concrete Examples:
For example, a typical dashboard might see:
// Before: All code shipped to client
import { format } from 'date-fns'; // ~30KB
import { marked } from 'marked'; // ~35KB
import numeral from 'numeral'; // ~25KB
// After: With RSC, these imports stay server-side
// Client bundle reduced by ~90KB
React's selective hydration is a powerful feature that significantly improves page interactivity by:
Independent Component Hydration
Interaction-Based Prioritization
Parallel Processing
For example, in a typical page layout:
<Layout>
<Suspense fallback={<NavSkeleton />}>
<Navigation />
</Suspense>
<Suspense fallback={<MainSkeleton />}>
<MainContent />
<Comments />
</Suspense>
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</Layout>
With selective hydration:
This approach significantly improves the user experience by:
For a deeper dive into selective hydration, see our Selective Hydration in Streamed Components guide.
Before following the component migration patterns below, complete the infrastructure setup with the current generator-based runbook:
bundle exec rails generate react_on_rails:rsc (or --typescript) to enable config.enable_rsc_support, add the RSC webpack bundle, and generate the example files.Once that infrastructure is in place, migrate components incrementally.
Adding the 'use client' directive to entry points maintains existing functionality while allowing for incremental migration of individual components to server components.
This approach ensures a smooth transition without disrupting the application's current behavior.
// app/components/App.jsx
'use client';
export default function App() {
// Your existing component code
}
Start by converting layout and container components at the top of your component tree to server components, moving any interactive logic down to child components. This "top-down" approach maximizes the benefits of RSC.
// app/components/Layout.jsx
// Remove 'use client' - This becomes a server component
// Move any state/effects to child components first
export default function Layout({ children }) {
return (
<div>
<Header />
<Sidebar />
<main>
{children}
</main>
<Footer />
</div>
);
}
// app/components/InteractiveWidget.jsx
'use client'; // Keep client directive for interactive components
export default function InteractiveWidget() {
const [state, setState] = useState();
// Interactive component logic
}
// app/components/LazyLoadedSection.jsx
// Remove lazy loading wrapper
// Convert to async server component
async function LazyLoadedSection() {
const data = await fetchData();
return (
<div>
<ServerContent data={data} />
<ClientInteraction />
</div>
);
}
This migration approach allows you to: