First impressions of the Remix web framework
I’ve been seeing a lot of buzz about Remix on Twitter lately, so my friend Matt and I decided to try it out by building a simple project together (yes, we’re both named Matt). You can check out the project here: ghostwriter.boo. The site has a few other small features, like the ability to log in and bookmark/favorite poems. But we were too lazy to make those features look nice, so we omitted them from the “production” website.
We didn’t spend much time on the project (a few hours each), so take the opinions below with a grain of salt 🧂. With that being said, here’s what we liked and didn’t like about Remix!
Things I liked
- Remix has a few templates (they call them stacks) which makes it easy to get started. When you clone one of these templates, you get a bunch of stuff for free, including CI/CD, a database, and authentication. We used the indie stack for our project.
- Everything (frontend + backend) is in one repo, which makes projects easier to manage. By default, Remix comes with a server, meaning you don’t have to spin up your own (for example, usually I would spin up an Express server in a separate repo, which can be annoying for small projects).
- It’s extremely easy to set up a simple CRUD backend. For reads, you just need a function that returns some data (e.g. by reading from a DB) + use
useLoaderData. For writes, you just need a function that writes some data (e.g. by writing to a DB) + either
useSubmitor a form.
- Unlike Create React App, Remix is built for server-side rendering. This has a lot of benefits, e.g. improved performance and better SEO. You can read about Remix vs. Next to hear more about the benefits of SSR.
useSubmitmake it easy to handle loading states and input validation when reading/writing data.
Things I didn’t like
- The biggest con for me is that with Remix, your frontend and backend code are tightly coupled. Remix touts this as a feature “This enables you to co-locate everything about a data set in a single route module: the data read, the component that renders the data, and the data writes:” I think this works fine for small projects, but I prefer having an actual API that can be re-used across the entire app. For example, if one page needs multiple actions (mutations), the recommended approach is to have one big action function and do a switch/case inside. I’d prefer to have two separate API endpoints on the backend, and then the frontend can call either of them. Again, I don’t think this matters much for small projects. But for medium/large projects, it’s quite valuable to have an easily readable API so you (and your teammates) can see what capabilities the backend has (e.g. like what GraphQL gives you). Perhaps with the right file organization, this issue can be mitigated — I haven’t worked enough with Remix to tell. Side note: having an API also makes it easier to add performance logging. For example, when I use GraphQL, I log each resolver’s performance, which makes it pretty easy to hone in on perf issues.
- Remix doesn’t support CSS modules (yet). Since I use CSS modules for all my projects, and have built up a small component library that relies on CSS modules, this is a bit of a dealbreaker for me right now. But it seems like support will be added soon.
- I ran into issues using the
react-selectlibrary (see here for more info). I fixed it by following this example. It wasn’t the biggest deal in the world, but it’s annoying that some libraries require extra work in order to get them working (as opposed to using CRA or Next.js, where you can just install
react-selectand use it with no issues).
- Remix does not have built-in support for websockets or SSE (server sent events). AFAIU, you can work around this by using Express +
@remix-run/express. However, this makes setting up a Remix project a little trickier vs. using the default Remix App Server.
Would I use it again?
Probably not, but mainly because I already have my own template that’s built using my preferred stack. My preferred stack is a CRA frontend using Relay deployed to Cloudflare Pages, and a GraphQL server built with Express + SQLite deployed to a DigitalOcean Droplet. This stack is simple, cheap (I just re-use the same Droplet for all my projects), and flexible.
Remix is nice, but not nice enough to make me switch.
- SSR has its benefits, but it doesn’t really matter for side projects. And even for bigger projects, if you’re going after a dynamic, app-like experience, CSR works just fine (the main advantage of SSR is the first page load).
- As mentioned above, my personal component library uses CSS modules, which Remix doesn’t support 😢.
- I prefer having a strongly typed API (e.g. GraphQL) over having the backend and frontend logic be co-located for each route (see above for why).
- I really like using Relay for the frontend. It makes it extremely clear what data each component reads and writes, especially if you leverage fragments.
- Lastly, I like boring technologies. React, Relay, Express, and SQLite have all been around for a long time — they’re stable, and I know I can rely on them. Remix is relatively new, which makes me weary of adopting it in my preferred tech stack. That being said, as it continues to improve, I may revisit it!
For context, I’m a full stack engineer at Meta working on building 0–1 products (opinions are my own). Check out my personal website for more of my background.
Things I liked
- It has a lot of quality of life improvements that just make sense. An example of this is the asset imports functionality. A developer shouldn’t have to worry about webpack plugins or other libraries just to import a png.
- The speed at which you can go from a blank repo to a fully functional project using one of their stacks is impressive. Using the indie stack we were able to get our project set up with GitHub actions, Fly deployment, and authentication in minutes.
- Remix has really solid implementations of solutions to pain points modern web developers face. Some examples of this are their error boundary, loading state, and form validation helpers.
- If you use one of their recommended stacks, things work really nicely together. One example of this is how well Prisma, loaders, and actions work together.
- Remix has excellent documentation with plenty of examples. The docs also have a lot of practical advice and explanations of why certain decisions were made in development.
Things I didn’t like
- Remix has a lot of “magic”, i.e. abstractions that hide the complexity of underlying concepts. This makes for a great developer experience when your project fits into the Remix model, but makes it more difficult to customize.
- Nested routes sound nicer on paper than in practice. With the app we built I didn’t really find a need for them — building traditional routes was fine and nested routes were unnecessarily complex (our app was pretty simple, so it may come in handier for more complex apps).
- Executing multiple actions in a single page does not yield very clean code, and can get unwieldy quickly. There’s some discussion about this here.
- It was difficult to find answers to common framework related questions by googling. For example, a search for the best way to integrate custom fonts into a Remix project yielded only one article, versus a full page of articles with Next.
Would I use it again?
I would use it again for a small, CRUD-based project where I am prioritizing development speed over customization. Since things work well when you do them “the Remix way”, I would feel very confident building another project starting with one of their recommended stacks. If the project required a more custom stack, I would stick with Next.
- Most of Remix’s “killer features” are focused on performance & SSR, neither of which are priorities for me on a smaller side project.
- I would use it again for a small project where I wanted to use Prisma and a relational database. Due to the tight coupling & other issues mentioned above, I don’t think I would use Remix in a larger project.
- Since there is a strong dev team (and company, Shopify) behind Remix I am confident many of the drawbacks mentioned above will be resolved in time. For this reason I’m going to keep following Remix and will likely use it again as it becomes more mature.