Next.js + dynamic imports: server and client markup mismatch

Does anyone who uses Next.js with ReScript ever faced an issue when server and client markups of dynamically loaded component don’t match? I.e. an error like:

Did not expect server HTML to contain a <div> in <div>

The markups themselves are the same. I rechecked it multiple times and pretty sure it’s not some silly issue when markups are actually different.

I spent the whole day trying to hunt it down, disassembled the app and was able to reproduce it only when dynamic gets imported via require instead of import. But in the app itself dynamic gets imported via import in the generated code. The last thought was that it’s something with next-transpile-modules but it shouldn’t touch these modules and I can’t repro it on the simple case. So I’m out of ideas why this happens.

Usually I got this when I have variation between server generated code & client side code only.

I would recommend using a useEffect to call code for client side if you got any.

It’s not my case b/c it errors on 1-char mdx or on component with empty div. I’m 95% sure it’s something with commonjs/es6 modules.

I have seen that when trying to use dynamic imports. I haven’t tried it with the latest next-transpile-moduels

Hunted down this little bitch lol

The problem was that for some reason each dynamic loader needs own options object.

This triggers warning:

open Next.Dynamic

let options = options(~loading=() => <Spinner />, ())

let items = [
  dynamic(() => import("posts/foo.mdx"), options),
  dynamic(() => import("posts/bar.mdx"), options),
]

But this does not:

open Next.Dynamic

let options = () => options(~loading=() => <Spinner />, ())

let items = [
  dynamic(() => import("posts/foo.mdx"), options()),
  dynamic(() => import("posts/bar.mdx"), options()),
]

¯_(ツ)_/¯

6 Likes

It only took one weekend.

1 Like

omg haha

1 Like

Can you explain how you are using Next.Dynamic further?

I am a little confused how you are using the items array at a later time.

Thanks in advance!

So I believe I have the correct use Next.Dynamic implemented, but I am still seeing issues with the: ``

As you can see the <Wonka.Pixels /> component is still trying to be loaded on the server while SSR is FALSE.

Also, I am not sure the correct import is being made coming from a mjs file from rescript (I have tried import("") and import("").make but both result in the same error. If it should be import().make how would I implement this within the rescript DynamicPixels.res file?

Thanks!

I’m afk right now, but you can dig into the sources of my blog, where I use this code. Hope it helps.

2 Likes

I see one major difference, I am using a new binding that has since been added to the template:

@val external import_: string => Js.Promise.t<'a> = "import"

I am still having a lot of issues with default exports and overall getting SSR: false working unfortunately. Wish there was an example included in the template for using the Dynamic module. :frowning:

Any insights @ryyppy ?

Yeah we should probably add a curated dynamic imports example.

I can recall that using the import_ binding was problematic, and that a raw expression on the usage site was the only way to make it behave correctly (I am currently not sure what magic Next involves to understand dynamic imports).

For the rescript-lang.org playground, I actually used JS to lazy load the page: rescript-lang.org/try.js at master · rescript-association/rescript-lang.org · GitHub

I will see if I can add some example to the template.

1 Like

That is exactly what I ended up going with, just made a simple component locally rendered the shared component from a pinned dependency and then used JS to use next/dynamic and then created a simple binding to that JS component and we were in business.

I also can now simply use a then() in JS to resolve to module.make as well :slight_smile:

Thanks again!