Front-end Development
8 min read

Introduction to Remix

Performance and developer productivity are key features for Remix, a new full-stack web framework for JavaScript developers.

Chris Laughlin
Published September 12, 2022

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.

export async function loader({ request }) {
  return getProjects();
}

export async function action({ request }) {
  const form = await request.formData();
  return createProject({ title: form.get("title") });
}

export default function Projects() {
  const projects = useLoaderData();
  const { state } = useTransition();
  const busy = state === "submitting";

  return (
    <div>
      {projects.map((project) => (
        <Link to={project.slug}>{project.title}</Link>
      ))}

      <Form method="post">
        <input name="title" />
        <button type="submit" disabled={busy}>
          {busy ? "Creating..." : "Create New Project"}
        </button>
      </Form>
    </div>
  );
}

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:

app
├── root.jsx
└── routes
   ├── about.jsx
   ├── dashboard.jsx
   ├── index.jsx
   ├── projects
   │   ├── projects.jsx
   │   ├── $projectId.jsx

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:

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.

Get started with Reflect today

Create your first test in 2 minutes, no installation or setup required. Accelerate your testing efforts with fast and maintainable test suites without writing a line of code.

Copyright © 2022 Reflect Software Inc. All Rights Reserved.