Reader small image

You're reading from  JavaScript Design Patterns

Product typeBook
Published inMar 2024
Reading LevelIntermediate
PublisherPackt
ISBN-139781804612279
Edition1st Edition
Languages
Right arrow
Author (1)
Hugo Di Francesco
Hugo Di Francesco
author image
Hugo Di Francesco

Hugo Di Francesco is a software engineer who has worked extensively with JavaScript. He holds a MEng degree in mathematical computation from University College London (UCL). He has used JavaScript across the stack to create scalable and performant platforms at companies such as Canon and Elsevier and in industries such as print on demand and mindfulness. He is currently tackling problems in the travel industry at Eurostar with Node.js, TypeScript, React, and Kubernetes while running the eponymous Code with Hugo website. Outside of work, he is an international fencer, in the pursuit of which he trains and competes across the globe.
Read more about Hugo Di Francesco

Right arrow

Rendering Strategies and Page Hydration

Rendering strategies and page hydration approaches allow us to leverage the JavaScript client and server ecosystem to serve performant and scalable web applications, depending on the needs of our end users. The React and JavaScript techniques covered in this chapter are another set of tools to augment the Chapter 4 chapter. We’ll use the strengths of the client (browser) and server (specifically, Node.js) runtimes to deliver fast and scalable React websites to users.

In this chapter, we’ll cover the following topics:

  • What the trade-offs are between client and server rendering of React applications by implementing pure client and server rendering applications
  • The types of advantages that frameworks such as Next.js can bring with static site generation functionality, alongside server-side rendering
  • Bridging the client-server rendering gap with a React page rehydration example and its gotchas
  • Streaming server...

Technical requirements

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Javascript-Design-Patterns

Client and server rendering with React

In a web context, client-side rendering is the process by which JavaScript is used inside a user’s browser to generate or update the page contents. A fully client-side-rendered application will only display meaningful content when the relevant JavaScript code has completed downloading, parsing, and running.

In the following sequence diagram, we use the term “origin” instead of something such as “server,” since one benefit of full client-side rendering is that the resources “serving” our content can be what’s called static hosting. This includes services such as AWS Simple Storage Service (S3), Netlify, Cloudflare Pages, and GitHub Pages, among others. There’s no dynamic server-side component in these services.

Figure 5.1: A client-side-rendering sequence diagram

Figure 5.1: A client-side-rendering sequence diagram

In contrast, server-side rendering denotes the process by which a server generates a full...

Static rendering with Next.js

Next.js is a React framework for creating full stack web applications. What this means is that it provides tools and opinions that will help developers be more productive in the short and long term.

Next.js includes a filesystem router for “pages”, a set of routing primitives for React, support for client and server rendering, and data fetching primitives, among others.

The features of Next.js we’ll focus on are the static site generation (SSG) ones. This type of rendering methodology resembles server rendering but mitigates some of its drawbacks, since the rendering pass is done at build time instead of at request time.

Figure 5.6: A sequence diagram for a pre-rendered/static site generation use case

Figure 5.6: A sequence diagram for a pre-rendered/static site generation use case

Now that we’ve looked at how static site generation changes the data flow when a user requests a website, we’ll look at Next.js automatic static generation.

Automatic static generation

In Next.js, the filesystem-based routing means that each path in your web application corresponds to a file in the pages directory of your application. For example, / corresponds to pages/index.js.

Next.js defaults to static generation when no Next.js data fetching methods are used for a given page. You can find more information from the Next.js documentation – Automatic Static Optimization (https://nextjs.org/docs/pages/building-your-application/rendering/automatic-static-optimization).

Next.js automatically determines that a page is static (i.e., can be prerendered) if it has no blocking data requirements. This determination is made by the absence of getServerSideProps and getInitialProps on the page.

For example, the following page in a Next.js application will be statically generated, since it only exports a page component (the default export of Index); no getServerSideProps or getInitialProps function is exported:

import React from...

Static generation with a third-party data source

Next.js has a getStaticProps data fetching method that allows us to load data at build time, which will be passed to a page.

The following sequence diagram illustrate what this involves:

Figure 5.8: A sequence diagram of Next.js pre-rendering using getStaticProps

Figure 5.8: A sequence diagram of Next.js pre-rendering using getStaticProps

For example, if we want to build a “product list” page based on fakestoreapi.com data, we can write the following getStaticProps method in a pages/products/index.js page:

export async function getStaticProps() {
  const products = await fetch
    ('https://fakestoreapi.com/products').then(
    (res) => res.json()
  );
  return {
    props: {
      products,
    },
  };
}

Here’s a product example in the response to illustrate the data shape:

{
  ...

Static generation with dynamic paths

It can be useful to pre-generate pages with dynamic paths and contents.

We could use getServerSideProps and render the pages on demand. In the context that we’re working in, that would be valid for a “cart” page.

getServerSideProps is server-side rendering, as we’ve seen previously. The reason a cart page should probably be server-rendered is that it can change very quickly, based on end user interaction. An example of a page that is dynamic but wouldn’t change quickly based on an end user action is a “view single product” page. We’ll see how to statically generate that after the cart page example.

We create a pages/cart.js file, where we provide the following getServerSideProps, which loads the cart, figures out the relevant product IDs (per cart content), and loads them (in order to display some information about them):

export async function getServerSideProps({ query }) {
 ...

Page hydration strategies

As we’ve seen in the first section of the chapter, react provides primitives to render applications on the server and the client. However, we only looked at examples where we did exclusively client or server rendering. One key feature of React frameworks such as Next.js is that they allow you to seamlessly switch between static, client, and server rendering. We’ll look at how to achieve this using React primitives.

Figure 5.16: A sequence diagram for a server-rendered page that is subsequently rehydrated on the client

Figure 5.16: A sequence diagram for a server-rendered page that is subsequently rehydrated on the client

We’ll start by extending our React client/server rendering app.jsx with a ClientCounter component. Event handlers are one of the simplest ways to observe interactivity primitives. Our ClientCounter component displays a counter that initializes with 0, and on every click of the Add button, it increments the count value. We put this component in a src/client-counter.jsx file:

import React, ...

Summary

In this chapter, we covered how a deeper understanding of rendering and page hydration strategies can help us deliver optimal and scalable web user interfaces with React.

Client and server rendering have benefits and drawbacks that are complimentary to each other. Client rendering takes longer to start up but provides more interactivity and doesn’t require as much server-side computer power; server rendering can return content faster but requires infrastructure and doesn’t provide the same level of interactivity.

The static site generation functionality of Next.js can be leveraged alongside classic server rendering to judiciously decide on a rendering strategy for a given set of pages, based on the access pattern and how often the content changes.

Finally, page hydration and rehydration alongside streaming server-side rendering bridges the gap between server and client rendering, allowing the benefits of both to be included in one page.

Now that we...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
JavaScript Design Patterns
Published in: Mar 2024Publisher: PacktISBN-13: 9781804612279
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Hugo Di Francesco

Hugo Di Francesco is a software engineer who has worked extensively with JavaScript. He holds a MEng degree in mathematical computation from University College London (UCL). He has used JavaScript across the stack to create scalable and performant platforms at companies such as Canon and Elsevier and in industries such as print on demand and mindfulness. He is currently tackling problems in the travel industry at Eurostar with Node.js, TypeScript, React, and Kubernetes while running the eponymous Code with Hugo website. Outside of work, he is an international fencer, in the pursuit of which he trains and competes across the globe.
Read more about Hugo Di Francesco