Narwhale

Since July, I have been busy migrating Nalu from NextJS to Remix. Since both frameworks are built on React, most things work, but some Remix errors that did crop up can be pretty opaque.

Think of this as the practical companion to Remix Docs | Gotchas. Here're the common errors we encountered early on and how to resolve them.

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module

Remix compiles down to CommonJS, so it's having trouble with dependencies that only distribute ESM bundles.

Remix Docs | Gotchas.

Adding packages to serverDependenciesToBundle tells Remix to bundle the ESM module directly into the server build instead of requiring it at runtime.

The error will point you to the ESM bundle in question, but you might play a fair bit of wack-a-mole with the error. The dependency's dependencies might also be ESM, so you get another error, etcetera...

Avoid the dependency whack-a-mole with kiliman's excellent rmx-cli library.

The Fix

Suppose react-markdown was the culprit, type into your command line:

npx rmx-cli get-esm-packages react-markdown

Add the output array to the serverDependenciesToBundle prop in the remix.config.js file and restart your Remix server.

react-markdown ESM bundles

Alternatively

Never worry about this error again by forcing Remix to bundle all dependencies:

serverDependenciesToBundle: [/.*/],

Beware! Bundling everything will increase build time and server size! .

Bonus Tip

serverDependenciesToBundle accepts string regex patterns. Use it to simplify and future-proof your ESM-only dependencies.

For example, the Flow Editor relies heavily on MDX, so we added the following regex patterns to include all UnifiedJS ESM bundles:

/^@mdx-js*/,
/^estree*/,
/^hast-*/,
/^mdast*/,
/^micromark*/,
/^remark*/,
/^rehype*/,
/^unist-*/,
/^vfile*/,

Server-Side Rendering Errors

Many React libraries are written assuming it'll run on the browser client, but by default Remix pre-renders your code on the server.

/API Limitations
ServerNo DOM access, can't use browser APIs like document or window.
ClientNo access to the server file system, so any library that depends on fs wouldn't work.

Remix is usually pretty good about tree-shaking and segregating code from the server/client bundles, but sometimes it needs a little help.

Client-Only Work Arounds

ReferenceError: "window" is not defined.
ReferenceError: "document" is not defined.
DomException: could not resolve "canvas".

These Errors indicate you have client code running on your server. Solving them usually involves some combination of the following:

1) Wrap the code in an useEffect.

useEffect(() => {
if (typeof document !== "undefined")
...
}, []);

The server will ignore the useEffect hook, so anything inside will only run client-side.

2) Re-export the dependency in a .client.tsx file.

// /components/newfile.client.tsx
export ClientBundle from "@some-library/graphs"

This tells Remix to exclude the dependency from the server bundle.

3) Wrap the component in <ClientOnly>

Import ClientOnly from sergioxda's handy remix-utils library.

Your code should look roughly like this:

import { ClientOnly } from "remix-utils";
import ClientBundle from "~/components/newfile.client";
<ClientOnly>
{() => <ClientBundle />}
</ClientOnly>

Server Code Leaking to Client

A less troublesome sibling to SSR Errors.

This has to do with how Remix (doesn't) work with Higher Order Functions, and can be fixed by moving relevant logic inside the Remix loader/action.

Alternatively, you can help the Remix compiler out by moving the server logic to a .server.tsx file and exclude it from the Client bundle.

Be Mindful...

As Center-Stack, Remix makes it easy to forget about the network chasm between the server and client, but be mindful that the data ultimately transmitted as JSON.

It must be serializable!

  • You cannot pass React components or JS methods from useLoaderData or useActionData!
  • Types like Dates will convert into strings!

Bonus Tip

Use the utility type SerializeFrom<> from "@remix-run/node" to resolve type errors between server and client code.

Nalu, Remixed

Tossing out the two years of work we spent building the Nalu wiki on NextJS wasn't an easy decision, but we saw massive potential in Remix for building the next-gen wiki engine.

In three months, the rebuilt Nalu.wiki is already more performant, efficient, and feature-complete than its NextJS predecessor!

Every day, I'm more confident that the bet is paying off.


As Nalu builds out, I'll be sharing more of my favorite Remix Design Patterns.

Watch this space!

NaluNalu
Improving Web Vitals in Remix
By xhomu
Improving Web Vitals in Remix

What is Nalu?

Nalu is a wiki engine built on Remix and MDX.

Why create content on Nalu?
By pogseal
Why create content on Nalu?