So, how does Remix work behind the scenes? Based on what we have learned so far, Remix seems to do quite a lot. In this section, we will have a look behind the curtain. This will help you understand some of the responsibilities Remix takes on.
Remix offers a fantastic developer experience but can also feel like magic. A bit of magic goes a long way, but it can also be overwhelming when one doesn’t understand what is happening. Magic is also hard to troubleshoot if something goes wrong. That is why I want to reveal some of Remix’s inner workings even before creating our first Remix project.
We can identify three distinct responsibilities that Remix takes on:
- Remix bundles your code
- Remix manages routing in your application
- Remix handles incoming HTTP requests
Based on the identified responsibilities, Remix can be broken down into three main components: a compiler, a router, and a runtime. In the following sections, we will examine each component. To start, let’s take a closer look at how Remix operates as a compiler.
Remix is a compiler
Remix compiles your file-based route modules into code. It infers the route structure from the file and folder hierarchy. For each route module, Remix inspects what functions it exports and translates the routes folder into a data structure used during runtime. This makes Remix a compiler.
One thing you will notice while working with Remix is the speed of its build step. This is thanks to esbuild, the build tool used by Remix. Remix doesn’t expose esbuild. Hence, Remix can adapt how it bundles your code and be competitive in the future by using the latest and greatest build tools. If an even faster, more powerful build tool is released, Remix could switch out esbuild tomorrow.
Remix builds on top of esbuild to bundle your JavaScript files into the following:
- A server bundle
- A client bundle
- An asset manifest
Remix’s command-line interface (CLI) builds your code into a client and server bundle. The server bundle contains Remix’s HTTP handler and adapter logic. This is the code that runs on the server. The client build contains client-side scripts that operate Remix’s client-side React application.
Remix also compiles an asset manifest based on your route hierarchy. Both the client and server use the asset manifest, which includes information about the dependency graph of your application. The manifest tells Remix what to load and allows prefetching assets and application data for page transitions.
Speaking of page transitions…
Remix is a router
Remix implements a router for both your client and server code. This eases the server-to-client handoff. The deep integration between the frontend and backend makes Remix bridge the network gap. This allows Remix to do some neat things. For instance, Remix calls your route loader
functions in parallel to avoid request waterfalls.
We can simplify things and say that Remix uses React Router under the hood. To be more specific, Remix uses both react-router-dom
and @remix-run/router
. @remix-run/router
is a frontend library/framework-agnostic package used by React Router v6 and Remix. React Router and Remix have been aligned and share a similar API surface. In many ways, you can think of Remix as a compiler for its underlying routing solution.
Remix has a client-side and a server-side part, as does its routing solution. The React components exposed by Remix make sure your application feels like an SPA. Remix handles form submissions and link clicks. It prevents the browser’s default behavior if JavaScript is loaded but can fall back to full page loads if necessary. All that is managed by Remix’s router at runtime.
Speaking of runtimes…
Remix is a runtime
Remix runs on an existing server, such as an Express.js Node server. Remix provides adapters to create combability with different server-side JavaScript environments. This enables Remix’s HTTP handler to be agnostic to the underlying server.
The adapter translates incoming requests from the server environment to standard Request
objects and reverts the HTTP handler’s responses back to the server environment’s response implementation.
Remix receives HTTP requests via the JavaScript server and prepares the responses. Remix’s router knows what route modules to load and render and which assets to fetch. On the client, Remix hydrates your React application and orchestrates the routing and data fetching. As a framework, Remix provides the foundation for your application and executes your application’s code. Next, let’s have a look at what Remix is not.
What Remix is not
Earlier in this chapter, we introduced Remix and explored many of the tools and features that it provides. Remix is a full stack web framework, but it is also essential to understand what Remix is not. Most importantly, Remix is not one of the following:
- A JavaScript server
- A database
- An Object Relation Mapper (ORM)
- A cloud provider
- A styling or theming library
- A magic crystal ball
Remix is neither a server nor a JavaScript engine. Remix runs on a JavaScript environment such as Node.js and uses adapters to communicate with a web server such as Express.js. Remix also provides no solutions for the data layer of your application. It helps you load and mutate data, but it is your job to implement those data loaders and actions. It is your job to select a database solution that fits your use case. Remix is also not an ORM. Hence, you must query your data in your actions and loaders, define your data types, or use third-party libraries for support.
The company behind Remix does not act as a cloud provider or offer a cloud hosting service. You can host your Remix application almost anywhere where JavaScript can be executed. Many cloud services support Remix out of the box, but Remix as a company does not offer any hosting services.
Remix is also not a styling or theming library. Remix is opinionated on how to work with CSS, but its solution is generic. Remix provides no tools for styling or theming other than utilities to load your stylesheets on a prefix route level.
Most things listed are out of scope for Remix, but some might be up for change in the future. For now, let’s focus on the present. In this first chapter, we have learned a lot about the many features Remix has to offer. Most of the things mentioned happen behind the scenes. This book will guide you through each aspect of Remix step by step. Each chapter will focus on a specific topic, such as routing, data fetching and mutation, error handling, and state management. By examining these topics one by one, we will explore the significance of Remix. I am certainly excited to start coding!