Head-to-head comparison between Remix and Next.js

Sunscrapers Team

1 August 2022, 8 min read

thumbnail post

What's inside

  1. Intro
  2. Speed
  3. Routes
  4. Data loading strategies
  5. Form handling
  6. Authentication
  7. Summary

Intro

Remix is a new full-stack framework based on React Js, it can be used as a data fetching library, router, and bundler. * It is fast, and it can run anywhere.* Before you had to pay for the license but recently it changed to open-source.

Remix is often compared to Next.js from Vercel, and the following article is a comparison between Remix and Next.js.

The main difference is - Remix.run is more of a full-stack framework. Remix it’s a framework for performance on the web and it supports the official HTTP SWR feature.

Note: SWR, not to be confused with Vercel SWR (client fetching package).

While Next.js is a more of frontend framework based on the flexible React framework that allows you to build blocks for creating fast web apps.

So, the question remains:

How to differentiate Remix from Next.js?

There doesn't seem to be much of a difference between Remix and Next.js because both of them support SSR. Although, the thing is, while Next.js supports SSG and other tools, Remix focuses only on SSR.

In this post we’d like to focus on the difference between these two frameworks in terms of speed, routes, data loading strategies, form handling, and authentication.

Speed

Right now, Remix is much faster than Next.js.

One reason is the data caching strategy. While Next.js cannot cache search pages, Remix can. Static Site Generation, which Next.js uses, doesn’t scale well for dynamic pages.

remix versus next

source

Things might probably change in the future, as Next.js already announced in their RFC a way to do the parallel fetching.

Routes

The route is one of the most important parts of the app. Basically it's what decides what to render based on the user URL.

In Next.js, when you create a file inside the /pages folder, it will automatically be set as a route.

pages /index.js ==> /
pages /products/index.ts ==> /products
pages /products/create.ts ==> /products/create

In Remix’s case, you need to nest your routes inside the routes folder instead of the pages directory.

app/routes/index.ts ==> /

aps/routes/products/indexj.ts ==> /product

app/routes/productse.ts ==> /products/create

The other difference is Remix allows you to create nested layouts as it’s based on react-router (the same people who made Remix).

The feature requested for a long time in Next.js will be a part of future updates announced in Next.js RFC (request for comments).

It looks like the competition is good for progress.

Data loading strategies

There are multiple ways to load data in web applications.

You can try:

  • Client-side rendering (in the runtime)
  • Server-side rendering (in the runtime)
  • Server-side rendering (at the build time)
  • A mix of the three above

For Next.js you may choose for data to be loaded exclusively in the server, client, both at runtime or build-time. You can configure how your data is loaded from your web app in detail.

  1. you can use getServerSideProps to load the data server-side in the runtime:
export async function getServerSideProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}
  1. use getStaticProps and getStaticPaths to load the data server-side in the build time:

a. getStaticProps example from documentation:

export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}

b. getStaticPaths example from documentation:

export async function getStaticPaths() {
  return {
    paths: [
      {params: {...}}
    ],
    fallback: true // false or 'blocking'
  };
}

For client-side rendering, there are some libraries available like SWR, from the same authors who made Next.js and React Query.

Also, in their RFC Next.js team mentions that they want to use React Server Components (there is already an Alpha version available) instead of the getServerSideProps - a feature that will give a significant speed boost to Next.js and reduce bundle size.

As we can read on their website:

"Most React hooks, such as useContext, useState, useReducer, useEffectand useLayoutEffect, are not supported as of today since server components are executed per request and aren't stateful”.

In Remix, they support only two strategies, server-side at runtime and client-side at runtime.

  1. Export a loader function from a route to load data server-side at runtime.

Example from Remix documentation:

import type {LoaderFunction} from "@remix-run/node"; // or "@remix-run/cloudflare"
import {json} from "@remix-run/node"; // or "@remix-run/cloudflare"
import {useLoaderData} from "@remix-run/react";

export const loader: LoaderFunction = async () => {
  return json([
    {id: "1", name: "Pants"},
    {id: "2", name: "Jacket"},
  ]);
};

export default function Products() {
  const products = useLoaderData();
  return (
    <div>
      <h1>Products</h1>
      {products.map((product) => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}
  1. Use the useFetcher hook from Remix to load data client-side at runtime.

Example from Remix documentation:

import {useFetcher} from "@remix-run/react";

function SomeComponent() {
  const fetcher = useFetcher();

  // trigger the fetch with these
  <fetcher.Form {...formOptions} />;

  useEffect(() => {
    fetcher.submit(data, options);
    fetcher.load(href);
  }, [fetcher]);

  // build UI with these
  fetcher.state;
  fetcher.type;
  fetcher.submission;
  fetcher.data;
}

Form handling

Next.js uses JavaScript to communicate with the server from the client. Also supports some partial functionality if you turn off JavaScript.

If Javascript is enabled it relies on basic functions such as handleSubmit().

From Next.js documentation:

export default function PageWithJSbasedForm() {
  // Handles the submit event on form submit.
  const handleSubmit = async (event) => {
    // Stop the form from submitting and refreshing the page.
    event.preventDefault()

    // Get data from the form.
    const data = {
      first: event.target.first.value,
      last: event.target.last.value,
    }

    // Send the data to the server in JSON format.
    const JSONdata = JSON.stringify(data)

    // API endpoint where we send form data.
    const endpoint = '/api/form'

    // Form the request for sending data to the server.
    const options = {
      // The method is POST because we are sending data.
      method: 'POST',
      // Tell the server we're sending JSON.
      headers: {
        'Content-Type': 'application/json',
      },
      // Body of the request is the JSON data we created above.
      body: JSONdata,
    }

    // Send the form data to our forms API on Vercel and get a response.
    const response = await fetch(endpoint, options)

    // Get the response data from server as JSON.
    // If server returns the name submitted, that means the form works.
    const result = await response.json()
    alert(`Is this your full name: ${result.data}`)
  }
  return (
    // We pass the event to the handleSubmit() function on submit.
    <form onSubmit={handleSubmit}>
      <label htmlFor="first">First Name</label>
      <input type="text" id="first" name="first" required/>

      <label htmlFor="last">Last Name</label>
      <input type="text" id="last" name="last" required/>

      <button type="submit">Submit</button>
    </form>
  )
}

Remix, on the other hand, relies on the browser's native HTML form element.This means that your site will still have its full functionality even if Javascript is turned off.

Example from Remix documentation:

// on route
app / routes / projects / new.js

import type {ActionFunction} from "@remix-run/node"; // or "@remix-run/cloudflare"
import {redirect} from "@remix-run/node"; // or "@remix-run/cloudflare"

// Note the "action" export name, this will handle our form POST
export const action: ActionFunction = async ({
                                               request,
                                             }) => {
  const formData = await request.formData();
  const project = await createProject(formData);
  return redirect(`/projects/${project.id}`);
};

export default function NewProject() {
  return (
    <form method="post" action="/projects/new">
      <p>
        <label>
          Name: <input name="name" type="text"/>
        </label>
      </p>
      <p>
        <label>
          Description:
          <br/>
          <textarea name="description"/>
        </label>
      </p>
      <p>
        <button type="submit">Create</button>
      </p>
    </form>
  );
}

Authentication

There is no built-in functionality to interact with cookies and sessions in Next.js, but you can use third-party libraries such as Cookie.js. Remix has support for cookies and sessions included.

// app/cookies.js
import {createCookie} from "@remix-run/node"; // or "@remix-run/cloudflare"

export const userPrefs = createCookie("user-prefs", {
  maxAge: 604_800, // one week
});
// app/routes/index.tsx
import {json, redirect} from "@remix-run/node"; // or "@remix-run/cloudflare"
import {useLoaderData} from "@remix-run/react";

import {userPrefs} from "~/cookies";

export async function loader({request}) {
  const cookieHeader = request.headers.get("Cookie");
  const cookie =
    (await userPrefs.parse(cookieHeader)) || {};
  return json({showBanner: cookie.showBanner});
}

export async function action({request}) {
  const cookieHeader = request.headers.get("Cookie");
  const cookie =
    (await userPrefs.parse(cookieHeader)) || {};
  const bodyParams = await request.formData();

  if (bodyParams.get("bannerVisibility") === "hidden") {
    cookie.showBanner = false;
  }

  return redirect("/", {
    headers: {
      "Set-Cookie": await userPrefs.serialize(cookie),
    },
  });
}

There are also third-party solutions such as next-auth and **remix-auth **.

Summary

According to the authors of the framework Next.js is

"The React Framework for Production. Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route prefetching, and more. No config needed".

Remix is described as

"(...) an edge native, full stack JavaScript framework for building modern, fast and resilient user experience. It unifies the client and server with web standards so you can think less about code and more about your product".

Both Remix and Next.js have out-of-the-box support for TypeScript. Next.js has built-in support for web font optimization - Remix is still missing this feature.

The Remix is being used mostly for personal projects and toy applications (as most new technologies), while Next.js excels in production applications.

Note, and this is important, that Next.js has been in development significantly longer than Remix. It has a lot bigger community support, and resources like learning materials, etc.

On the other hand, Remix has a lot of potential but doesn’t have a big community of users. Yet…

Sunscrapers Team

Sunscrapers empowers visionary leaders to ride the wave of the digital transformation with solutions that generate tangible business results. Thanks to agile and lean startup methods, we deliver high-quality software at top speed and efficiency.

Tags

remix
next.js
react
react framework

Share

Let's talk

Discover how software, data, and AI can accelerate your growth. Let's discuss your goals and find the best solutions to help you achieve them.