What is Remix?
Remix is a full stack web application framework with some unique design decisions centered around application performance, developer experience, and portability. It’s part of a recent trend around JavaScript frameworks that move most rendering and logic off of the client and into the server. Remix in particular encourages developers to deliver as little JavaScript to the client as possible.
Starting with the advent of AJAX and increasing through the introduction of front-end frameworks like React and Angular, web applications have seen more and more server-side functionality being moved into client-side JavaScript code. For the frontend of a typical web application, things like template rendering, handling data mutations, handling routing (e.g. functioning as an SPA), and fetching data from the backend API are all now responsibilities of the frontend.
It’s interesting that this trend has now been reversing in recent years, with new frameworks like Next.js, Svelte, and Remix which move responsibilities back to the server.
How does Remix work?
Like other full stack frameworks, Remix provides a development experience that combines frontend and backend code into a single application, and does it using an approach that will feel familiar to frontend developers. This allows developers to quickly build out a full stack application in a few lines of code. The example below demonstrates a very bare bones Remix application.
|
|
If you’ve ever built an application with React, all of this will look familiar. You’ll notice that two
hooks are in use here: useLoaderData
and
useTransition.
These hooks give us the data to render the UI and the state of the application.
In a traditional web application, this data would be retrieved by making an HTTP call to the backend API. However in our Remix application, the hook is instead responsible for initiating a network request and returning the data that should be rendered in the UI. Since all of this logic is executing on the server, Remix is able to deliver the resultant HTML immediately and ensure the page is interactive as soon as possible.
As part of its focus on improved application performance, Remix encourages developers to build apps that require as
little front-end JavaScript code as possible. In the above example, Remix will emit an HTML form that is mostly static.
The dynamic behavior in this example comes from Remix’s Form
component. This component handles the interaction with
the server and prevents the page reloading like a traditional web application. The form needs a corresponding function
that will perform the required mutations. Similar to the loader
function, this is an async function that receives the
request and then can do an operation based on the data provided. You can see the above example in use in a fully
configured Remix application here.
Next, let’s take a look at one of the most beneficial Remix features.
Routing
Remix was developed by the same team that created React Router, and thus routing is one of the key benefits of using Remix. Like the most recent versions of React Router, Remix uses a file-based routing system.
In a Remix project, you can see the initial routes under the app/routes/index.jsx
file. File-based routing allows you
to build out routing rules based on the file and folder hierarchy of the application. Let’s look at an example
structure:
|
|
The root.jsx
file is the main entry point of the router, this component renders the layout of your application. This
includes the <head>
and <body>
elements of each HTML response. In the root component you can make use of the Remix
Outlet
component. The Outlet
component is a concept taken from React Router V6, and is what powers the nested
routing features of Remix.
In our example, the index.jsx
component will be rendered when the route is "/"
. Let’s say that the index page has a
link for projects. Clicking this link will render the project.jsx
component and change the relative URL in the browser
to /projects
. In the project.jsx
component, we could render a nav bar that has all the projects listed, with each
list item represented as a clickable link with a URL like /projects/1234
. By default, the projects
component will
not render anything into the outlet until the route changes and contains projectId
in the url. When the user clicks on
the project link, the URL will update to /project/1234
.
The Outlet
on the project page will then render the individual project component. We can visualise this page and the
nested routing using the mock-up below:
This mock-up shows the Outlet
component that renders the nested route based on the current url. In a traditional
routing pattern, we would have to render a new component for each project which would be its own route and related
component. Having nested routing allows for better component reuse and allows us to nest components in the way that they
are rendered on the page.
NextJS
We can’t talk about Remix without talking about alternative full-stack JS frameworks, the most popular of which is NextJS. NextJS has a lot of similarities to Remix and both frameworks can provide a similar set of useful features, some of which are achieved via different design patterns. Let’s take a look at the key features and key differences between Remix and NextJS.
React-focused
Both frameworks are based on the React library for generating UIs. This is a common pattern across several other server-side frameworks such as Gatsby and Redwood. Remix is trying to disassociate itself as being a React framework, and has community members working on versions of Remix that work with other UI libraries such as Preact, Vue, Angular and Svelte. Next currently only supports React and there are no plans to expand this as of the writing of this article.
Server-side rendering
Both frameworks support server side rendering. When building a project you are encouraged to make use of the server capabilities of each framework. Both frameworks try to generate as much UI as possible on the server, then take advantage of React’s hydration feature to provide client side interactions after the first render in the browser. One main difference that NextJS has over Remix is the type of server generations supported.
NextJS has support for static site generation. This is a pattern where the entire website is generated at build time and the static pages are delivered to the user when requested.
NextJS also supports incremental static regeneration. This is very similar to static site generation, however the pages are regenerated at runtime, not at build time. The regeneration works off a time interval so that the data on the page can change over time but also be cached as a static page. This works for an application that has data that will not change often but is also not completely static. You get the best of both worlds: cached pages that can be updated over time without long build times.
Remix only supports server side rendering, so this does limit its caching capabilities. However, this also reduces the potential side effects of static site generation and incremental static regeneration. When using static generation, you need to generate all your site pages at build time and this can get very time consuming when you have a large site with lots of pages. Remix focuses on making the server render as fast as possible over trying to cache and build static pages. Since most web applications have dynamic pages and content, prebuilding the pages would not make sense or add any benefit.
Deployment
Remix has a number of deployment paths when it comes to pushing your application to a production environment. When starting a new Remix project, the CLI will ask you what your deployment target will be. Currently the out of the box options include:
- Remix App Server
- Express Server
- Architect (AWS Lambda)
- Fly.io
- Netlify
- Vercel
- Cloudflare Pages
Requiring the user to choose a deployment target means that you need to be able to know ahead of time what your deployment target will be. This might be clear when working in a company with a preexisting dev ops team that dictates the deployment targets. But for a small team that is unsure where they will deploy it might need more research, it is probably unclear if it is trivial to change deployment targets without having to nuke your setup and rebuild. This process might be simplified as the Remix framework grows in maturity.
Remix is trying to keep its deployment options as wide as possible, as evidenced by the fact that you don’t even need to take a dependency on Node to run Remix. This is a smart decision, and a great way of targeting as many developers as possible while not being dependent on the feature set and roadmap of one platform.
Conclusion
Remix is one of the latest players in the full stack JavaScript framework game. It has a solid foundation built by the React Router team and has a strong community. Remix is in the same development space as many other established frameworks, like NextJS, and is holding its own. The team at Remix are constantly pushing out updates and have been responsive to developer feedback. While it’s still early days for Remix, it already feels like a production ready framework and is poised to only get better over time.