React  

Rethinking React: How Server Components Change Everything

React is constantly evolving, but React Server Components (RSC) aren’t just an update; they’re a rewrite of how we think about rendering in modern web apps.

Instead of rendering everything on the client with heavy JavaScript bundles, RSC lets you render parts of your app entirely on the server, sending zero JS for those parts. The result? Faster apps, smaller bundles, and cleaner code.

Let’s break down what RSC is, how it works, and build a working blog app using RSC with a modern Tailwind UI.

What Are React Server Components?

In traditional React apps, everything, even content that never changes, is bundled and hydrated on the client. This means you’re shipping JavaScript unnecessarily and making your users wait.

React Server Components solve that. They render on the server and send HTML, not JS, to the browser. This means:

  • No hydration required
  • Zero JS shipped for server components
  • Full access to server-only resources (e.g., database, file system)
  • Smaller client-side bundles

Server vs Client Components: Know the Difference

Feature Client Component Server Component
Runs On Browser Server
JS in bundle YES NO
Access to DOM YES NO
Can use useState/useEffect? YES NO

By default, components in the App Router (Next.js 13+) are Server Components. You only mark Client Components with 'use client'.

Let’s Build: A Blog Viewer Using RSC

We’re building a simple blog page with:

  • Server Component that fetches and renders posts
  • Client Component for interactivity (Like button)
  • Tailwind CSS for styling

Tech stack

  • Next.js 14+
  • App Router (for RSC)
  • Tailwind CSS
  • No database needed, just mock data

Folder Structure

app/
├── page.js                  // Home page (Server Component)
├── components/
│   ├── BlogList.js          // Server Component
│   └── LikeButton.js        // Client Component
├── layout.js
├── globals.css

How It Works

  • BlogList.js is a Server Component. It fetches blog post data and renders content.
  • LikeButton.js It is a Client Component. It handles client-side state (like counts).
  • Posts are rendered directly into the HTML on the server.
  • Client-side JS is only loaded where needed.

Complete Source Code (JS + Tailwind + RSC)

page.js

import BlogList from './components/BlogList';

export default function HomePage() {
  return (
    <main className="max-w-3xl mx-auto p-6">
      <h1 className="text-3xl font-bold mb-6">React RSC Demo</h1>
      <BlogList />
    </main>
  );
}

BlogList.js

import { Suspense } from "react";
import LikeButton from "./LikeButton";

async function getPosts() {
  await new Promise((res) => setTimeout(res, 500));

  return [
    {
      id: 1,
      title: "What is RSC?",
      content: "React Server Components explained simply.",
    },
    { 
      id: 2, 
      title: "Why It Matters", 
      content: "Speed, security, and sanity." 
    },
    {
      id: 3,
      title: "Client vs Server Components",
      content: "When to use each, and why it matters for performance.",
    },
    {
      id: 4,
      title: "Tailwind in RSC Apps",
      content: "Build beautiful UIs without writing a line of CSS.",
    },
    {
      id: 5,
      title: "Deploying to Vercel",
      content: "Push your RSC app to production in minutes.",
    },
    {
      id: 6,
      title: "Common Mistakes with RSC",
      content: "Avoid hydration errors, props mismatches, and bad patterns.",
    },
  ];
}

export default async function BlogList() {
  const posts = await getPosts();

  return (
    <div className="space-y-4">
      {posts.map((post) => (
        <div
          key={post.id}
          className="border p-4 rounded bg-white shadow hover:shadow-md transition"
        >
          <h2 className="text-xl font-semibold">{post.title}</h2>
          <p className="text-gray-700">{post.content}</p>
          <Suspense fallback={<div>Loading Like button...</div>}>
            <LikeButton postId={post.id} />
          </Suspense>
        </div>
      ))}
    </div>
  );
}

LikeButton.js

"use client";

import { useState } from "react";

export default function LikeButton({ postId }) {
  const [likes, setLikes] = useState(0);

  return (
    <button
      onClick={() => setLikes(likes + 1)}
      className="mt-2 px-4 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition"
    >
      ❤️ Like ({likes})
    </button>
  );
}

Run the App

npm run dev

You’ll see your blog posts rendered instantly (Server Component), and the Like button is fully interactive (Client Component).

Note. In this example, we used Tailwind CSS for quick and clean styling. You can swap it out for any styling approach you prefer, plain CSS, Sass, CSS Modules, styled-components, or UI libraries like Chakra UI or ShadCN. The choice of styling doesn't affect how Server Components work.

Conclusion: Why This Matters

React Server Components aren’t just about performance. They change how you architect apps. By pushing logic to the server and trimming JS from the client, your apps become faster, lighter, and more scalable without losing interactivity.

RSC is the future of React. And you just built with it.

Links