DEV Community

Cover image for React Design Pattern /Client-side Rendering
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

React Design Pattern /Client-side Rendering

Client-side Rendering
In client-side rendering (CSR), only the basic HTML structure of a page is rendered by the server. The JavaScript code that executes in the browser/client handles the logic, data fetching, template and routing required to display content on the page. CSR became popular as a method of building single-page applications.

Basic structure
This is an example for showing and updating the current time on a page using React.

・index.html

<div id="root"></div>

Enter fullscreen mode Exit fullscreen mode

・index.js


function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(element, document.getElementById("root"));
}

setInterval(tick, 1000);
Enter fullscreen mode Exit fullscreen mode

The HTML consists of a single root div tag. Content display and updates are handled in JavaScript, however.
There is no round-trip to the server, and the rendered HTML updates in place. In this example, 'time' could be replaced by other real-time information, such as exchange rates or stock prices obtained from an API. This information is displayed without the need to refresh the page or make a round-trip to the server.

JavaScript Bundles and Performance
As pages become more complex in order to display images, data from a data store and event handling, the size and complexity of the required JavaScript code will also increase. CSR resulted in large JavaScript bundles, which increased the First Contentful Paint (FCP) and Time to Interactive (TTI) of the page.

Pros and Cons
In React, most of the application logic is executed on the client and interacts with the server via API calls to fetch or save data. Consequently, almost all of the UI is generated on the client. The entire web application loads on the first request. As the user navigates by clicking on links, no new requests are generated to the server to render the pages. The code runs on the client to change the view/data.

CSR enables us to create a single-page application that supports navigation without refreshing the page and provides an excellent user experience. As only limited data is processed to change the view, routing between pages is generally faster, making the CSR application seem more responsive. CSR also enables developers to clearly separate client and server code.

Although it provides a great interactive experience, there are a few pitfalls to this CSR.

  1. SEO considerations: Most web crawlers can interpret server-rendered websites easily. However, things become slightly more complicated with client-side rendering, as large payloads and a series of network requests (e.g. for API responses) may result in meaningful content not being rendered quickly enough for a crawler to index it. While crawlers can understand JavaScript, there are limitations. Therefore, some workarounds are required to optimise client-rendered websites for SEO.

  2. Performance: Client-side rendering greatly improves response times during interactions as there is no round-trip to the server. However, browsers have to wait for the JavaScript to load and start processing before they can render content for the first time. Consequently, users may experience some lag before the initial page loads. This may affect the user experience if JS bundles become larger and/or the client does not have sufficient processing power.

  3. Code maintainability: Some elements of the code may be repeated in different languages across the client and server APIs. In other cases, achieving clean separation of business logic may not be possible. Examples of this include validations and formatting logic for currency and date fields.

  4. Data fetching: with client-side rendering, data fetching is typically triggered by an event. The page may initially load without any data. Data can then be fetched when events occur, such as when the page is loaded or a button is clicked, using API calls. Depending on the size of the data, this could increase the application's load/interaction time.

The importance of these considerations may differ depending on the application. Developers are often interested in finding SEO-friendly solutions that can serve pages faster without compromising interaction time. The priorities assigned to different performance criteria may vary based on application requirements. Sometimes, using client-side rendering with some adjustments instead of a completely different approach may be sufficient.

Improving CSR performance
Since the performance of CSR is inversely proportional to the size of the JavaScript bundle, the best approach is to structure our JavaScript code to allow for optimal performance. The following pointers could help.

Budgeting JavaScript
Make sure you have a reasonably tight JavaScript budget for your initial page loads. A good starting point is an initial bundle of 100–170 KB minified and gzipped. Code can then be loaded on demand as features are required.

Preloading
This technique can be used to preload critical resources required by the page earlier in the lifecycle of the page. These resources may include JavaScript, which can be preloaded by adding the following directive to the HTML's head section.

<link rel="preload" as="script" href="critical.js" />

This tells the browser to start loading the critical.js file before the page rendering mechanism begins. This means that the script will be available earlier and will not block the rendering process, thereby improving performance.

Lazy loading
Lazy loading allows you to identify non-critical resources and only load them when needed. This approach can improve initial page load times as the size of the initial load is reduced. For instance, a chat widget component is usually not required immediately upon page loading and can therefore be lazy-loaded.

Code Splitting
To avoid creating a large JavaScript bundle, you could start splitting your bundles. Code splitting is supported by bundlers such as Webpack, and can be used to create multiple bundles that can be loaded dynamically at runtime. Code splitting also enables you to lazy-load JavaScript resources.

Application shell caching with service workers
This technique involves caching the application shell, i.e. the minimum amount of HTML, CSS and JavaScript required to power a user interface. Service workers can be used to cache the application for offline use. This can be useful for providing a native single-page app experience, with the remaining content loading progressively as needed.

Top comments (0)