I have a dynamic component loading another dynamic component. Both chunks get correctly loaded by react-lodable
_before_ main.js
is loaded (so chunk reporting is working as expected). However, the inner component generates a warning:
Warning: Expected server HTML to contain a matching <p> in <div>.
The content doesn't flash (I assume because the component has already been requested, so the "loading" state only lasts a few milliseconds), but the warning still indicates that something went wrong.
I updated my proof of concept:
https://github.com/arthens/nextjs-canary
Install deps, run it, open http://localhost:3000, you should see a warning in the console.
There shouldn't be any warning. The chunk has been pre-loaded, so it should always display the correct value and never go into loading state.
I tried reproducing the problem using react-loadable
directly, but I couldn't (note: my react-lodable
setup is significantly different because it's part of a large application, so it is possible that this is a react-loadable
bug that doesn't get triggered in my app)
The same happens to me. Your reproducible is very simple and it doesn't show that, but in more complex apps the layout of the page gets broken by this tag mismatch.
In production mode the warning is not shown, but the visual break still happens.
@zvictor please provide a reproduction so that I can look into it.
@timneutkens could you reproduce the warning with my proof of concept or do you need me to provide something else?
@timneutkens I just realised my previous proof of concept was wrong (because I forgot to push one commit 馃槥 ), I just pushed a new version. Apologies for the confusion.
without having ran the example yet, it looks like you are importing a page inside a page using Dynamic imports, in general it鈥檚 a bad idea to import pages in pages as ever js file inside the pages directory is a webpack entrypoint
Sorry, that wasn't intentional. I just wanted to import a dynamic component from another dynamic component and didn't pay attention to the fact that I was importing a page. I updated it so that there's only 1 page and 2 components and I'm still getting the warning.
@timneutkens I tried creating a reproducible that would have the visual break, but I failed to figure out what is causing that in my app.
All I managed to do in the end was a reproducible of the warning message, similarly to the reproducible supplied by @arthens
https://github.com/zvictor/nextjs-issue-5347
While taking care of this, it became very clear that as soon as a dynamically imported component imports another component dynamically, the warning message starts appearing.
I confirm the bug since 7.0.1
I'm currently stucked with 7.0.0 because otherwise this error breaks my layout. Since I'm using Drupal for a decoupled CMS with my app, a lot of my components are nested and using next/dynamic. Hope this get fixed soon.
@quietshu let us know if there is anything else we can do to help you on this. Like @mario-iliev stated, we are very interested on finding a solution for this issue.
Is it important for you that we manage to find a reproducible that shows the "visual break" or should we focus on investigating something else?
I'm running into this bug as well. However in my case it appears to be how I'm setting up the components to import. I've also tried reverting to 7.0.0
as @zvictor mentioned and I still see the same error on 7.0.0
so I'm not sure whether this is the same bug or not. I'm not nesting dynamic components within one another, I'm just importing them differently. But the behavior is identical.
When I define an object of components to import such as:
const Components = {};
Components['component-one'] = dynamic(import('./dynamic/component-one'));
Components['component-two'] = dynamic(import( './dynamic/component-two'));
This appears to work fine. But if I build the components dynamically like:
const componentList = [
'component-one',
'component-two',
];
const Components = {};
componentList.forEach(path =>
Components[path] = dynamic(import(`./dynamic/${path}`))
);
Then I end up with the same error on rehydrate in the client:
The working repository is here: https://github.com/KB1RMA/next-dynamic-working
Non-working repository is here: https://github.com/KB1RMA/next-dynamic-not-working
Same steps:
npm run dev
Other than this initial reload of the component in the client everything else appears to work fine.
@KB1RMA as much as I know your problem is with the import function it self. You can't use it with variables because it's executing before everything else and what actually is trying to import is ./dynamic/${path}
and not ./dynamic/component-one
. I'll be glad if I'm wrong, because I have the same problem.
I updated my reproducible trying to include the lessons brought by @KB1RMA.
You can now switch from different modes of how to import the components by uncommenting lines
I have noticed that around 50% of the times I reload my application it will render properly while in the remaining times the main content will jump to the left.
@timneutkens @zeit/docs
on the master
branch is producing a similar warning, if you're looking for something to troubleshoot with.
Warning: Expected server HTML to contain a matching <div> in <div>.
I finally got it! The jumping on the screen happens when static dynamic import is combined with ~Reactive Search~ ThemeProvider. I made a branch on the reproducible repo showing that.
SSR rendering:
Client rerendering:
What seems to be happening is that ReactiveBase
renders all its content properly (_Inner Component, in BLUE_) but somehow messes up with React's internal references. The sibling below a dynamically imported ReactiveBase
component will have all its properties/attributes sent to the next component and being replaced by the attributes of the previous component.
SSR rendering:
Client rerendering:
I am in doubt if this is a bug with dynamic import or with Reactive Search. Uncommenting the line that makes the import run from inside a function makes everything work fine again, even though it is still a dynamic import.
@metagrover would you like me to open an issue for that in Reactive Search as well?
Hello @zvictor, we wrap the content of ReactiveBase
component with emotion ThemeProvider
. Could that be causing this? 馃
You are right! I created yet another branch that uses emotion's ThemeProvider
and the problems exists there as well. I tried both emotion 9 (9.2.9) and 10 (10.0.6).
I am in doubt if this is a bug with dynamic import or with ~Reactive Search~ emotion. Uncommenting the line that makes the import run from inside a function makes everything work fine again, even though it is still a dynamic import.
@mitchellhamilton would you like me to open an issue for that in emotion as well?
Any update on the original issue? Getting the exact same issue and I have the latest version
@zvictor In your example, by using the "Component" on line 8 (inside the export default), you are disabling the SSR and because of that there is no problem. Yes it's still a dynamic import but it's loaded on the client side.
I'm really annoyed by this bug!
Because of which we have to release our application without SSR (which is a very big loss and a problem for us). But in the end, we prefer to be ignored by search engines and have a flickerish user experience then having all of the website chunks to be loaded from the client on first request.
Our app is combo of Next, Express, React and Styled Components.
I really hope this issue will be resolved soon from someone.
Added Connor on this issue.
I'm really annoyed by this bug!
You could contribute to Next.js in many ways, including solving this issue. 馃檪
@timneutkens I tried everything in the past 4 months, but it seems that my knowledge as a developer is not enough to help Next.js community. If I a was able to fix and share the fix, believe me I would :)
No worries, we'll look into it 馃憤
Seems like what's happening is:
loadable:initialized = false
index.js
=> outer.js
is initialized with loadable:createLoadableComponent()
(link)loadable:preloadReady()
(link) is called including outer.js
and inner.js
from __NEXT_DATA__
loadable:initialized = true
outer.js
=> inner.js
tries initialize with loadable:createLoadableComponent()
(link), but loadable:initialized == true
, so it can't.The loading element is hydrated on the client for inner.js
only. outer.js
get hydrated with the correct component.
console.log()
output:
// createLoadableComponent()
loadable.js:111 ["../components/outer"] "initialized:" false
// preloadReady()
loadable.js:264 茠 () {
return init();
}
loadable.js:265 ./components/outer.js
loadable.js:264 undefined // this is a value set by createLoadableComponent()
loadable.js:265 ./components/inner.js // because the above is undefined, it cannot hydrate inner.js
// createLoadableComponent()
loadable.js:111 ["../components/inner"] "initialized:" true // somehow this needs to happen before preloadReady()
I've solved the issue in #6326
It's out on next@canary right now, will do a new stable release soon 馃檹 Feel free to verify against canary
Appreciate the fix, Tim! Looking at your changes it's quite obvious now what was going on!
@timneutkens Thank you so much for your fast response to this issue.
Unfortunately the fix didn't resolve the problem in our app. I'll give my best to create a reproducible repository but it's gonna be hard and it'll take some time.
Meanwhile the only thing I can do is to describe several things from our code.
It would help to have a reproduction, otherwise it's going to be hard to investigate.
@timneutkens I was able to reproduce the error, here is the repo with the minimal config, I added the steps to reproduce it.
(https://github.com/jcampalo/next-styled-theme)
@timneutkens this isn't solved yet.
Wrapping next/dynamic
in a component show the error.
The following reproduces the error with:
next: 9.1.6
// components/DynamicImporter.js
import dynamic from 'next/dynamic'
import React from 'react'
const DynamicImporter = ({ loader, ssr = true }) => {
return dynamic({
loader,
ssr,
})
}
export default DynamicImporter
//components/Nav.js
import React from 'react'
function Nav() {
return 'nav'
}
md5-e51159b7d7967a69cecf2bfe9f5df985
//pages/index.js
import React from 'react'
const Nav = DynamicImporter({ loader: () => import('../components/Nav') })
function Home() {
return <Nav />
}
You should use next/dynamic
directly as documented:
const MyComponent = dynamic(() => import('../components/my-component'))
Main reason for this is that we have to compile a bunch of metadata in order to be able to correctly load the file server-side.
@timneutkens , thanks. It seemed to disable the problem for now
Most helpful comment
@timneutkens
@zeit/docs
on themaster
branch is producing a similar warning, if you're looking for something to troubleshoot with.