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.
- Read more about ESM vs. CommonJS here!
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.
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 |
---|---|
Server | No DOM access, can't use browser APIs like document or window. |
Client | No 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!
NaluWhat is Nalu?
Nalu is a wiki engine built on Remix and MDX.