React Server Components (RSC) are one of the biggest architectural shifts in React's history.
They change how we think about component rendering, moving computation from the client to the server to build faster, more efficient applications.
What are React Server Components?
React Server Components are a new type of component that runs only on the server.
Unlike traditional React components that run in the browser, Server Components run during the build or on the server at request time, sending only the rendered output to the client.
Core Attributes:
Execute on the server, not in the browser
Have direct access to backend resources (databases, file systems, APIs)
Cannot use browser-only features (useState, useEffect, event handlers)
Reduce JavaScript bundle size sent to the client
Framework Requirements
Important: You Need a Supporting Framework
React Server Components are a React feature, but you cannot use them in a regular React application (like Create React App).
You need a framework that supports RSC, such as Next.js App Router, which handles the server-side rendering.
Currently Supported Frameworks:
Next.js App Router Most mature and widely adopted implementation
Remix Adding experimental support
Waku Minimalist React framework with RSC support
Next.js App Router is the Standard
Most RSC usage today happens through Next.js App Router. When you create a new Next.js app with App Router, Server Components are the default:
1npx create-next-app@latest my-app
2# Choose "Yes" for App Router during setup34# All components in app/ directory are Server Components by default5# Use 'use client' directive only when you need client-side features
How Server Components Work
When a user requests a page, Server Components run on the server, fetch the data they need, and send a serialized output to the client
1// ServerComponent.js (Server Component)2import{ db }from'./database.js';// Server-only import34exportdefaultasyncfunctionUserProfile({ userId }){5// This runs on the server6const user =await db.users.findById(userId);78return(9<div>10<h1>{user.name}</h1>11<p>{user.email}</p>12<p>Joined: {user.createdAt}</p>13</div>14);15}
Server vs Client Components
Knowing the difference between Server and Client Components is key to using RSC well.
Server Components
Capabilities:
Server-side data fetching with direct database access
Access to server-only APIs
Zero impact on bundle size
Limitations:
No state (useState, useReducer)
No effects (useEffect, useLayoutEffect)
No event handlers (onClick, onChange)
No browser APIs (localStorage, window)
1// ✅ Server Component - Good practices2asyncfunctionBlogPost({ postId }){3const post =awaitfetchPost(postId);// Direct server call45return(6<article>7<h1>{post.title}</h1>8<p>{post.content}</p>9<PublishedDatedate={post.publishedAt}/>10</article>11);12}
Client Components
Client Components are traditional React components that run in the browser. You mark them with the 'use client' directive.
Important Note on Hydration
Client Components still run on the server first during Server-Side Rendering (SSR), then hydrate on the client.
This means any server-side data fetching in Client Components gets exposed during hydration,
which is why components with the 'use client' directive cannot do sensitive operations.
Server Components solve this by running only on the server and sending only the serialized output.
What is Hydration?
Hydration turns the static HTML generated by Server Components into an interactive React tree in the browser.
It attaches event listeners to the DOM elements and makes them interactive.
1'use client';23import{ useState }from'react';45functionInteractiveButton(){6const[count, setCount]=useState(0);78return(9<buttononClick={()=>setCount(count +1)}>10 Clicked {count} times
11</button>12);13}
Practical Examples
Data Fetching Without Loading States
Server Components remove the need for loading states in many cases:
1// Server Component approach - No loading state needed!2asyncfunctionUserList(){3const users =awaitfetchUsers();// Runs on server45return(6<ul>7{users.map(user=>(8<likey={user.id}>{user.name}</li>9))}10</ul>11);12}
Combining Server and Client Components
You can compose Server and Client Components together:
1// App.js (Server Component)2importUserProfilefrom'./UserProfile.js';// Server Component3importInteractivePanelfrom'./InteractivePanel.js';// Client Component45exportdefaultasyncfunctionApp({ userId }){6const user =awaitfetchUser(userId);78return(9<div>10<UserProfileuser={user}/>11<InteractivePaneluserId={userId}/>12</div>13);14}
Server Components don't add to your JavaScript bundle, so pages load faster:
1// This large library only runs on the server2import{MDXRemote}from'next-mdx-remote-client/rsc';// 100KB library34exportdefaultasyncfunctionRemoteMdxPage(){5// MDX text - can be from a database, CMS, fetch, anywhere...6const res =awaitfetch('https://...')7const markdown =await res.text()8return<MDXRemotesource={markdown}/>9}10// Client receives only the processed HTML, not the 100KB library!
Direct Backend Access
Access databases, file systems, and APIs directly, with no extra API layers:
1import{ readFile }from'fs/promises';2importpathfrom'path';34asyncfunctionDocumentViewer({ filename }){5// Direct file system access6const content =awaitreadFile(7 path.join(process.cwd(),'documents', filename),8'utf-8'9);1011return<pre>{content}</pre>;12}
Improved SEO and Performance
Because Server Components render on the server, they give you great SEO and faster initial page loads:
1asyncfunctionProductPage({ productId }){2const product =await db.products.findById(productId);34return(5<div>6<h1>{product.name}</h1>7<imgsrc={product.imageUrl}alt={product.name}/>8<p>{product.description}</p>9</div>10);11}12// This renders on the server, perfect for SEO!
When to Use Server Components
Ideal Use Cases
Data-heavy pages: Product listings, user profiles, dashboards
Static content: Blog posts, documentation, marketing pages
SEO-critical pages: Landing pages, product pages
Heavy computations: Image processing, data transformations
Avoid When You Need
User interactions: Forms, buttons, modals
Real-time updates: Chat applications, live data
Browser APIs: Geolocation, camera access
State management: Shopping carts, user preferences
Performance Considerations
Bundle Size Impact
1// Server Component - Zero bundle impact2import{ hugeLibrary }from'path/to/huge-library';// 500KB library34exportdefaultasyncfunctionHomePage(){5const processed = hugeLibrary.process(data);// Runs on server6return<div>{processed.result}</div>;7}89// vs Client Component - Adds to bundle10'use client';11import{ hugeLibrary }from'path/to/huge-library';// 500KB added to client bundle1213exportdefaultfunctionHomePage(){14const processed = hugeLibrary.process(data);15return<div>{processed.result}</div>;16}
Streaming Benefits
Server Components let you render progressively:
1exportdefaultfunctionDashboard(){2return(3<div>4<QuickStats/>{/* Renders immediately */}56{/* Streams in when ready */}7<Suspensefallback={<Skeleton/>}>8<SlowChart/>9</Suspense>10</div>11);12}
Quick Recap
Architectural Shift: Server Components move computation from client to server, reducing JavaScript bundle sizes and improving performance.
Direct Backend Access: Server Components can directly access databases, file systems, and APIs without additional API layers.
Composition Model: Combine Server and Client Components by keeping interactive elements on the client and data fetching on the server.
Performance Benefits: Faster initial page loads, better SEO, and reduced client-side JavaScript.
Use Case Clarity: Use Server Components for data-heavy, static content and Client Components for interactive features.
Framework Integration: Next.js App Router and other React frameworks are embracing Server Components as the default.
React Server Components aren't just a new feature, they're a paradigm shift that lets us build faster, more efficient web applications. Once you know when and how to use them, you can build better user experiences and simplify your application architecture.
I build fast, accessible, and delightful digital experiences for the web. Whether you have a project in mind or just want to connect, I'd love to hear from you.