·7 min read

Working with React Server Components

Yunus Emre OzdemirYunus Emre OzdemirSoftware Engineer @Upstash

In this tutorial, we will understand React Server Components and with that knowledge, build a simple view counter for your app together. Feel free to jump to Project Setup if you want to go ahead with the implementation.

Understanding React Server Components

In order to understand how React Server Components work, we will briefly look at Client-Side Rendering (CSR) and Server-Side Rendering (SSR).

Client-Side Rendering (CSR)

In CSR, most of the rendering work happens in the browser on the client-side:

  1. User requests a site.
  2. Server sends a HTML file and links to CSS and JS files.
  3. Client downloads the JS resources.
  4. Client renders the page without content.
  5. Client fetches the data from the API in Server.
  6. Client re-renders the page with content.

Client-Side Rendering

Server-Side Rendering (SSR)

In SSR, the server generates the full HTML content for each request and sends it to the client:

  1. User requests a site.
  2. Server renders the actual HTML files but they are not yet interactive.
  3. Client downloads the JS resources.
  4. In Client, using hydrateRoot() function, event listeners, state and other interactivity of React components are attached to the existing DOM elements generated by the Server, this is known as hydration.
  5. Client fetches the data from the API in Server.
  6. Client re-renders the page with content.

Server-Side Rendering

Advantages of SSR

  • Better SEO: Because search engines crawlers can read the fully-rendered HTML, SSR is more SEO-friendly.
  • Faster initial load: Since the complete HTML content is rendered and sent from the server, users see the page's content quickly.

Disadvantages of SSR

  • Slower navigation: Each new page request requires a round-trip communication for a full reload of the HTML from the server.
  • Increased server load: Since the server does most of the work in rendering the page, it may require more resources to handle multiple requests.

React Server Components

RSC is a feature in React that allow components to be rendered entirely on the server:

  1. Server fetches the data.
  2. Server renders the app with content.
  3. Client downloads the JS resources.
  4. Hydration happens in the Client.

Note: Server components are rendered entirely on the server, they are not included in the JS bundle and they are not hydrated. So steps 3 and 4 happen for Client components.

React Server Components

Advantages of RSC

  • Efficient Data Handling: Data fetching is moved to the server, closer to your database. This improves performance by removing the Client-Server round trip.
  • Reduced JavaScript Size on the Client: Since React Server Components don't send any JavaScript to the client, the overall bundle size is reduced, improving performance.
  • Faster Initial Page Load: Pre-rendering components on the server means the client receives fully-rendered HTML faster, improving time to First Contentful Paint (FCP).
  • Improved SEO: Content is fully rendered server-side, making it easier for search engines to crawl and index the content.

Challenges of RSC

  • State and Lifecycle: React Server Components don’t have access to browser APIs (like useState, useEffect), since they are rendered on the server.
  • No Client-Side Interactivity: React Server Components are not meant for dynamic updates or interactivity. You need to use Client Components for interactive parts of your app.

Using React Server Components

React Server Components in Next.js

By default, Next.js uses Server Components. They are rendered in 3 different ways:

  • Static Rendering (Default): routes are rendered at build time, or in the background after data revalidation.
  • Dynamic Rendering: routes are rendered for each user at request time.
  • Streaming: UI is progressively rendered from the server.

When should you use React Server Components?

You should use React Server Components when:

  • You have static, non-interactive content that doesn’t change frequently.
  • You want to improve performance by reducing JavaScript sent to the client.
  • SEO is important for your application.
  • You want to offload heavy data-fetching or computational tasks to the server.

You shouldn’t React Server Components when:

  • Your components require client-side interactivity, state, or hooks.
  • You need access to browser APIs or require real-time updates.
  • You are building complex pages with heavy client-side logic or user-specific content.

Common Pitfalls

Client Components are not rendered entirely on the Client

They are rendered both on Client and Server. They are named Client Components to differentiate them from Server Components.

‘use server’ directive is for Server Actions

By default, Next.js uses Server Components so you don’t need to specify anything. If you want to use Client Components, you add the ‘use client’ directive to declare a boundary between Server and Client modules. ‘use server’ is an entirely different directive used for Server Actions, which is beyond the scope of this blogpost.

Project Setup

We will use the Blog template from Vercel:

pnpm create next-app --example https://github.com/vercel/examples/tree/main/solutions/blog blog

You can run the example locally and see what it looks like:

cd blog
pnpm dev

Let’s install @upstash/redis:

pnpm add @upstash/redis

Environment Setup

  1. Go to Upstash Console → Redis and create a new Database:

Create Database

  1. Scroll down to REST API section, switch to .env tab and copy the environment variables for the next step:

Upstash Environment Variables

  1. Create a .env file and paste your environment variables:
UPSTASH_REDIS_REST_URL=<YOUR_URL>
UPSTASH_REDIS_REST_TOKEN=<YOUR_TOKEN>

Setup Views Component

Create /app/components/views.tsx:

import { headers } from 'next/headers'
import { Redis } from "@upstash/redis"
 
const redis = Redis.fromEnv();
 
async function view(slug: string, ip: string) {
    // Hash the IP address to anonymize it
    const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(ip));
    const hash = Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
    // Deduplicate views
    const newView = await redis.set(`deduplicate:${hash}:${slug}`, true, {
        nx: true, // Only set the key if it doesn't exist
        ex: 24 * 60 * 60, // Expire the key after 24 hours
    });
    if (newView) {
        await redis.incr(`pageviews:${slug}`); // Increment the view count
    }
}
 
export default async function Views({ slug }: { slug: string }) {
    // Get the IP address of the user
    const header = headers()
    const ip = (header.get('x-forwarded-for') ?? '127.0.0.1').split(',')[0]
    // Increment the view count
    await view(slug, ip)
    // Get the view count
    const views = await redis.get<number>(`pageviews:${slug}`) || 0
    return (
        <p className="text-sm text-neutral-600 dark:text-neutral-400">
            {views} views
        </p>
    )
}
 

Import and Display Views Component

Edit /app/blog/[slug]/page.tsx:

...
import Views from 'app/components/views'
...
	  <div className="flex justify-between items-center mt-2 mb-8 text-sm">
        <p className="text-sm text-neutral-600 dark:text-neutral-400">
          {formatDate(post.metadata.publishedAt)}
        </p>
        <Views slug={post.slug} />
      </div>
...

Visit http://localhost:3000/blog/vim to see the view counter in action:

View Counter

Comparing with Client Components Setup

Checkout our blogpost "Adding a View Counter to your Next.js Blog" to see the implementation with Client Components.

Along with other benefits mentioned in previous sections, using React Server Components:

  • Removed the necessity of a separate API.
  • Provided a simple unified view of the component and its logic with the benefits of type checking.

Deploy

You can deploy your site to Vercel with the following command:

vercel

Final Words

By combining the strengths of both Server and Client Components, you can strike the right balance between performance and interactivity in your applications. I hope this guide helps you make informed decisions about when to leverage React Server Components effectively.