I have been using Next for years, since the v1 was released, I even become an early contributor of the framework and joined Vercel for a year. Years to the future, Remix was announced and after reading some of their newsletters I purchased the indie license to start to play with it, published a few libraries to use it Remix and once it become Open Source I become a contributor.
Why I'm telling you that? Because I have used both, a lot, and I think that put me in a good position to compare them. What is Next.js? Next is a React Framework, it uses React as the view layer and build features on top of it.
Remix is a Web Framework, it uses React as the view layer like Next, but it's not tied to React and it's more intended to be a full-stack web application framework. Now, let's compare the features of both frameworks.
Because we are building a web application, the route is one of the most important parts of an application.
It's what decide what to render based on the user URL. In Next.js, they have their own router using the file system, so you create a pages folder and put files there. Those files are going to becomes pages inside the application, with the following URLs:. They also have a useRouter hook to access data from the router like the search (query) params or methods like reload or push to navigate to another URL.
In Remix, they use React Router v6 internally but they provide a file system based system, instead of pages Remix call them routes, but the general is similar.
- Those files are going to become routes with the same URLs as in Next. The main difference here comes with the introduction of Layout Routes.
- One common requirement of user interfaces is to re-use a layout between two URLs, a common example is to keep a header and footer on every page, but this can become more complex.
A great example of this is Discord, let's analyze it quickly.
You can see at least four main areas. The left column with the list of servers. The next column with the list of channels. The widest column with the list of messages. The right column with the list of users of the server. It's not on the image but you can have a list of messages for a thread replacing the users. If you wanted to build this UI in Next.js, you would need to create a file at pages/[serverId]/[channelId].tsx, get the data of each list and render a component like this:.
When the user navigate to another server or channel, based on the data loading strategy you used, you may need to get load everything again with the new channel or server. This is because Next doesn't have support for layout routes, so every page renders everything on the screen, including shared layouts between screens.
Contrary to Next.js, Remix has support for that, so in Remix we would create a file structure like this:. While you have more files, this will help you to keep the code more organized and to make loading data more optimized.
The __layout.tsx creates what Remix calls a Pathless Layout Route, this is a route component that works as a layout but without adding segments to the URL, so you will not see a /__layout in the URL, and instead you will go directly to the /:serverId part.
The $serverId.tsx creates a layout route with a dyanmic parameter serverId that can be used in the server to load the data of the route.
Meta-frameworks built on React + Router
The same goes for $channelId.tsx. The index.tsx inside the $serverId folder will be used when the user goes to /:serverId without a channel ID, there we can render something special or we can redirect to a channel.
The index.tsx inside the $channelId folder would be rendered when the user is not in a specific thread, in that case we can render the list of users of the channel.
- Those files will generate the following routes:. Now, each route file will be able to render parts of the UI.
- The __layout could render the Layout component, load the list of servers and render it, also render an
to indicate where the nested routes will be placed in the UI.
- The $serverId will load the data of the server, this is the list of channels and extra server info, then render them together with an
- The $channelId will load the data of the channel, this is the list of messages and extra channel info, then render them together with an
The $channelId/index will load the data of the list of users of the channel and render it. The $threadId will load the data of the thread and render it. Aside of being able to have individual route files with their own data and UI, it has another benefit, if the user is on the URL /server-a/channel-a and navigates to /server-a/channel-b Remix knows that the server ID didn't changed and can get the data of the cannel without touching the server, so you can avoid loading data you already have.
To get the same behavior in Next.js you should move your data loading to be completely client-side so the component can re-use the data it already has, using something like SWR or React Query. So this is a huge performance benefit of using layout routes that you can't get as easy and with the same UX in Next.
In a web application, you can use different strategies to load the data your app needs, some are:.
Server-side at runtime. Server-side at build time. Client-side at runtime. A mix of 1 and 3 or 2 and 3. In Next you can use all of them with different functions you can export from a page file. Use getServerSideProps to load the data server-side at runtime.
Use getStaticProps and getStaticPaths to load the data server-side at build time. If you want to load data client-side Next let you do it however you want, some popular libraries are SWR, from the authors of Next, and React Query. In Remix, they support only two strategies, server-side at runtime and client-side at runtime.
Export a loader function from a route to load data server-side at runtime.
Use the useFetcher hook from Remix to load data client-side at runtime. You can, of course, keep using SWR or React Query with Remix, the useFetcher however does a really good job to simplify the code and most of the time it's all you need.
Based on that, Next supports SSR (Server-Side Rendering), SSG and ISR (Static Site Generation and Incremental Site Regeneration) and CSR (Client-Side Rendering). While Remix supports only SSR and CSR.
Next first versions only supported SSR and CSR, it wasn't until the release 9.3 when they added support for SSG, and later in 9.5 they added stable support for ISR.
- Since they added support for SSG and ISR the team started to pitch SSG as a solution for lots of websites and proposed to combine it with CSR for dynamic or provide data.
- The idea, is to use SSG to generate a static site with the public information of a website and then use CSR to get the rest, and example of this is when you build an ecommerce you can generate a page for every product with the public data, and then using CSR get the user-specific data like their shopping cart.
- The problem starts when your app needs to show more and more private or dynamic data, in a big enough ecommerce the price of the products may change during the day, the user may have coupons, there could be seasonal discounts, a product may have a different price depending on the user's location, or maybe a discount is only for a few units and once they are sell it should go back to the original.
- Anotehr example could be if you want to show suggestions to the user for related products, should they be the same for every user or should they differ?
In the first scenario you could get the suggestions at build time, in the later which is the most common, you will need to do it client-side at runtime.
- And for product listing pages, with search and filters, SSG is just not an option because generating every possible search plus filter combination is going to take a huge amount of time.
- At that moment, you will need to make a decision, use CSR or SSR. If you go with the CSR way you will degrade the UX of your app adding lots of spinners or loading state, if you go with the SSR way you will get a better UX but now your TTFB (Time To First Byte) will be slower, however a fast TTFB is meaningless if the TTI (Time To Interactive) is high because you do lots of request on the client to get the data needed to show something useful for the user.
- Because of that, Remix decided to go with only SSR, and not SSG, most apps will eventually need private data and if you have a truly static route you can cache it at the edge using a CDN.
Let's define a UI route as any route rendering HTML (a UI), and a non-UI route as the rest, in Next.js this are called API routes, which is probably the most common term used for them, in Remix they decided to call them Resource Routes. Aside of what they expect the files to look like, they are intended to solve the same use case, your app may need some routes which are not sending HTML and instead return something else, typically they're used for sending JSON or in Next to receive a JSON from the browser and perform a mutation server-side (e.g.
Remix decided to go with the name Resource Routes to differentiate from the idea they are only used for an API, you can use a resource route to send or receive JSON, but also send CSS, images, JS scripts, PDFs, etc. Anyway, you could do that with Next.js's API routes, it's just not the most common thing to do.
Next.js comes with many ways to load the data but it doesn't have a built-in way to perform mutations, so you need to do it manually.
This typicalle involves creating a form, attach an onSubmit, prevent the default behavior, get the inputs values (probably from React state or a Form management library like Formik or React Hooks Form), then send the data using the Fetch API to an API route, and of course add a few states to handle the loading state, store any error, etc.
Instead, with Remix you can use their built-in Form component, this component works as a normal Form, you decide if the method is GET or POST, you can add an action with a URL to send the data to which is optional and defaults to the same route fo the form, and put the inputs inside. But now you don't need to do anything else, Remix takes care of sending the values from the form to the action with the correct method and keep a loading state, abort requests if the form is sent again, and way more things you may forgot to do manually, then with the useTransition hook you can know the status of the request.
The request can be sent to any route, not only resource routes, if the method is GET Remix will run the exported loader function, if the method is POST it will run the exported action function.
And, if you need to send a form programmatically, you can use the useFetcher hook or useSubmit hook, useful to send on every keystroke or after checkbox is clicked. Sessions are used commonly to store the user authentication data, you can store a session in a cookie or in an external data store like a DB or the file system, then save a session ID on a cookie.
A cookie can be used to store the session data or session ID or for any other data outside the session you may need, it could be tracking, discount codes, etc. In Next.js, you don't have anything special built-in to work with cookies or session, there are popular libraries like Cookie.js which can work with Next or NextAuth.js to do user authentication and store some session data in a cookie.
Remix comes with cookies and sessions support out of the box, you can create a cookie calling a function and then serialize data to store there or parse data to read it.
With session, you can choose between storing the session data on a cookie, in memory, on the file system, if you deploy to Cloudflare on Worker KV and you can create your own session storage to store the session data in your DB or somewhere else.
Next.js can be deployed to any server where you can run Node.js by doing next build && next start, additionally it has built-in integration to run in serverless mode when deploying to Vercel, and the Netlify team created an adapter to deploy to their service in serverless mode.