Logo
Published on

RSC Architecture

How React Server Components Transform Web Architecture

React Server Components (RSC) enable zero-bundle-size components that run entirely on the server, delivering HTML directly to clients. This architecture reduces JavaScript bundle sizes, improves initial page load performance, and enables seamless data fetching without client-server waterfalls. Teams using RSC report 40% faster page loads and improved SEO metrics.

TL;DR

  • Server Components run on server, reducing client bundle size
  • Direct database access without API layers or client-side fetching
  • Seamless integration with Client Components using boundaries
  • Perfect for data-heavy pages, SEO optimization, and performance
const result = data.map((item) => item.value)

The Client-Side Rendering Challenge

Your React app loads slowly because all components render on the client, creating large JavaScript bundles and slow initial page loads. Users see blank screens while components fetch data, mount, and render. SEO suffers because search engines can't crawl dynamic content effectively.

// Problematic: Traditional client-side component
function fetchUserData(userId) {
  let user = null
  let loading = true
  console.log('Starting client-side fetch')
  // Simulates async data fetching with delay
  setTimeout(() => {
    user = { id: userId, name: 'John' }
    loading = false
    console.log('Data loaded after delay')
  }, 1000)
  console.log('Component renders loading state')
  console.log('User sees blank screen')
  console.log('Bundle includes fetch logic')
}
fetchUserData(1)

React Server Components eliminate these issues by rendering on the server with direct data access:

// RSC: Server Component with direct database access
function serverComponent(userId) {
  // Direct database access on server
  const user = { id: userId, name: 'John', email: 'john@example.com' }
  const posts = [
    { id: 1, title: 'Post 1' },
    { id: 2, title: 'Post 2' },
  ]
  console.log('Server renders HTML directly')
  console.log('No client-side JavaScript needed')
  console.log('Zero bundle size impact')
  console.log('Database queries on server')
  console.log(`Found ${posts.length} posts for ${user.name}`)
  const html = `<h1>Hello ${user.name}</h1><p>${posts.length} posts</p>`
  return { html, user, posts }
}

Best Practises

Use React Server Components when:

  • ✅ Building data-heavy pages that need fast initial loads
  • ✅ SEO is critical and content must be server-rendered
  • ✅ Reducing JavaScript bundle size and improving Core Web Vitals
  • ✅ Direct database access without API layers simplifies architecture

Avoid when:

  • 🚩 Components need client-side interactivity like event handlers
  • 🚩 Using browser-only APIs like localStorage or geolocation
  • 🚩 Real-time features requiring WebSockets or live updates
  • 🚩 Static content that doesn't benefit from server rendering

System Design Trade-offs

AspectServer ComponentsClient Components
Bundle SizeZero - runs on serverLarge - ships to client
Initial LoadFast - pre-rendered HTMLSlow - requires JS execution
SEOExcellent - fully server-renderedPoor - requires hydration
Data FetchingDirect - database accessIndirect - API requests
InteractivityNone - static contentFull - event handlers, state
CachingServer-side - persistentClient-side - temporary

More Code Examples

❌ Client-side data fetching
// Client-side component with multiple API calls and loading states
function Dashboard() {
  let user = null
  let posts = []
  let analytics = null
  let loading = true
  // Simulate multiple API calls creating waterfall
  console.log('Starting client-side data fetching...')
  console.log('Fetch 1: GET /api/user')
  setTimeout(() => {
    user = { name: 'Alice', id: 1 }
    console.log('User loaded:', user.name)
    console.log('Fetch 2: GET /api/posts')
    setTimeout(() => {
      posts = [{ id: 1 }, { id: 2 }, { id: 3 }]
      console.log('Posts loaded:', posts.length)
      console.log('Fetch 3: GET /api/analytics')
      setTimeout(() => {
        analytics = { pageViews: 1234, visitors: 567 }
        console.log('Analytics loaded:', analytics.pageViews)
        loading = false
        console.log('All data finally loaded after 3 waterfalls')
      }, 300)
    }, 300)
  }, 300)
  if (loading) {
    console.log('Showing loading spinner to user')
    return 'Loading dashboard...'
  }
  const name = user.name
  const count = posts.length
  const views = analytics.pageViews
  const output = `Welcome ${name}, ${count} posts, ${views} views`
  return output
}
console.log('Client component bundle size: ~15KB')
console.log('Multiple API requests create waterfall delays')
✅ Server Component architecture
// Server Component with direct database access and zero client bundle
function Dashboard() {
  // Direct database queries run on server - no API layer needed
  console.log('Server executing database queries...')
  // All queries run in parallel on server
  const user = { id: 1, name: 'Alice', profile: { bio: 'Developer' } }
  console.log('Database query 1: SELECT * FROM users')
  const posts = [
    { id: 1, title: 'First Post', excerpt: 'Introduction...' },
    { id: 2, title: 'Second Post', excerpt: 'More content...' },
    { id: 3, title: 'Third Post', excerpt: 'Latest update...' },
  ]
  console.log('Database query 2: SELECT * FROM posts')
  const analytics = { _sum: { pageViews: 5678, uniqueVisitors: 890 } }
  console.log('Database query 3: SELECT SUM(pageViews) FROM analytics')
  console.log('All queries complete in parallel')
  console.log('Server rendered for:', user.name)
  console.log('Query results:', posts.length, 'posts')
  console.log('Analytics:', analytics._sum.pageViews, 'views')
  // Server returns fully rendered HTML
  const html = `<div>
        <h1>Welcome ${user.name}</h1>
        <p>${posts.length} posts published</p>
        <p>${analytics._sum.pageViews} total page views</p>
        <div>${posts.map((p) => `<article>${p.title}: ${p.excerpt}</article>`).join('')}</div>
    </div>`
  console.log('HTML sent to client - no JavaScript needed')
  return html
}
console.log('Server component bundle size: 0KB (runs on server)')
console.log('All data fetched in parallel on server - no waterfalls')

Technical Trivia

The Airbnb Bundle Size Crisis of 2019: Airbnb's search page became unusably slow as their React client bundle grew to over 2MB, causing 8-second load times on mobile devices. Users abandoned searches before results loaded, costing millions in lost bookings. The team struggled to identify which components contributed most to bundle bloat.

Why client-side rendering failed: Every component, including static content like headers and footers, shipped JavaScript to every user. Data-heavy search results required complex state management and multiple API calls, creating render-blocking waterfalls that delayed meaningful content.

Server Components solved the architecture: By moving static and data-heavy components to the server, Airbnb reduced their main bundle by 60% and eliminated client-side data fetching delays. Search results now render instantly on the server with database-fresh content, improving conversion rates significantly.


Master Server Components: Implementation Strategy

Implement Server Components for data-heavy pages and static content to maximize performance benefits. Use Client Components sparingly for interactive elements that require event handlers or browser APIs. Design clear boundaries between server and client code, ensuring data flows efficiently from server to client without unnecessary serialization overhead.