30 Days of NextJS — Part 1: Basic Understanding

Fredrik Erasmus
7 min readMar 11, 2024

--

Introduction

ReactJS 19 is coming. React Server Components (RSC) remains one of the most anticipated (or not) features. ReactJS is becoming a fullstack framework — targeting both server-side and client side. Reading through the ReactJS document, specifically the part from Which features make up the React team’s full-stack architecture vision?.

Next.js’s App Router bundler fully implements the official React Server Components specification. This lets you mix build-time, server-only, and interactive components in a single React tree.

Its worth noting NextJS uses two “different types” of routing — page routing and app routing. Page routing targets Server Side Rendering (SSR) whereas app routing uses React Server Components.

But what does it all mean if you come from “conventional approaches” to building web applications. In my mind one of the most conventional ways to build ReactJS apps is to use a web framework that loads ReactJS as a script inside your rendered HTML. Once the HTML has rendered ReactJS along with your code will be loaded by the browser. Your ReactJS code might then do other things like do HTTP calls to fetch data.

Next.js’s SSR renders components to HTML strings on the server using renderToString() and sends them to the client. The client then hydrates the HTML with the same data and state as the server. This requires that the server and the client have the same data and code to render the same components. Next.js’s SSR is faster than client-side rendering for the initial page load, but it also increases the client bundle size and the server load. Next.js’s SSR is the default mode for pages that use getServerSideProps or getInitialProps.

How does Server Side Rendering (SSR) differ from “conventional” approaches?

You might or might not have heard someone say — “but we already use server side rendering”. Generally the reference is to how conventional web frameworks render content. Using Express JS as an example — a client application such as a web browser would connect to a route rendering an HTML view. The web framework would process the incoming request, sending back the HTML response. The request/response pattern is a very common one not just for web frameworks; sometimes backend applications log requests and responses between applications. Web frameworks such as ExpressJS can also respond to requests with many different response types such as HTML or JSON. With HTML responses the web framework would render the markup using a view engine on the server before sending it back to the client. In many cases the HTML will include renferences to JavaScript. In many cases the JavaScript will have to code to perform AJAX requests using GET/POST verbs. Its important to understand that if the HTML is rendered with a single request by the web framework the JavaScript is only loaded after the HTML is returned to the browser. Once the scripts are loaded it will perform additional HTTP requests.

I have a basic diagram to illustrate the process of rendering the HTML response from an ExpressJS request, or any other web framework.

  1. Initial Page Request
  2. The client sends an HTTP GET request to the server for a specific page.
  3. The server handles the route, queries the database for necessary data, and renders the HTML including the JavaScript.
  4. The server responds to the client with the HTML page that includes the JavaScript function.
  5. JavaScript Execution & API Call:
  6. The browser renders the HTML and executes the JavaScript function included in the page on load.
  7. The JavaScript function then makes an API call to the Express server (e.g., to /api/data).
  8. The server handles the API request, queries the database for the data needed by the API, and responds with the data.
  9. Dynamic Page Update:
  10. The JavaScript function receives the API response and processes the data.
  11. The client’s page is updated dynamically based on the API response data, without needing to refresh the page.

How does SSR work?

In contrast NextJS (or frameworks using SSR) use JavaScript runtimes to execute code on the server. NodeJS is the most prevalent runtime with others like Deno or BunJS also being able to run JavaScript on the server. Since runtimes use JavaScript on the server they utilize something known as “Universal JavaScript” or “Isomorphic JavaScript”. It means the same code can run on the server and in the browser. ReactJS components can then be rendered on the server before sending the HTML to the client.

A basic diagram of the process.

A basic overview of the steps:

  1. On the server: Next.js runs your React components to generate HTML. It also prepares the initial state based on any data fetched during this phase. Next.js provide specific hooks or functions such as getServerSideProps (for Next.js) that run on the server before the page is rendered. These functions can perform any operations needed to fetch data, such as calling a database, accessing a REST API, or performing file operations. The data returned by these functions is then passed as props to your React components, which can use this data to render the initial HTML.
  2. On the client: The browser receives the HTML and displays the page. after the server-side rendered HTML is sent to the client and displayed by the browser, the JavaScript bundle for the Next.js application (which includes ReactJS among other client-side libraries and application code) is being loaded by the browser in the background.
  3. Hydration: Once the JavaScript bundle is loaded, Next.js “hydrates” the static HTML, attaching event listeners and enabling the full interactive experience of a React application. During hydration, React “rehydrates” the static content by attaching event listeners to the DOM elements, thereby enabling interactive features such as clicks, form submissions, and any other user interactions that your components are designed to handle. This process effectively turns the static server-rendered HTML into a fully interactive React application in the client’s browser.

What are the benefits of SSR?

One of the more conventional ways of writing ReactJS would have been to include all the required client side scripts in one or more <script> tags in your web framework's view engine HTML. The client side code would have been bundled into one or more minified JavaScript files referenced in the HTML. The bundled JavaScript code would also include your custom ReactJS components. When your HTML containing the scripts has finished loading the scripts will need to be loaded or initialized as well. In contrast Server Side Rendering presents the user with a fully formed HTML page without the need to wait for client side scripts to load. The fully formed HTML page makes for better SEO (Search Engine Optimization). Traditionally speaking Single Page Apps (SPA) were terrible for SEO because the content only loaded after the HTML page loaded. Search engine crawlers effectively saw no content. With SSR the content is available. Once the initial page is loaded NextJS starts the hydration process. Hydration is the process where React takes over the static content and turns it into a fully interactive web application in the browser. During hydration, React attaches event listeners to the DOM elements and reconciles the static content with the React components in the JavaScript bundle. This process transforms the static page into a dynamic SPA (Single Page Application) that can handle user interactions, fetch data dynamically, and navigate between pages without reloading. Hydration offers several benefits.

  1. Seamless User Experience: By starting with server-rendered HTML and then enhancing that with client-side interactivity, Next.js applications can offer a smooth, app-like experience. Users can start interacting with the page even before the JavaScript has fully kicked in, and once hydration is complete, they get the full dynamic capabilities of a React application.
  2. Performance Optimization: Since the initial view of the page doesn’t rely on JavaScript execution, the perceived performance is significantly improved. Additionally, because the same React components are used on the server and the client, React can efficiently attach event handlers and update the components without needing to redo the initial render.
  3. Unified Development Model: Developers can write components and logic that work on both the server and the client, simplifying the development process and reducing the need for duplicative code. This isomorphic approach also allows for optimizations like code splitting and lazy loading to be handled more effectively, further improving performance.

React Server Components (RSC)

NextJS’s app router is built using React Server Components (RSC). I’ll be honest I initially thought RSC and SSR were related but its not quite the same. If you read about routes on NextJS you will notice a key point.

In version 13, Next.js introduced a new App Router built on React Server Components, which supports shared layouts, nested routing, loading states, error handling, and more.

The details around NextJS’s app router and React Server Components are explained here. I will uncover the React Server Components rendering in the next post.

Final thoughts

I have set myself a goal of trying to write NextJS code for the next 30 days. My first day was spent covering the basics of SSR comparing it with conventional approaches. In the days that are coming I am going to delve deeper into NextJS. It is important to write code as well though. I don’t want to fall into the trap of doing too much “fake learning”.

--

--

Fredrik Erasmus

Software Imagineer. Eager to learn new things. Focused on solving problems. Mainly focused on ASP.NET Core, C#, Azure.