MFS202 : Table of Contents

Chapter 1

Lesson 2

The Philosophy of Remix: Introduction to Modern Web Development

Experience the zen within the philosophy of Remix and how it solves the problems faced by web developers. You'll also get an overview of the unique features of Remix, such as its focus on web standards, modern web app UX, and its use of the Web Fetch API instead of Node.

Quiz it to win it

Take the quiz

3 Questions
12 XP

In our previous lesson, we traced the evolution of the web. We began with static websites, transitioned to server-side rendered dynamic sites, then moved on to Single Page Applications (SPAs), and eventually saw a resurgence in server-side rendering.

In this lesson, we will focus on Remix, take a look at some of its best features, and provide a high-level overview of its internal technical details.

Remix is a full-stack web framework built on top of React, following web standards. We will understand what this exactly means as we progress through this lesson.

Let's look at them one by one.

Server Side Rendering


While learning the history of the web, we saw that one of the eras was of Single Page Application (SPA), where the browser receives an almost empty HTML document, along with JavaScript. This JavaScript is then parsed and starts rendering components' loading state while fetching data for them. This data fetching is recursive. A parent component may have nested child components that fetch their own data, but the parent won't know about them until it fetches its own data.

This often results in a network waterfall request chain, chaining one network request to another. It degrades the user experience as data is fetched one by one for nested components.

The official Remix website homepage has a very good visualization explaining how the network waterfall request chain can lead to an unpleasant user experience.

Several frameworks, such as Gatsby, Hugo, etc., try to solve this using Server Side Generation (SSG). In SSG, all the data for all the pages is fetched during the build time, and all the pages are generated during this phase. The browser receives a fully rendered webpage with all the content. This may solve the majority of the issues that a traditional SPA encounters. The website is fast, as there is no rendering involved on demand, and since the document contains all the content (unlike SPA, where the document is empty), it is great for SEO.

But there are some problems:

Since the data is fetched during the build time, this approach makes sense only if the data on the website rarely changes. And to make even small content changes on the website, the whole site needs to be rebuilt. The build step itself can become very long, as it renders all the pages on the website during build time.

While SSG offers notable advantages over client-side rendered SPAs, it might not be the ideal solution for many modern websites, particularly those with dynamic data. As a result, there has been a renewed interest in Server Side Rendering (SSR), a technique that has its roots in the 1990s.

In Server Side Rendering, the site is generated on demand. This means every time someone visits a page, the data for that page is fetched at that moment and is used to render the document and send it to the browser. This approach is slower compared to SSG (we will see how to make it almost as fast as SSG later), but now our content is generated on demand. And our build step is a lot faster.

SSR combined with Nested Route


However, since the data fetching now happens on the server, the user doesn't see anything until all the data is fetched for the whole page and the page is rendered and sent to the browser. If this was a client-side rendered app, we could have implemented a loading spinner while the data is fetched on the client side. This is more problematic when we need to fetch a large amount of data that are not directly related to each other.

Acknowledgment

The official Remix website has a great example explaining nested routing. We will use them for explanation in this lesson, in a lot more detail.

Remix solves this with Nested Routes and parallel data fetching. Suppose we are building a sales dashboard. Here, we are fetching two different types of data on the individual invoice page, among many others:

A list of all the invoices.

And data on the individual invoice.

In traditional SSR apps, data for an entire route is fetched at once. For instance, if we're on the invoices/1231 page, both the entire list of invoices and the details for invoice 1231 are loaded. When navigating to invoices/1233, since the URL changes, the app would typically refetch all data, including the complete invoice list, even though the list hasn't changed. This is redundant and inefficient.

Remix takes a different approach. In a Remix website, we can create /invoices and invoices/${invoiceID} as different URLs nested within each other. Consequently, the list of invoices will be downloaded once for the /invoices route, and this whole component is linked to the route. This means the data will be fetched only once and won't need to be refetched in nested routes when the route changes.

And, the code for route /invoices/${invoiceId} is only responsible for fetching the individual invoice component.

Remix knows what component to fetch using URL. For example, here, the URL is example.com/sales/invoices/102000. It knows that it needs to

fetch the root data

sales data,

data for the list of invoices

and individual details on invoice 10200.

And since it now knows what exactly to fetch for a URL, it can fetch them in parallel. This reduces the time it needs to generate the document, and the browser can receive the fully rendered page much faster.

This can be further optimized by enabling React Streaming. It is a bleeding-edge feature introduced in React 18, which allows sending parts of pages as chunks as they get ready. Since the data is being fetched in parallel, we can send the parts of UI related to those data as they get ready. We will learn more about streaming in detail in a later lesson.

Here, we only saw a high-level overview of how nested routes in Remix make it faster. Later in this course, we have a dedicated lesson on Nested routes, where we go into even more detail and talk about the implementation and some of the best practices when using nested routes.

Error Boundary


One added benefit of nested routing is that it allows adding route level Error Boundary. This means if a part of the nested layout is broken, the error will be shown only in the part of UI related to that part of UI, and the rest of the app continues to work as usual. For example, say for some, our individual invoice route is not getting correct data and is thus broken. We will see the error data only in the individual route component while the rest of the website is functional.

Mutations with actions


A framework isn't truly fullstack unless it has a built-in method for data mutation. Remix utilizes APIs that allow for data mutation using the traditional approach of HTML forms. This seamlessly integrates with the native HTML form element. In addition to this, Remix offers a specialized Form component. This component is built on top of the HTML form element and brings additional features. It helps prevent race conditions, enables revalidation without a page reload, supports progressive enhancement, and offers many other advantages.

In this example, we are managing a simple to-do list. The loader function runs on the server and fetches a list of to-dos from the mock database.

The Todos component then renders this list and provides a form, utilizing the specialized Form component from Remix, for users to add new items.

Upon form submission, the action function takes over. It processes the form data to extract the title, creates a new to-do using the fakeCreateTodo mock database function, and then redirects the user to a page displaying the specific to-do item.

We will learn about all of this in detail in later lessons.

Deploy Anywhere: You own your architecture


Since Remix is built on the Web Fetch API instead of Node.js, this enables it to run anywhere. This can be any Node.js server like Vercel, Netlify, Architect, etc., as well as non-Node.js environments like Cloudflare Worker and Deno Deploy on the edge.

Remix relies on standard Web APIs, but not all server runtimes support these APIs to the same extent. Therefore, the Remix runtime package provides polyfills to bridge any gaps in these features for a given runtime. These polyfills encompass web standard APIs such as Request, Response, and crypto, among others. These are called Adapters in Remix.

In this example, we can see that we can import specific helper functions, such as createCookieSessionStorage, in this case, from the adapter, based on our server runtime.

If our app is deployed on a Node.js server, then the import will be from @remix-run/node.

But, if we deploy our app on Cloudflare runtime, such as Cloudflare Pages or Cloudflare workers, we will import the same function from @remix-run/cloudflare

We have a lesson dedicated to choosing the best deployment option for a fullstack Remix app.

Decomposing Remix: Understanding Its Four Core Components


Remix has four core parts: 1. A compiler, 2. A server-side HTTP handler, 3. A server framework, and 4. A browser framework.

At its core, Remix uses a compiler that creates a server HTTP handler, a browser build, and an asset manifest.

While Remix operates on the server, it isn't a server itself; it functions as an HTTP handler given to an actual JavaScript server, which essentially means it processes incoming HTTP requests and responds to them. This modular design allows Remix to be integrated into various existing JavaScript servers. Adapters help integrate Remix with specific servers by converting APIs accordingly.

Remix distinguishes itself by focusing on the UI rather than just the model, as traditional server-side MVC frameworks do. Routes in Remix can handle full or segmental URLs, which become nested layouts in the UI. This allows for a cohesive mix of the UI and controller functions in one file, improving developer efficiency.

This example, that we saw earlier, shows how we can handle everything about a route (or part of a route) in a single file. Here, we are loading data, rendering the UI, and responding to form events on the server in the same file. This is the server framework part of Remix.

On the browser side, once a document has been served, Remix "hydrates" it with JavaScript modules. It enhances user experience by fetching only the necessary data for the next page during navigation, thus avoiding redundant data pulls and maintaining elements like scroll position.

Remix also intelligently prefetches resources based on user navigation patterns, ensuring a swift response even in slow networks. The framework also provides client-side APIs, allowing developers to enhance user experiences without deviating from the fundamental browser model. The essence of Remix lies in its seamless integration of back-end and front-end experiences.

Furthermore, Remix emphasizes Progressive Enhancement, enabling developers to start with basic functionalities and build up without changing the core structure.

Conclusion


In this lesson, we saw some of Remix's superpowers. By combining Server Side Rendering (SSR) with Nested Routes, Remix optimizes data fetching, reduces page load times, and enhances the user experience. It also provides error boundaries, mutation capabilities, and the flexibility to deploy in various environments, making it a versatile choice for modern web development. Remix's four core components, including a compiler, server-side handler, server framework, and browser framework, work seamlessly together to offer a unified and efficient approach to building web applications.

In the coming parts of this course, we will learn about all of them in detail, along with the implementations and examples.

To better understand all the concepts, we will develop a full-stack remix app as we progress through the course. In the next lesson, we will see what the project is, discuss the tech stack, and set up the project.

Quiz it to win it

Complete this quiz successfully to proceed to the next lesson and win upto 12XP.

Start quiz for this lesson

Completing this quiz will get you

+12 Experience Points

+7% course progress