Thoughts on Remix
Reflections on Remix by seasoned React developers. TL;DR: it good.
Viget has a storied history of building rich web applications going way back to the Backbone/Marionette days with Dick’s Sporting Goods and beyond, into the pre-single-page-app days. Things have changed a lot since then, and the tools we have for building maintainable, scalable products are so much better. Viget was an early adopter of React when it exploded in popularity around 2014. Since then, we’ve built dozens of products with the library and have followed developments around it closely.
As React’s capabilities have grown and the technical approaches to building UIs have become more sophisticated, new “meta-frameworks” built on top of React have emerged that allow us to deliver more performant and scalable frontend codebases faster, with greater confidence and consistency and reduced custom development efforts.
React is a fairly low-level tool. Building modern applications with it requires sourcing dependencies and establishing patterns for routing, data fetching, state management, styling, SEO, and more; and often comes with a heavy tooling and configuration tax. Meta-frameworks stitch together a set of opinionated approaches to many of these needs, and lower the barrier to entry for optimizations (server-side rendering, static site generation) that enable finer-grained control over the tradeoffs that directly impact users’ experiences. Building hybrid server/client-rendered applications without a meta-framework isn’t cost-effective in many cases.
At this point the two main competing React meta-frameworks are Next.js (maintained by Vercel) and Remix (maintained by Shopify). We’ve written about Next.js before, on the topic of React Server Components (RSC) and Next 13. We’ve also built several products with Next.js and have found it to be well-designed, well-documented, and easy to use.
Lately we’ve been digging into Remix. We’ve been long-time users of react-router (docs) since before it was a part of Remix and we’ve followed Ryan Florence's work since 2015 when we attended React Week, a precursor to React Training workshops, led by Ryan and Tyler McGinnis.
To explore Remix more, we built an internal tool called Mainframe. Mainframe is an app that allows developers to search for and capture topic-centric technical takeaways, with the goal of preserving institutional knowledge. We were able to build the bones of the application in a couple of days, finishing the project in just a few short weeks. Iterations were short and productive. I’d like to share some takeaways with you from that experience.
What we like about Remix #
- The docs are easy to navigate and extensive, spanning high-level discussions on the various approaches and tradeoffs for building modern web applications, API documentation, and topic/objective-based guides.
- Learning Remix is mostly about understanding routing (same API as
react-routerv6), and data loading, along with learning the framework conventions (“the right thing is the easy thing," ideally, though not always in practice).
- The framework design puts a focus on standard/fundamental web APIs and interfaces (e.g.,
FormData) – the knowledge of which is transferrable outside of Remix applications.
- Remix is easy to deploy on a variety of different infrastructures and provides pre-packaged “stacks” that ease new project setup.
- Remix has a thoughtful approach to rolling out new features, offering early and incremental adoption through “future flags”.
- It just generally feels productive to build with. Loaders and actions feel like solid lego pieces to snap together - compared to a framework-less React app, having some stronger conventions about where code lives helps with maintainability and navigating the codebase.
- It’s much easier to build applications that work progressively and degrade gracefully, as compared to something like Next.js.
- For many use cases and features, you don’t need to pull in third-party dependencies for state management, data fetching, or networking (loaders replace react-query and Remix handles making the requests). If you do need some client-side states, it might make sense to pull in something lightweight like
jotai, rather than something heavier and more robust like
- Remix gives developers flexible levers for making different tradeoffs in different places with regards to eager vs. lazy loading of data, allowing us to opt in to showing loading states granularly if a particular use case favors valuing the UX of fast navigations over avoiding loading states.
- The Shopify acquisition last year gives us a reason to believe that Remix will continue to mature and grow.
Food for Thought #
- Remix uses
esbuildbut does not allow extension of the configuration, since its compilation stage does a lot of work to generate client and server bundles and user
esbuildconfiguration could lead to bundling problems.
- The community has asked for and attempted to hack solutions to work around this limitation to enable compatibility for things like cloudflare workers and full-stack code coverage through Istanbul instrumentation.
- Recently, the Remix team announced unstable support for Vite in Remix v2.2.0, which unlocks even faster builds and opens the door for leaning on the growing ecosystem of Vite tooling.
- Since that announcement, the Remix team released v2.3.0 and v2.4.0 which include a bunch of bug fixes and improvements to the Vite integration.
- Remix is competing with Next.js for market and mindshare.
- As of this writing, Remix is sitting at 25k stars on GitHub while Next.js has 115k. So, if you’re looking at that metric alone, it’s about 1/4 as popular.
- Next.js and Vercel seem to have tight ties to the React core team and are making larger, more experimental strides to move their framework forward (React Server Components, experimental partial pre-rendering). Kent Dodds wrote about this recently, which elicited a spirited response from Lee Robinson.
- The Remix team has taken a more cautious approach to adopting canary-only React features like RSC, which means they’re behind Next.js on delivering features like partial pre-rendering. Ryan Florence wrote a bit about this at the end of 2021.
- Remix’s architecture revolves around declaring data dependencies at the (nested) route level (loaders are associated with a route).
- This works well for many use cases, but may break down if you’re building a large, highly complex, modular application where you might rather think about data dependencies at a component level.
- In that case, with Remix you must reach for Resource routes and (the more traditional) client-side data fetching approach which may not scale all that well like a GraphQL/Relay-based approach (which comes with a host of other complications like handling partial success, query optimization, etc.).
- Deploying API routes on serverless architecture is more involved with Remix than Next.js.
- Working with ESM dependencies can be a bit tricky.
- Remix bundles your server modules in the CommonJS format and does not bundle your dependencies by default.
- To get Remix to transpile dependencies, you have to add them to
serverDependenciesToBundlein the config.
- In practice, this can be difficult to get right with transitive dependencies - something we hit with the unified/remark package ecosystem which we wanted to use for GitHub-flavored markdown.
Compared to NextJS, Remix is an attractive alternative that’s less coupled to the SaaS offering of its parent company and is therefore easier to deploy anywhere. For consultants like our team at Viget, it’s valuable to be able to hand off work to clients that isn’t tied to a specific service and its pricing model.
Remix offers stable nested layouts and has opted to take a conservative approach to adopting React Server Components while the finer points continue to get ironed out. This means fewer bugs compared to Next.js and generally a more stable product.
As a meta framework, it ticks a lot of boxes for a strong React distro:
- SSR out of the box
- sane defaults for routing/data-fetching
- unopinionated about styling
- great DX/tooling
But, we'd still like to see:
- Generators (a la Rails or Redwood)
- Tighter testing integration, full-stack code coverage (Vite integration should make this story better)
- Better ergonomics around cookies and flash messages (it’s not difficult to build an abstraction over the current APIs, but I think the framework would benefit from providing something more conventional out of the box here)
- Less API churn
- With the complexity of frameworks comes additional maintenance burden
- Upgrading from Remix v1.16 to v1.17 required some fairly involved changes to the framework configuration, dev and build scripts, and the Remix server code
Overall, we find Remix to be a thoughtfully-designed, easy-to-use, powerful, and well-documented meta-framework that’s maturing quickly and has a lot of momentum behind it. Competition is good for developers and users. Having options gives us greater flexibility when choosing a technical foundation and leaves room for different ideas to grow and flourish.
I hope sharing our experience helps you discover whether Remix is a good fit for your next project.