The Evolution of Web Architecture
Shout out to Theo. He’s a cool guy, and I’ve learned a lot from him. In this article, I reference a lot of content from his YouTube video.
Front-end technology changes rapidly. We used to develop websites using the Multi-Page Application (MPA) architecture. Then React came along, allowing us to update and render HTML on the client side, known as Single Page Applications (SPA). This makes client-side page transitions smooth, like using a native app. However, there are also several issues with this approach. To address some of these performance and experience problems, the React team introduced Server Components in React 18, allowing us to write UI components that can be rendered and cached on the server side.
However, before we begin, we need to understand the evolution of front-end rendering technologies from the past to the present, and why SPAs emerged. Next, we will discuss the issues with SPAs and how these issues led to the development of SSR.
Multi-Page App (MPA) Model
In the beginning, our website architecture was implemented by rendering multiple pages of HTML on the server side. When a user clicked on a link, we handled this link on the server and returned an HTML document. Then, the user’s browser would automatically load this HTML document into the webpage. The website architecture was as follows:
Single-Page App (SPA) Model
With React’s introduction, the SPA architecture has gained popularity. It greatly enhances the user browsing experience on websites. Unlike MPA (Multi-Page Application), SPA enables seamless page transitions without the page flickering during navigation. This architecture also allows for a complete separation between client and server sides. In case of server issues, the front end can handle errors gracefully. This rendering approach is known as Client-Side Rendering (CSR). However, this architecture also presents some challenges:
- Users’ devices have more work to do. For example, using JavaScript to dynamically update HTML content, manage user sessions, and handle page viewing permissions.
- HTML metadata and content require updates through JavaScript. Thus, search engine crawlers may only see an unrendered page (blank page), potentially impacting SEO. While modern search engines like Google can process and index some JavaScript-generated content, it doesn’t guarantee full indexing of all content.
- SPA architecture may consume more memory. Initially loading a SPA requires loading a significant amount of JavaScript, CSS, and other resources, and maintaining application state on the client side. Moreover, long-running background tasks and improper memory management may lead to memory leaks, burdening user devices.
- Users rely heavily on APIs to fetch website content, including dynamic or time-sensitive content. This necessitates the backend to produce numerous RESTful APIs and requires extensive error handling in frontend fetch operations.
Of course, there are some solutions, such as:
- Using frameworks like Next.js shifts responsibilities such as permissions, state management, and data fetching back to the server-side through deployed web servers. This allows the frontend to focus solely on enhancing client-side experiences.
- Implementing Server-Side Rendering (SSR) ensures that search engine crawlers can access complete page content, enhancing SEO.
- Employing techniques like Code Splitting, Lazy Loading, State Management, and Memory Management helps efficiently control memory usage, ensuring website performance and stability.
- Utilizing GraphQL enhances efficiency and flexibility in querying API data.
After all that talk, let’s take a look at what an SPA architecture looks like!
After all that talk, let’s take a look at what an SPA architecture looks like!
Server Side Rendering
Full Page SSR: Rendering Entire Web Pages on the Server
Full Page SSR refers to the behavior where HTML is generated and rendered on the server-side. The entire HTML page is generated and then sent to the browser. In contrast, SPA architecture sends HTML, CSS, and JavaScript to the client-side and uses JavaScript execution on the client to generate and render content. Here are a few pros and cons listed simply:
Pros:
-
SEO-friendly: Since search engine crawlers can directly see the complete HTML, it benefits SEO.
-
Fast initial page load (above the fold): Because the server generates the complete HTML, the browser can display the page immediately upon receipt, improving the speed of the initial page load and user experience.
-
Better compatibility: It does not rely on client-side JavaScript execution, so it can function properly in older browsers or browsers with JavaScript restrictions.
-
Avoids the need to parse a large amount of JavaScript code initially, unlike SPA. Consequently, the JavaScript bundle size is typically much smaller compared to SPA architectures.
Cons:
-
Increased server load: Servers need to handle more rendering tasks, which can potentially increase server load.
-
Longer response times: Server-side rendering involves data processing and HTML generation on the server, which may increase website response times.
-
Development complexity: Handling mixed server-side and client-side logic increases the complexity of development.
Let’s take a look at the architecture of Full Page SSR:
It’s worth mentioning that after the page completes Hydration (a phase in Browser Rendering), you can update the UI by fetching website data via API without needing to refresh the page. This contrasts with Multi-Page Applications, which often require page refreshes. However, since the introduction of server components (in React 18), this architecture has become even more powerful. Let’s delve into how integrating server components will alter the SSR architecture!
Server Component SSR: Streaming Server Rendering with Suspense
Let’s imagine the Full Page SSR architecture we discussed earlier, where the entire page is rendered on the server side. Now, what if individual components could also be independently rendered on the server? Instead of sending the entire HTML page, could we transmit pre-rendered components to the client using streaming techniques to reduce website response times?
Moreover, let’s take this a step further. If components can be server-rendered, could we split them, pre-render static components, and cache them? Then, whenever the client needs a component, it could fetch it directly from the cache. This approach not only resolves slow website response times but also reduces server load. The key to making this possible is the emergence of server component. They enable us to achieve these optimizations.
Finally, let’s explore how the introduction of server components enables the implementation of streaming rendering architecture! Also very recommended to watch this video.