Overview
React Server Components (RSC) are components that run exclusively on the server. They have direct access to server resources like databases, file systems, and environment variables, and never send JavaScript to the client.
Server vs client components
By default, all components in COSMOS RSC are server components unless marked with 'use client'.
Server Component (default)
Client Component
// No directive needed - this is a server component
async function UserProfile () {
// Can directly access server resources
const userData = await fetchUserData ();
return (
< div >
< h3 > User Profile </ h3 >
< p > Name: { userData . name } </ p >
< p > Email: { userData . email } </ p >
</ div >
);
}
Server component benefits
Server components provide several key advantages:
Server components don’t ship any JavaScript to the browser. The component code stays on the server, reducing bundle size.
Access databases, file systems, and environment variables directly without creating API endpoints.
API keys, database credentials, and sensitive logic never reach the client.
Each server component is automatically split and loaded on-demand.
Async server components
Server components can be async functions, allowing you to await data directly:
app/pages/features/server-components.js
// Simulated database call
async function fetchUserData () {
await new Promise (( resolve ) => setTimeout ( resolve , 1000 ));
return {
id: 1 ,
name: 'John Doe' ,
email: '[email protected] ' ,
preferences: {
theme: 'light' ,
notifications: true ,
},
};
}
// Server component with async data fetching
async function UserProfile () {
const userData = await fetchUserData ();
const cookieManager = cookies ();
const lastVisit = cookieManager . get ( 'last_visit' );
return (
< div className = 'rounded bg-white p-4 shadow' >
< h3 className = 'mb-2 text-lg font-medium' > User Profile </ h3 >
< div className = 'space-y-2 text-gray-600' >
< p > Name: { userData . name } </ p >
< p > Email: { userData . email } </ p >
< p > Theme: { userData . preferences . theme } </ p >
{ lastVisit && (
< p className = 'text-sm' >
Last visit: {new Date ( lastVisit ). toLocaleString () }
</ p >
) }
</ div >
</ div >
);
}
Async components are only supported in React Server Components. Client components cannot be async.
Accessing server resources
Server components can import and use server-only utilities:
import { cookies } from '#cosmos-rsc/server' ;
import { db } from '../lib/database' ;
async function DashboardPage () {
// Read cookies
const cookieManager = cookies ();
const userId = cookieManager . get ( 'user_id' );
// Query database
const user = await db . users . findById ( userId );
const posts = await db . posts . findByAuthor ( userId );
return (
< div >
< h1 > Welcome, { user . name } </ h1 >
< PostList posts = { posts } />
</ div >
);
}
Using cookies
The cookies() function provides access to request cookies:
import { cookies } from '#cosmos-rsc/server' ;
async function ServerComponent () {
const cookieManager = cookies ();
// Get a cookie
const theme = cookieManager . get ( 'theme' );
// Set a cookie
cookieManager . set ( 'last_visit' , new Date (). toISOString (), {
maxAge: 24 * 60 * 60 , // 24 hours
path: '/' ,
});
return < div > Theme: { theme } </ div > ;
}
The cookies() function can only be called during the RSC render phase, not during server actions.
Multiple async operations
You can compose multiple server components with different data requirements:
app/pages/features/server-components.js
// Component 1: User data
async function UserProfile () {
const userData = await fetchUserData ();
return < div > { /* User profile UI */ } </ div > ;
}
// Component 2: Weather data
async function WeatherWidget () {
const weather = await fetchWeatherData ();
return (
< div className = 'rounded bg-blue-50 p-4 shadow' >
< h3 className = 'mb-2 text-lg font-medium' > Current Weather </ h3 >
< div className = 'space-y-1' >
< p className = 'text-gray-600' > Location: { weather . location } </ p >
< p className = 'text-2xl' > { weather . temperature } °C </ p >
< p className = 'text-gray-500' > { weather . condition } </ p >
</ div >
</ div >
);
}
// Page combining both components
export default function ServerComponentsDemo () {
return (
< div className = 'grid gap-6 md:grid-cols-2' >
< UserProfile />
< WeatherWidget />
</ div >
);
}
Each async component fetches its own data independently. React handles the coordination.
Composing with client components
Server components can render client components and pass data via props:
// Server component
async function ProductPage ({ productId }) {
const product = await fetchProduct ( productId );
const reviews = await fetchReviews ( productId );
return (
< div >
< h1 > { product . name } </ h1 >
< p > { product . description } </ p >
{ /* Pass server data to client component */ }
< AddToCartButton product = { product } />
< ReviewList reviews = { reviews } />
</ div >
);
}
// Client component
'use client' ;
function AddToCartButton ({ product }) {
const [ added , setAdded ] = useState ( false );
return (
< button onClick = { () => {
addToCart ( product . id );
setAdded ( true );
} } >
{ added ? 'Added!' : 'Add to Cart' }
</ button >
);
}
Client components can be children of server components, but server components cannot be children of client components.
Serializable props
Data passed from server components to client components must be serializable:
// Plain objects, arrays, primitives
< ClientComponent
user = { { name: 'John' , age: 30 } }
items = { [ 1 , 2 , 3 ] }
message = "Hello"
/>
Server-only code
Use the server-only package to ensure code never runs on the client:
import 'server-only' ;
export async function queryDatabase ( sql ) {
// This code will error if accidentally imported in a client component
return db . query ( sql );
}
Rendering flow
When a page is requested:
Server receives the request
Server components execute and fetch data
React renders server components to RSC format
RSC stream includes data and client component references
Server renders HTML with React DOM Server
Client hydrates and renders client components
Server component limitations
Server components have some restrictions:
Cannot use React hooks like useState, useEffect
Cannot use browser APIs like window or document
Cannot attach event handlers like onClick
Cannot use context created with createContext
For these features, use client components instead.
Example: Full server component page
Here’s the complete server components demo from the COSMOS RSC project:
app/pages/features/server-components.js
import { cookies } from '#cosmos-rsc/server' ;
import { NavigationTransition } from '../../components/navigation-transition' ;
async function fetchUserData () {
await new Promise (( resolve ) => setTimeout ( resolve , 1000 ));
return {
id: 1 ,
name: 'John Doe' ,
email: '[email protected] ' ,
preferences: { theme: 'light' , notifications: true },
};
}
async function UserProfile () {
const userData = await fetchUserData ();
const cookieManager = cookies ();
const lastVisit = cookieManager . get ( 'last_visit' );
return (
< div className = 'rounded bg-white p-4 shadow' >
< h3 > User Profile </ h3 >
< p > Name: { userData . name } </ p >
< p > Email: { userData . email } </ p >
{ lastVisit && < p > Last visit: {new Date ( lastVisit ). toLocaleString () } </ p > }
</ div >
);
}
export default function ServerComponentsDemo () {
return (
< div className = 'mx-auto max-w-4xl px-4 py-12' >
< NavigationTransition >
< h1 > Server Components Demo </ h1 >
</ NavigationTransition >
< div className = 'grid gap-6 md:grid-cols-2' >
< UserProfile />
</ div >
</ div >
);
}
Next steps
Streaming Learn about SSR streaming with Suspense
Server Actions Execute server code from client components