Blog>>Software development>>Frontend>>Projects in Remix: best practices and lessons learned

Projects in Remix: best practices and lessons learned

Remix is a popular React framework, often used for its ease and speed of development for web applications. However, just like any technology, it has its shortcomings. They are not visible at first sight - you might come across them only once you’ve started a project using this technology. In this article, an experienced developer shares his mistakes to avoid and best practices to follow while working on web applications in Remix. 

What is Remix?

Remix is a React-based frontend framework curated by React ecosystem veterans. Its greatest selling point is a progressive enhancement over the core web platform, which brings the entry level almost down to fundamental web platform knowledge.

That being said, Remix is not a perfect technology. During my work on a project using this framework, I learned about a few of its shortcomings. I explain how to avoid them in the sections below.

If you’re looking for more information about React frameworks, we recommended our previous article about Next.js.  

Services Frontend development

Remix – not suitable for all architectures

Creating full-stack web applications, even as thin as client frontends with BFFs, or backends for frontends – is what Remix is designed for. You get everything you need: the framework itself, documentation, courses, talks, tutorials, community support, you name it. But as it turns out, Remix falls short for architectures where a remote API server is to be directly consumed from the client (browser, most of the time) by a frontend web application via HTTP. 

With React, normally, you would set up an application with state management for preference, perhaps with some server-side rendering flavor, and you’d be well set to fetch, manage and display data. 

However, in Remix, the web application server isn’t any less significant than the client. You can’t just connect to any remote server, it has to be a Remix server. It is well justified, bearing in mind all the features that Remix server provides, but ultimately, this brings us back to client frontend + BFF architecture, in which every request has to go through the web application server. It’s not wrong at all, for general purpose remote APIs there’s a big chance you’ll be needing a BFF anyway. Keep it in mind though, because in the described case, using Remix will most certainly increase boilerplate code.

Fig.1: Simplified Remix recommended architecture diagram
Simplified Remix recommended architecture diagram

Real-time data updates headache

With loaders, Remix suggests that you can forget state management, because the framework will do it for you. Cool, but what if you need real-time data updates on your page? Unfortunately, Remix doesn’t support any real-time update technologies other than full-page data revalidation (something like full-page refresh, but without really refreshing the document with all the static files, such as scripts, stylesheets, images, etc.) By using loaders, one could implement polling or long polling endpoints as resource routes, as they are called in Remix nomenclature. There are also third party libraries that implement SSE (server-sent events) support, namely remix-utils and remix-sse. 

Nonetheless, the most popular real-time update technology on the web, WebSocket, is nowhere to be found in the picture. WebRTC is far more complex, so it’s clear why it didn’t get Remix support either – actually, I don’t know of any JavaScript framework that would do that. 

So, no matter which option you choose, whether it’s polling, long-polling or SSE, if you plan for real–time data updates, there is a great risk that useLoaderData will become one of the multiple sources of truth for the page, because after all, you’re not getting away from having additional loader/fetcher pairs. Especially when you need the initial “get” to happen on the server and then to update in real-time in the client – it gets wild. All in all, is the state management really gone? Is it, Remix?

Fig.2: Real-time updated data with initial render on the server flow
Real-time updated data with initial render on the server flow

Handling errors with conditional statements

Remix does amazing work with error handling. It literally allows you to throw HTTP responses! And to handle them, you get bubbling route error boundaries. This is very useful when your web application throws an actual exception, regardless of it being expected or not. 

Custom errors, such as submit or fetch errors, are a pain to manage. If an error handler doesn’t fit into an error boundary domain, it has to be managed manually, often branched with a conditional statement, differentiating it from valid data. A good example of this would be a form submission error, e.g. a toast. Most of the time, in case of a form submission error, you’d like to keep the form with all data that the user entered and display an error message, often in a not too in-your-face way, like a toast. 

If you throw a HTTP response, you’ll trigger a route error boundary, which is actually quite in-your-face. You need to return an error response then, but there’s a catch – Remix fetchers don’t store the HTTP response status code. You end up with a very obscure conditional statement, which differentiates between error data and valid data, solely by reading the response body content.

Fig.3: High—level HTTP response error handling flows
High—level HTTP response error handling flows

Form submissions can mess up browser history cleanliness

If you’re particular about keeping the client browser history clean, be extra careful with Remix form submissions. Here’s the most important thing to remember – using the replace prop in the Form component or submit function options doesn’t always guarantee that a new entry won’t be added to the browser history stack. Remix encourages returning server-side redirect instead of client navigation in cases when the result of a certain request-response exchange is expected to be page navigation. By the way, it’s a great example of Remix’s progressive enhancement, for which the client navigates to the desired page regardless of JavaScript being enabled in the browser or not – either with a virtual SPA or full-page refreshing navigation. 

Let’s circle back to form submission though – it’s often the case when, after submitting a form, you want to navigate users somewhere. However, when a redirect response from the server action handler is called after form submission, it will always add an entry to the browser history stack, whether the replace prop is used in the Form component (or submit function options) or not. The reason behind this is that form submission adds a new entry to the browser history stack, just as the classic HTML form does, which can be avoided with a replace prop. Then, the server-side redirect kicks in and at that point, it is beyond the replace prop reach. 

If you need to have full control over the browser history, consider avoiding this scenario. It is an important note for web applications which rely, or simply care about, the browser’s back and forward navigation.

Summary

These are a few examples of Remix not being the one-to-rule-them-all framework. Enough of my complaints though, I wish all the other frameworks I’ve used in the past had such a short list of problems. For some, they may be discouraging enough not to pick up Remix, while others, like me, are ready to turn a blind eye to them, because there are no flawless pieces of technology in the universe. One way or another, it’s always good to know what we’re facing.

To sum up, Remix is a framework which suits a very broad range of web applications. At the same time, I feel that Remix curators, and part of its community, focus on marketing buzzwords too much, sometimes forgetting to showcase its pitfalls properly. 

In the real world, there is no one size fits all, and even though Remix could be used to develop virtually any web application, it’s always a good idea to consider other alternatives, such as the much more mature Next.js. That being said,  if I could turn back time, I still would have chosen Remix, just because so far I haven’t come across any web framework so intuitive, easy to pick up and not too hard to master – simple, yet powerful.

Here, you can read more about our frontend services.

Mieczkowski Norbert

Norbert Mieczkowski

Senior Fontend Engineer

Norbert Mieczkowski is a senior frontend engineer and author on CodiLime's blog. Check out the author's articles on the blog.Read about author >

Read also

Get your project estimate

For businesses that need support in their software or network engineering projects, please fill in the form and we'll get back to you within one business day.