iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
😘

Key Features of Remix Compared to Next.js: A Hands-on Review

に公開
2

Today, November 23rd, Remix was officially released, so I decided to try it out immediately.

https://remix.run/

Remix is a React framework created by the developers of the react-router library. Like Next.js, it features file-based routing.
Since many people might be wondering what exactly makes it different from Next.js, in this article, I would like to summarize the differences in Remix's philosophy compared to Next.js.

SSG and ISR are not supported

Next.js has features like SSG and ISR that pre-build data and return the results at request time, but Remix does not have these.
Instead, Remix provides a mechanism to control HTTP headers simply by exporting headers for each page component, allowing you to make adjustments using the Cache-Control HTTP header.

export const headers: HeadersFunction = ({ loaderHeaders }) => {
  return {
    "Cache-Control": "max-age=3600, stale-while-revalidate=360"
  },
}

As seen in the documentation below, rather than a cache strategy using SSG or ISR, Remix seems to recommend improving performance by executing on the Edge (such as Cloudflare Workers) and utilizing Cache-Control.

https://remix.run/docs/en/v1/guides/performance

No Environment Lock-in

Of course, with Next.js, it is naturally possible to deploy to other environments like GCP or Serverless. However, by deploying to Vercel, you can receive several benefits for performance optimization, such as ISR and next/image.
In the case of Remix, it can collaborate with Express, and adapters are provided to run it in other environments like Vercel, AWS Lambda, Fly.io, and Cloudflare Workers. It's great because you aren't locked into a specific environment!

Routes in Remix correspond to page components in Next.js. You can easily define meta and link tags for each page by simply exporting them as follows:

export const meta: MetaFunction = () => {
  return {
    title: "About Remix"
  };
};

export const links: LinksFunction = () => {
  return [{ rel: "stylesheet", href: stylesUrl }];
};

It's great to be able to easily do something similar to what the next-seo library does for Next.js! It can also be used when you want to load CSS only on specific pages.

Error Handling Per Route

In Remix, you can set the error display for each Route by exporting ErrorBoundary or CatchBoundary components. The ErrorBoundary can be displayed when an unexpected error occurs in a client-side component, and the CatchBoundary can be displayed when an error-related status code is returned from the server-side during page rendering or a POST request.

export function ErrorBoundary({ error }) {
  return (
    <div>
      <h1>{error.message}</h1>
    </div>
  );
}

In CatchBoundary, you can also retrieve the error content using useCatch as follows.

export function CatchBoundary() {
  const caught = useCatch();

  return (
    <div>
      <h1>{caught.status} {caught.statusText}</h1>
    </div>
  );
}

It makes placing error boundaries for each page easier than in Next.js.

Partial Routing Per Page

This feature is called Nested Routes, and personally, I found this to be the most interesting feature that Next.js lacks. By using the Outlet component provided by Remix, you can define components that apply to only a specific part of a nested URL.
For example, let's say a component is defined in routes/users.tsx as follows:

import { Outlet } from "remix";

export default function Index() {
  return (
    <Layout>
      <Main>
        <Outlet />
      </Main>
      <Sub />
    </div>
  );
}

In this case, you can define the component corresponding to <Outlet /> in a file like routes/users/$id.tsx.
Since each Nested Route can also have its own ErrorBoundary as mentioned earlier, even if an error occurs in routes/users/$id.tsx and the component fails, only the <Outlet /> section will be replaced by the ErrorBoundary.
This means you no longer have the common React issue where the entire page goes down just because of a mistake in one small part.
It definitely seems beneficial for building robust web applications!

POST Processing Within the Same Route

You can also write the logic for handling POST requests in the same file as the page component, which provides great visibility.
It feels like taking the logic you would usually write under pages/api/ in Next.js and bringing it directly into the page component.

This is made possible by using the Form component and the useActionData hook from remix.

todo.tsx
import { Form, useActionData, json } from "remix";

export async function action({ request }) {
  const body = await request.formData();
  return json({ result: 'OK' })
}

export default function Todo () {
  // Data returned from the API (action) can be received here
  const data = useActionData()
  return (
    <Form method="post">
      {/* Form content goes here */}
    </Form>
  )
}

Summary

From my initial experience, while not having SSG or ISR felt a bit inconvenient compared to Next.js, I found that Remix allows you to do a lot on a per-page basis, enabling various strategies for caching and error handling, such as:

  • Setting link and meta tags for each page
  • Modifying header information for each page
  • Writing POST processing logic for each page
  • Setting an ErrorBoundary for each page
  • Using Nested Routes to swap out only a specific part of a component for each URL

However, since there are several features supported in Next.js that are not available in Remix, which one to use depends on the situation:

  • SSG and ISR are not available
  • Convenient styling features like CSS Modules are missing
  • There are no API Routes like Next.js's pages/api/**

From a performance perspective, I felt that Remix's intention is not to provide the kind of tricky features found in Next.js, but rather to encourage developers to optimize performance using standard web technologies—such as setting link rel="preload" or Cache-Control on a per-page basis as needed.

For those with sufficient web knowledge, Remix seems to offer a lot of tuning possibilities. However, for those with less experience in performance optimization, Next.js is recommended as it provides optimization by simply following its conventions.

There are still some things I haven't fully investigated, so I'll add more as I find them. Also, if there are any mistakes, I would appreciate your kind corrections.

Discussion

プログラミングをするパンダプログラミングをするパンダ

next/linkのようなページを事前フェッチングする機能はない

Link コンポーネントの props に応じて prefetch の設定が可能ですね。

デフォルトでは prefetch は off とのことです。

Link can automatically prefetch all the resources the next page needs: JavaScript modules, stylesheets, and data.

<Link /> // defaults to "none"
<Link prefetch="none" />
<Link prefetch="intent" />
<Link prefetch="render" />

https://remix.run/docs/en/v1/api/remix#link

Godai HoriGodai Hori

なるほど!あったんですね。
わざわざコメントいただきありがとうございますmm