A detailed guide on how to implement Server-side Rendering (SSR) in a NextJs application

A detailed guide on how to implement Server-side Rendering (SSR) in a NextJs application

·

9 min read

Introduction

Server-side Rendering (SSR) is becoming increasingly important in web development due to its ability to improve website performance and user experience. Unlike Client-side Rendering (CSR), where the website's content is generated on the user's device, server-side rendering generates the HTML on the server and sends it to the client. This method can improve website load time, search engine optimization, and accessibility.

Next.Js is a popular framework for building React applications, and it offers built-in support for server-side rendering. With Next.js, we can easily set up our application to generate HTML on the server and deliver it to the client, providing a seamless user experience and optimized website performance. In this detailed guide, we will build a cryptocurrency web app to show how to implement SSR in a Next.js application.

We will also cover the basic concepts behind server-side rendering and walk through the steps required to set up SSR in our Next.js application. By the end of this article, you will have a solid understanding of improving your website's performance and SEO by implementing SSR in your Next.js application.

Pre-rendering: A built-in Feature in Next.js

Regarding page rendering with Next.js, pre-rendering is a fundamental component. It is a crucial feature of Next.js, which means that static HTML content is generated in advance rather than dynamically on each request. When comparing the page source of a traditional React.js web app and a Next.js web app, it is clear that the Javascript code is loaded before the contents are rendered to the user, which is a bad user experience. However, when inspecting the contents of a Next.js page source, the HTML is already generated with all the necessary data, making Next.js the most efficient method for improved web performance and user experience.

Next.js gives us the option of selecting one of two pre-rendering modes:

  1. SSG (Static Side Generation): This is Next.js's default pre-rendering mode, in which HTML pages are generated at build time and served to the user as static files. This approach is appropriate for websites with static content because it reduces server load and provides the fastest possible performance.

  2. Server-side rendering (SSR): SSR, on the other hand, generates HTML pages on the server whenever a user requests them. This approach is helpful for websites with frequently changing content or that require dynamic data because it can provide a more responsive user experience while ensuring the content is always up to date.

Understanding the Server-Side Rendering Process

Server-side rendering is fast becoming a popular technique widely used in web development that involves rendering a web page on the server before sending it to the client, unlike client-side rendering, where the page is rendered in the browser first after the server has sent the necessary HTML, CSS, and JavaScript bundled files.

To fully understand the server-side rendering process, knowing the key players involved is crucial. It includes the server and the client.

The server is responsible for handling all incoming requests made from the client side and sending the appropriate response. In the context of SSR, this involves rendering the requested web page on the server and sending the resulting HTML, CSS, and JavaScript to the client.

The client is the web browser through which a user accesses the web application. In SSR, the client gets the rendered HTML, CSS, and JavaScript from the server and displays the contents on the web page.

Now that we've identified the two major players in server-side rendering let's look at the thought process behind it. The client requests the server for a specific web page as the first step in the server-side rendering process.

The server will receive the request and determine which page the client is looking for. The server will then render the requested page on the server, which includes generating the page's HTML, CSS, and JavaScript and compiling them into a complete web page.

After rendering the web page on the server, the server will send the resulting HTML, CSS, and JavaScript to the client. The client will then use these files to show the user the web page.

Implementing SSR with Data Fetching in Next.js

Data fetching is an essential part of developing any web application. Next.Js provides several methods for retrieving data, including server-side rendering, static site generation, client-side rendering, incremental static regeneration, and dynamic routing. However, for this article, we will only look at server-side generation. You can learn about the other types by reading the Next.js documentation.

getServerSideProps: a built-in Function for Data Fetching in Next.Js

Next.js includes a built-in function called getServerSideProps that allows us to fetch data from the server with each request. To use server-side rendering on a page, we must export getServerSideProps, and the server will call this function on every request.

getServerSideProps Syntax

export default function Page( {data} ){
   return <>YOU CAN DISPLAY YOUR DATA ACCORDINGLY</>
}
export async function getServerSideProps() {
   // Your code
   const data = .... ;

   // Passing data to the page using props
   return {
       props : {data}
   }
}

In place of data we can use a different variable name. We can also pass multiple props by using commas ", and" to separate them.

Key Notes about getServerSideProps

  1. getServerSideProps only runs on the server and never on the client.

  2. It runs at the request time, and the web page is pre-rendered with the returned props specified inside.

  3. getServerSideProps can only be exported from a page. You cannot export it from non-page files. It will not work if you make getServerSideProps a page component property.

  4. getServerSideProps would only render a page whose data must be fetched at the requested time. If the data does not need to be rendered during the request, consider fetching it on the client side or using static side generation.

  5. The getServerSideProps function must return an object containing the data that will be passed to the page component as props. If the function does not have a return statement, it cannot pass data to the page component.

Fetching data using getServerSideProps

Step 1: Setting up a Next.Js Application

To get started, we have to set up a development environment where we can start building our web app. If you have ever built a React app, you are familiar with create-react-appwhich sets up the development environment. Next.Js has its command called create-next-app. Run the following command via a terminal. I am using npm it as my package manager, but you can use your preferred package manager's commands instead:

npx create-next-app crypto-web-app

This command will create a new Next.js project and all other app dependencies in your folder directory.

Once all of the dependencies have been installed, navigate to the crypto-web-app directory by running

cd crypto-web-app

To start the development server, run:

npm run dev or yarn dev

To view the application, go to localhost:3000.

Step2: Getting a third-party API endpoint

In the pages/index.js file, we will get the top nine cryptocurrency coins by market value from CoinGecko, an external API. coingecko's API documentation is an excellent source to find our endpoint. The endpoint we require is in the coins category.

CoinGecko API End point:

 <https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=9&page=1&sparkline=false>

Step 3: Declaring the getServerSideProps function.

Add the following code to our index.js file:

import React from "react";
import styles from "/styles/Home.module.css";
import { FiArrowDown, FiArrowUpRight } from "react-icons/fi";

function Home({ data }) {
  return (
    // Renders the data passed as props from the getServerSideProps
    <div className={styles.container}>
      <h1>Cryptocurrencies by Market Cap</h1>

      <div className={styles.crypto__container}>
        {data.map((crypto) => (
          <div key={crypto.id} className={styles.crypto__child}>
            <img src={crypto.image} alt={crypto.symbol} />
            <h3>{crypto.name}</h3>

            <div className={styles.crypto__price}>
              <p>$ {crypto.current_price.toLocaleString()}</p>
              {crypto.price_change_percentage_24h < 0 ? (
                <span className={styles.arrow__down}>
                  <FiArrowDown className={styles.price__icon} size={20} />
                  {crypto.price_change_percentage_24h.toFixed(2)}%
                </span>
              ) : (
                <span className={styles.arrow__up}>
                  <FiArrowUpRight className={styles.price__icon} size={20} />
                  {crypto.price_change_percentage_24h.toFixed(2)}%
                </span>
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// This function gets triggered on every request
export async function getServerSideProps() {
  // This fetches the data from the Coingecko external API
  const response = await fetch(
    "<https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=9&page=1&sparkline=false>"
  );
  const data = await response.json();

  // Pass data to the page component via props
  return {
    props: {
      data,
    },
  };
}

export default Home;

We declared the async getServerSideProps function and used JavaScript's built-in fetch API function to retrieve data from the CoinGecko API endpoint. Remember that we must declare a return statement to pass the data obtained from the API to our page component.

We accepted the props passed by the getServerSideProps method in our Home function component. We used the JavaScript map() method to display a list of cryptocurrencies sorted by market value as a child element of the component.

Here is the CSS to add more styling to the web app:

@import url('<https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;600;700&display=swap>');

.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100%;
  padding: 100px 0;
  font-family: 'Nunito Sans', sans-serif;
}

.container h1 {
  margin-bottom: 40px;
}

.crypto__container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  grid-column-gap: 25px;
  grid-row-gap: 80px;
}

.crypto__child {
  box-shadow: rgba(0, 0, 0, 0.16) 0px 10px 36px 0px, rgba(0, 0, 0, 0.06) 0px 0px 0px 1px;
  padding: 30px 15px;
  border-radius: 7px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.crypto__child img {
  width: 50%;
  margin-bottom: 7px;
}

.crypto__child h3 {
  font-size: 20px;
  margin: 7px 0;
}

.crypto__price {
  display: flex;
  align-items: center;
  gap: 10px;
}

.arrow__down {
  color: red;
  display: flex;
  align-items: center;
}

.arrow__up {
  color: green;
  display: flex;
  align-items: center;
}

/* Mobile */
@media (max-width: 700px) {
  .container {
    padding: 70px 0;
    font-family: 'Nunito Sans', sans-serif;
  }

  .container h1 {
    margin-bottom: 30px;
    font-size: 25px;
  }

  .crypto__container {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: repeat(9, 1fr);
    grid-column-gap: 0;
    grid-row-gap: 20px;
  }

  .crypto__child {
    padding: 25px 13px;
  }

  .crypto__child img {
    width: 50%;
  }

  .crypto__child h3 {
    font-size: 17px;
  }
}

/* Tablet and Smaller Desktop */
@media (min-width: 701px) and (max-width: 1120px) {
  .container {
    padding: 80px 0;
  }

  .container h1 {
    margin-bottom: 30px;
  }

  .crypto__container {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(5, 1fr);
    grid-column-gap: 25px;
    grid-row-gap: 80px;
  }

  .crypto__child img {
    width: 70%;
  }

  .crypto__child h3 {
    font-size: 19px;
  }

  .crypto__price {
    display: flex;
    align-items: center;
    gap: 10px;
  }
}

If we go to localhost:3000 in our browser, we should see a list of nine cryptocurrencies fetched on each request to the server before being returned to us in the browser.

Conclusion

Server-side rendering (SSR) is a modern method in modern web development that offers numerous advantages, including improved performance and SEO, faster interaction time, better accessibility, a better user experience, and easier maintenance.

Finally, implementing Server-Side Rendering (SSR) into a Next.js application can yield significant benefits. However, it is critical to understand that implementing SSR can add complexity to the application; as a result, it is critical to carefully consider the use cases and opt for other types of pre-rendering modes that Next.js provides.

Nonetheless, implementing SSR in a Next.js application with the right approach can help to provide a more robust, faster, and smoother web experience for users, leading to higher engagement and better outcomes for web applications.

Resources