7
Chapter 7

Introducing Server Side Rendering

This chapter dives into leveraging Server-Side Rendering (SSR) in React applications. It discusses how SSR, combined with React Server Components, can optimize initial page load times and enhance overall application performance.

In this chapter, you will learn

  • Understanding and implementing SSR in React
  • Integrating React Server Components for efficient rendering
  • Enhancing user experience with faster initial page loads

  • In the previous chapters, we've explored various client-side techniques to enhance initial page rendering speed. However, significant optimizations can also be achieved server-side, sometimes even before a user sends a request. What if we could complete some tasks during the request, leveraging backend efficiencies?

    Let's delve into Server-Side Rendering (SSR) in this chapter, particularly focusing on React Server Components within Next.js.

    Server-Side Rendering is a web development technique where the server generates the complete HTML of a webpage in response to a user request. Each time a user accesses a URL or refreshes their browser, the server executes the necessary JavaScript code, renders all dynamic content, and sends this fully prepared HTML to the browser. While this involves some processing time on the server, it allows for a quicker display of a fully rendered page compared to waiting for client-side JavaScript to render content. SSR significantly enhances the user experience, especially for initial page loads, and improves a website's search engine optimization by ensuring all content is immediately accessible.

    Implementing SSR requires backend techniques. There are many alternatives: building a full-featured backend service is time-consuming and effort-intensive, but we don't need to start from scratch. In this chapter, we will use Next.js for SSR and React Server Components.

    Introducing Next.js

    If you're not familiar with Next.js, I recommend going through its dashboard tutorial. It's beneficial to follow along with the tutorial, experiment with it, and then return here.

    Next.js is an open-source web development framework built on Node.js, designed to simplify the building of server-rendered React applications. Created by Vercel (formerly Zeit), it aims to streamline the process of building performant, scalable, and user-friendly web applications. It enhances the developer experience with features like automatic code splitting, server-side rendering, static site generation, and built-in CSS support. In this chapter, we'll focus on its app router feature for SSR, which functions similarly to client-side React components.

    To start, we'll create a dynamic route – app/user/[id]/page.tsx. This path signifies a dynamic route within the app directory in Next.js. For instance, accessing /user/123 renders the page.tsx component for the user with ID 123, changing content based on the URL parameter.

    Next.js version 13 introduced an App Router utilizing React Server Components, supporting shared layouts, nested routing, loading states, error handling, and more. This system bases its routes on the file structure in the "app" directory.

    Learn more about Routing Fundamentals and Pages and Layouts.

    In contrast, static rendering generates HTML at build time with pre-fetched data, resulting in super-fast page loads due to static HTML. We'll explore this further in the next chapter.

    In page.tsx, we define a Page component to fetch data and pass it to the frontend:

    app/user/[id]/page.tsx
    export default async function Page({ params }: PageProps) { const { user, friends } = await getUserBasicInfo(params.id); return (<Profile user={user} friends={friends} />); }

    The PageProps interface is:

    interface PageProps { params: { id: string; }; }

    The params object is part of Next.js's routing feature, which allows you to access dynamic parts of the URL. In the case of app/user/[id]/page.tsx, params would contain an id property. This id corresponds to the dynamic segment in the URL. For example, if the URL is /user/123, params.id will be 123. This allows the Page component to fetch or compute data specific to that user ID.

    The function getUserBasicInfo is defined as follows for actual data fetching at request time:

    app/user/[id]/page.tsx
    async function getUserBasicInfo(id: string) { const [user, friends] = await Promise.all([ get<User>(`/users/${id}`), get<User[]>(`/users/${id}/friends`), ]); return { user, friends }; }

    The Profile component, defined as a simple presentational component, receives data directly from the Page component. We are using About and Friends components from Chapter 3, they are all presentational component and only render whatever passed in:

    components/profile.tsx
    const Profile = ({ user, friends }: { user: User; friends: User[] }) => { return ( <> <About user={user} /> <Friends users={friends} /> <Feeds category={user.interests[0]} /> </> ); };

    Next.js by default uses Server Components, meaning they are rendered server-side without additional setup. We can define traditional, or client components, for contrast. Let's make the Feeds component a client component to demonstrate its use alongside Server Components.

    components/feeds.tsx
    'use client'; import { useEffect, useState } from "react"; const Feeds = ({ category }: { category: string }) => { const [loading, setLoading] = useState<boolean>(false); const [feeds, setFeeds] = useState<Feed[]>([]); useEffect(() => { const fetchFeeds = async () => { setLoading(true); const response = await fetch( `http://localhost:1573/articles/${category}` ); const data = await response.json(); setLoading(false); setFeeds(data); }; fetchFeeds(); }, [category]); if (loading) { return <div>Loading...</div>; } return ( <div> <h2>Your Feeds</h2> <div> {feeds.map((feed) => ( <> <h3>{feed.title}</h3> <p>{feed.description}</p> </> ))} </div> </div> ); }; export { Feeds };

    Pay special attention to the use client directive employed in our example. This directive explicitly marks a component as a client-side component in Next.js.

    This distinction is crucial because it dictates the use of certain React hooks. In client components, you have the freedom to use hooks like useState and useEffect, just as you would in a standard React application. These hooks are essential for managing state and side effects on the client side.

    However, it's important to note that these APIs are not available for Server Components. Server Components are designed for different purposes, mainly focused on preparing and delivering content from the server without interactive or stateful behavior.

    React Server Components

    React Server Components represent a paradigm shift in application architecture, as defined on the React homepage. They run ahead of time, separate from your JavaScript bundle, and are instrumental in optimizing your app's performance.

    React's research introduced Server Components as a novel component type that operates before the browser load and is not included in the JavaScript bundle. These components can execute during the build process, allowing access to the filesystem or static content without the need for an API. They facilitate data transfer to interactive Client Components in the browser through props.

    Server Components are best suited for non-interactive elements that form the content's framework, while Client Components handle interactive aspects like mouse and keyboard events.

    When a page loads, you might notice a slight delay initially. Soon, components like About and Friends appear, followed by a loading phase for Feeds. Once the API call for feeds completes, the entire content is displayed.

    Server Side Rendering
    Server Side Rendering

    Why shift requests from the frontend to the backend? Several benefits include:

    • Faster data retrieval, as servers are usually closer to data storage.
    • Simplified data caching and control on the server side.
    • Enhanced security and power in backend processing.
    • Frontend transparency in security setups like API keys or tokens.

    Though the initial load may take slightly longer, subsequent operations become more responsive, as no additional data loading is required. This approach enhances the user experience and allows for various caching levels, speeding up future data requests.

    7

    You have Completed Chapter 7

    In this chapter, we explore Server-Side Rendering (SSR) and its integration with React Server Components. We delve into how SSR improves initial page load times, making React applications more performant and user-friendly. Up next: Static Site Generation.

    With key insights from this chapter, we pave the way to explore the synergy between SSR and React Server Components, setting the stage for the next topic: Static Site Generation.

    © 2023