When you use react a component residing outside the main Next.js project folder which uses hooks. You end up getting Invalid hook call
error and the application breaks. Components without hooks work as expected.
The bug appears in all versions >9.0.5
when you change the webpack config so that files outside the main folder are transpiled. It’s working fine in <=9.0.5
Check out the repro at https://github.com/baldurh/next-9.0.6-bug-repro
The code should not break when using files outside the project folder.
>=9.0.6
I know this is probably not a common use of Next.js but in our project we’re using a monorepo and have a shared folder with components used by multiple applications. It would be nice to get this working again. If someone finds an alternative config we could use I’d also be happy to do that 🙂
@baldurh This is indeed very uncommon, when using platforms like Now only the folder where the Next.js app lives is deployed, it's better that way because otherwise you would need to know about all the external modules first. 2 better alternatives are:
@lfades thanks for the reply. Neither of those options are available to us and we're not deploying to Now or anything similar. We used yarn workspaces initially but then we integrated bazel and it does not play nicely with the symlinking nature of yarn workspaces. Npm packages means we cannot develop the shared modules as fast as we like. It's just too much overhead.
@baldurh Just ran into this with next-i18next as we've got nested NextJs apps as examples. Did you find a workaround?
@isaachinman We have not. We haven’t managed to upgrade to 9.x yet because of other reasons so I haven’t been looking into it. Does anyone have an idea of where the code affecting this might be? I’d love to understand the problem better.
I haven't had time to dig into this yet, but if anyone needs a repro: clone next-i18next, cd
into examples/simple
and upgrade the NextJs version to >=9.0.6.
It's currently on 9.0.3, so this is technically a breaking change on a patch.
I had a similar error come up recently and had to downgrade to 9.0.5 (and React 16.8.x). I sort of narrowed down the problem I saw to Next's use of MDX, but I don't have any concrete details beyond that.
I've dug into the same issue with a pretty big project based on Next & next-i18next.
I saw that this error can be thrown by 3 reasons:
The strange thing is that it happens only on production build.
@timneutkens @Timer sorry for tagging you in this but I’d love some input from you. Do you think this is something that could be fixed do we all need to implement some workarounds? This is a pretty big blocker for us at the moment. Thanks.
It seems you aliased react
but not react-dom
. Can you try that?
@Timer Thanks. I tried but it did not have any effect
I was able to resolve this just now in the repro by moving the react
and react-dom
dependencies one level up. I just pushed the changes if anyone wants to try it out. I haven’t tried it on our actual project but I’m hopeful it will work for us. Perhaps this could solve the issue for @isaachinman, @jaredcwhite and @felixmosh as well?
@Timer I got this working in our project but I also had to make sure I had no other dependencies which installed react
into our projects’ node_modules
folder. In our case it was related to storybook (yarn why react
helped a lot 😄). We were planning to move storybook to a separate project anyway so I think this solution will work in our case.
Ah, yes. Improperly published node_module
packages will cause this (with dependencies on react(-dom|)
instead of peer dependency).
I was able to resolve this just now in the repro by moving the
react
andreact-dom
dependencies one level up. I just pushed the changes if anyone wants to try it out. I haven’t tried it on our actual project but I’m hopeful it will work for us. Perhaps this could solve the issue for @isaachinman, @jaredcwhite and @felixmosh as well?
Can you elaborate regarding the changes that you have done in this repo?
I run npm ls react
or npm ls react-dom
I got only my next app in the list.
@felixmosh Sorry, apparently the push failed for me yesterday. Now the changes are definitely there 😅 I moved react
and react-dom
from the app
folder into the root folder so now you need to do yarn/npm install
in both the app
folder and the root folder before you run the app
. Hope this is clear enough.
We’re going to have to do some changes to our build system to get this working in production so this solution is still a bit of a hassle for us 😝
Thanx for the explanation, I will wait for Next team to solve it, sounds a bit weird to put react deps on the root of my mono-repo...
@felixmosh Yeah I kind of agree with you. However, if you use something like yarn workspaces that’s exactly what that tool will do. If you have the same dependency in two or more projects it will hoist the dependencies to the root. It’s nice because it ensures that you have the same version of the dependencies in all your projects. But if you don’t have a tool like that you’d have to manage it yourself which is indeed a bit awkward. I agree the best solution would be that the Next.js team takes a look and solves this for all of us 😇🙏🏻
Running into the same issue, and bringing react
and react-dom
one level up and running the server from the root is the only workaround that works currently on 9.1.5. Linking https://github.com/facebook/react/issues/13991 and https://github.com/facebook/react/issues/15315#issuecomment-479802153 for reference as I found those links before this issue.
Hi any update on this issue? We have a monorepo and we are encountering this exact issue.
Meet with the same problem.
v9.0.5 works great with hooks for components imported outside the rootFolder.
Starting from 9.0.6 till 9.1.6-canary.5 have the same problems.
The problem occurs only on the server-side. If SSR disabled (eg. load external component via dynamic) then all works as expected for versions >=9.0.6.
@nodkz it is an issue with react package resolving, therefore it happens only in node.
@Timer this issue got move to the "next" milestone for around 6 versions, it prevents me from updating to the latest version.
I've wasted entire day of time in accommodation on it, don't know what is the source issue of it, even tried the workaround (which didn't worked).
Do you need any help of investigating any direction?
Do you have any gut-feeling about it?
Why is that happens only on production build?
What was changed from 9.0.5
to 9.0.6
that may be related to this?
Thanx 🙏🏼
I think that I've found the issue!!!
I think that it is a combination of 2 things:
i18next
/ react-i18next
were not external in server bundles!!useTranslation
hook...So i've investigated the reason for why there are node modules inside server bundles at all (best practice for server bundles is to make them externals).
I saw that next has some exceptions for next lib (why?), the funny part is that this regex catches all libs that ends with next/dist/
, as i18next
& react-i18next
does!!
So, if you change this:
res.match(/next[/\\]dist[/\\]/)
into
res.match(/[/\\]next[/\\]dist[/\\]/)
The server bundle will exclude all libs that are not next
& ends with next/dist
, and it solved the issue!
For me the main issue is the new way requests are resolved: https://github.com/zeit/next.js/blob/canary/packages/next/build/webpack-config.ts#L446
Since we have components outside the project root the request resolution will throw an error which results in react
being bundled in the server chunks. And that’s why we get the Invalid hook call
error on the server.
@baldurh context
in that expression you linked is provided by webpack, and is different that the compilation root (your project directory).
It's always the directory of the file that's issuing the require.
Right. I’ve patched this to make it work for us for now. I think we’ll eventually change the structure of the code so that the dependencies are shared on an upper directory level. However, even if react
is available in the external folder (outside the root) I still got the error.
I'm trying to use a linked package and I'm experiencing the same issue. Sadly none of the fixes from https://github.com/facebook/react/issues/13991 work 🙁
I'm also experiencing the same issue with a component library symlinked with yarn link
. This worked fine up until v9.0.6-canary.4
and now I'm in the same position as some other commenters and cannot upgrade past this.. I have pin-pointed the change to this PR https://github.com/zeit/next.js/pull/8739
My component library uses react
, react-dom
, and styled-components
. The configuration for this is as follows
Update
I was able to fix this by including these modules in the server externals. Thanks to @HosseinAgha in this comment https://github.com/martpie/next-transpile-modules/issues/50#issuecomment-558318688
if (isServer) {
config.externals = [
'react',
'react-dom',
'styled-components',
...config.externals
]
}
I'm seeing the exact same issues, none of the workaround worked for me so far.
My package works if I publish it and install it (and use resolve.alias in my next.config.js).
But running a dev build with the package linked via yarn link @mypackage
always results in an invalid hook error.
I was also able to get it working by modifying node_modules/dist/build/wepack-config.js
and commenting out the lines added in https://github.com/zeit/next.js/pull/8739
What I'm seeing if I log baseRes and res is that the if condition triggers as:
Update:
We managed to work around the problem by converting our package to use yarn workspaces (even though our repo only contains a single package).
We moved our code from ./src to ./package/our-package-name/src and setup yarn workspaces => https://classic.yarnpkg.com/en/docs/workspaces/
This works around the problem as this will hoist common dependencies to the top level ./node_modules folder and ./package/our-package-name/node_modules will remain largely empty.
So now when we link our package next won't pull in a second version of react anymore (as it's not present in our packages node_modules folder) and everything works as it should.
I have the same fucking problem. ¬¬'
We generally collapse swearing as it's against the code of conduct.
Sorry, I was angry with this bug. Actually, I like too much of the Next.JS. But no can use it cause this issue is boring.
We are facing this issue when working with external local packages and next-transpile-modules
.
As we want to stick with the latest version of Next.js, I will try to submit a patch to Next.js if I find the root cause.
I face the same issue, after installing [email protected]
My dependencies are [email protected], [email protected], [email protected] and of course many other libs (but everything was working before installing next-i18next). If someone has a workaround, it could be great :+1:
Thank you for posting this issue, I also had problems with symlinking our design system (react component library) package. We are also transpiling it using https://github.com/martpie/next-transpile-modules.
The fix as suggested here worked for us:
node_modules
folder (we did it with our own utility instead of npm link
but should basically be the same)next.config.js
:// next.config.js
const nextConfig = {
webpack: (config, options) => {
// modify the `config` here
if (options.isServer) {
config.externals = ["react", ...config.externals];
}
config.resolve.alias["react"] = path.resolve(__dirname, ".\\node_modules\\react");
return config;
},
};
// more plugins etc...
Our alternative workaround that requires no config
node_modules
from your package. I created my own utility for this can maybe post it on github. But would be nice to see this fixed in NextJS, I spent so much time trying to understand why webpack alias worked for all my non NextJS project :)
PS. I have no idea how this would affect a production build but we would only use this during dev
if (options.isServer) {
config.externals = ["react", ...config.externals];
}
react
is already external server-side.
config.resolve.alias["react"] = path.resolve(__dirname, ".\\node_modules\\react");
This won't solve the issue.
As said before this issue is related to your dependencies depending on react
whereas they should have a peerDependency
on react
(and react-dom if they need it).
@timneutkens
Well no this is not always the case. I for sure has react and react-dom as peer dependencies. The problem still occurs though if you for example symlink your library to a nextjs project. What then happens is that you will have a node_modules
folder inside your library (at least if you ever ran npm i
or npm link
in that library folder).
When react gets resolved from this library folder it will resolve to the one in that node_modules
folder and you get dual copies of react causing the problem. If I delete the node_modules
folder inside my lib or install it using something else than npm link
then yes ofc it works (if you use peer dependencies or exactly the same react version).
So to solve this during dev you want to be able to alias react to force everyone to use the same version. Because of the problems mentioned here this has no effect in the current NextJS version without adding the config.externals ...
part (at least for me), probably as people have mentioned here because of some change as noted here #8739 ?
A similar issue is happening to me but (potentially) because of the material-ui library (as outlined in #10162), my temporary fix for now was just to add npm run clean
in my preserve
and dev
scripts as outlined here:
https://github.com/zeit/next.js/issues/10162#issuecomment-612501431
@timneutkens I understand the real issue has to with how those dependencies are listing their own deps (deps vs peer-dep) but any idea what we can do in our own app to fix this more permanently?
@ryan-0 Have you any special setup? Would be surprised if material Ui doesn’t list react as a peer dep? Like do you use a very old react version or symlink anything?
no special setup.. no symlinking and react 16.13.1
--> we have some other deps that might be causing the issue to be fair, but at least according to that repro it seems maybe related to material-ui/core (which we also use):
https://github.com/zeit/next.js/issues/10162
@ryan-0 is there a node_modules folder with react inside your material-ui folder?
Also does is start working after running npm dedupe?
no it doesn't look like there is a nested node folder, which is why I'm confused as to how the bug is happening. and no npm dedupe
did not work :(
Strangely, using resolve.alias
does not seem to affect packages outside the project root.
Here's my next.config.js
file:
const path = require('path')
module.exports = {
webpack: config => {
const { alias } = config.resolve || {}
alias.react$ = path.resolve('node_modules/react')
alias['react-dom$'] = path.resolve('node_modules/react-dom')
config.resolve = {
...config.resolve,
alias,
}
return config
}
}
I'm using yarn link
with a local package that exists in a Lerna monorepo. Its node_modules
does not contain a copy of react
, but the monorepo root does. I wouldn't expect that to make a difference as long as resolve.alias
is used, but that's unfortunately not the case. After removing the copy of react
from the monorepo root, I'm now getting a Cannot find module 'react'
error instead.
Did someone find a good solution for this?
I have a linked next library which I am using next-transpile-modules
to have it added to my 'consumer' project. I added the react
alias in my next.config.json
as mentioned in their docs but it wasn't enough. I am still getting the error of duplicated dependencies for React.
You could try using relative-deps
by @mweststrate
Did someone find a good solution for this?
I have a linked next library which I am using
next-transpile-modules
to have it added to my 'consumer' project. I added thereact
alias in mynext.config.json
as mentioned in their docs but it wasn't enough. I am still getting the error of duplicated dependencies for React.
Yes see my post above you need to add the config.externals part in my sample, then alias starts working again
@johot I tried your solution but it didn't quite work for me. I started getting some weird errors, but mainly this one: cannot destructure property 'query' of 'Object(...)(...)' as it is null
after trying your solution. The object seen as null in this case is the useRouter
from next/router
.
@aleclarson Thanks for the tip. I will give it a try if I can't make it work with the next setup. Are you using it currently?
If you are using next-transpile-modules
and Yarn, the solution is quite straightforward: https://github.com/martpie/next-transpile-modules#i-have-trouble-with-duplicated-dependencies-or-the-invalid-hook-call-error-in-react
If you are using npm
, I am still myself looking for a solution :c
Ok, so my final solution was to migrate from yarn link
to yalc
. I have a gulp task which watch for files changes and copy the files to my dist
folder and then pushes the changes to the yalc store.
In my 'consumer' I modified the tsconfig.json
to resolve the paths like this:
"paths": {
"~/*": ["/src/*"],
"my-library/*": ["./node_modules/my-library/dist/*"]
},
and in the next.config.js
I added the following:
experimental: {
jsconfigPaths: true, // enables it for both jsconfig.json and tsconfig.json
}
So next can resolve the paths based on the tsconfig.json paths
. More info here.
Long story short: combining yalc
+ next-transpile-modules
improved a lot my local development setup. No duplicate dependencies and weird errors. The behavior of directly adding the module using yarn add
and linking the module with yalc
are pretty much the same.
If you're using a locally linked library that depends on styled-components
, refer to this: https://styled-components.com/docs/faqs#linking-in-an-ssr-scenario
In server/index.js
:
const moduleAlias = require('module-alias');
moduleAlias.addAlias('styled-components', path.join(__dirname, '../node_modules/styled-components'));
But, we also needed to add the following in next.config.js
:
config.resolve.alias['react'] = path.resolve(__dirname, './node_modules', 'react');
config.resolve.alias['react-dom'] = path.resolve(__dirname, './node_modules', 'react-dom');
config.resolve.alias['prop-types'] = path.resolve(__dirname, './node_modules', 'prop-types');
config.resolve.alias['styled-components'] = path.resolve(__dirname, './node_modules', 'styled-components');
Hope it helps.
Tested with:
Next: 9.3.5
React: 16.13.1
styled-components: 5.1.0
Folks, simple fix, remove your global version of react, next and react-dom by doing:
npm remove -g react next react-dom
Folks, simple fix, remove your global version of react, next and react-dom by doing:
npm remove -g react next react-dom
I'm glad that worked for you but I doubt many people in this thread have these dependencies globally installed.
Not only web!
react 16.9
react-native 0.62
Running on Android
Maybe the smallest reproducer in history?
import React, { Component, useState } from 'react';
import {
AppRegistry,
} from 'react-native';
function hooker() {
const [count, setCount] = useState(0)
}
class ClassA extends Component {
constructor(props) {
super(props)
//hooker(); //Invalid hook call Error
}
componentDidMount(){
//hooker(); //Invalid hook call Error
}
render() {
hooker(); //Invalid hook call Error
return (
null
);
}
}
export default function App(props) {
//hooker(); //No problem
return (
<ClassA/>
);
};
AppRegistry.registerComponent('default', () => App);
I faced this issue as well and fighting it down to use yarn
instead of npm
(with npm it did not work) and using https://github.com/vercel/next.js/issues/9022#issuecomment-616169466
Is there a solution for this at all?
Just completely stuck with version 9.4.4.
Issue happening on HOC for private routes below. I also tried using withRouter
but then the same error is thrown on the wrapped component instead.
import { useRouter } from 'next/router'
function withPrivateRoute(WrappedComponent) {
const router = useRouter(); //**** ERROR IS THROWN HERE *******
class WPR extends React.Component {
componentDidMount(){
console.log('wrappeed', WrappedComponent);
// const { router } = this.props;
const intendedRoute = router.pathname;
// const isAdmin = !!cookies.get('isAdmin');
// const isAuthenticated = !!cookies.get('username');
const isAuthenticated = false;
const isAdmin = false;
if (!isAuthenticated) {
router.push({
pathname: '/login',
query: { from: intendedRoute },
});
}
if (
isAuthenticated &&
router.pathname.includes('admin') &&
!isAdmin
) {
router.push('/');
}
}
render() {
return ({ ...props }) => <WrappedComponent {...props} />;
}
}
return WPR;
}
export default withPrivateRoute;
I had the same issue so I had to go back to my previous branch(where I assumed this issue wasn't there) and add the latest code file by file and found that the issue was
import { useToasts, AppearanceTypes } from 'react-toast-notifications';
export const showToast = (message: string, appearance: AppearanceTypes) => {
const { addToast } = useToasts();
addToast(message, {
appearance,
});
};
I was using a toast service and on every request if there's an error showToast
pops up but now with this error I don't think i'll be using this service
I can confirm that this is related to the PR https://github.com/vercel/next.js/pull/8739 by @arcanis
We are using a monorepo setup with Rush and pnpm which eliminates the reason the mentioned PR was merged. It also means that the point that @timneutkens made in https://github.com/vercel/next.js/issues/9022#issuecomment-610255555 does not apply to us, we have the following structure:
website
dependencies: next, react, react-dom, library
library
devDependencies: react, react-dom (for tests)
peerDependencies: react, react-dom
The library.devDependencies.(react|react-dom)
are symlinks that point to the exact same files as the website.dependencies(react|react-dom)
. However it looks like [email protected] that is used in the https://github.com/vercel/next.js/blob/f98e38c9b634b85e6679e7b5f953a9d98074cfc3/packages/next/lib/resolve-request.ts#L16 does not follow the current default Node.js behaviour by preserving the symlinks.
We ended up with the following:
library
resolve.symlinks = true
in the webpack config inside of the next.config.js
library
to be requested from the library/node_modules
(for the server-side build to resolve modules correctly)This works as intended but feels hacky, given that Next.js is powering some of the important websites like Apple's is it possible to expect better support for the monorepos that are often used to manage shared code in those big projects?
I've been playing around with these and found that, when I use a HOC it throws an error, but, if I use a component as a wrapper, it works fine.
If someone is interested, I have a repo where you can reproduce this: next-components-hooks-error
components/withPrivateRoute.js
-> Higher Order Component
import React, { useEffect } from 'react';
import { useRouter } from 'next/router';
const withPrivateRoute = WrappedComponent => {
const router = useRouter();
const [loading, setLoading] = useState(true);
useEffect(() => {
const user = localStorage.getItem('user');
console.log(user);
if (!user) {
router.push('/login');
} else {
setLoading(false);
}
}, []);
return ({ ...props }) => !loading && <WrappedComponent test={test} {...props}/>;
};
export default withPrivateRoute;
pages/hoc.js
-> Does not work (page using the HOC)
import React from 'react';
import withPrivateRoute from '../components/withPrivateRoute';
const HocTest = () => <p>Authorization HOC Test!</p>;
export default withPrivateRoute(HocTest);
components/AuthLayout.js
-> Component (wrapper)
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
const AuthLayout = ({ children }) => {
const router = useRouter();
const [loading, setLoading] = useState(true);
useEffect(() => {
const user = localStorage.getItem('user');
console.log(user);
if (!user) {
router.push('/login');
} else {
setLoading(false);
}
}, []);
return !loading && (
<React.Fragment>
{children}
</React.Fragment>
);
};
export default AuthLayout;
pages/wrapper.js
-> Page using the wrapper component, it works
import React from 'react';
import AuthLayout from '../components/AuthLayout';
const WrapperTest = () => (
<AuthLayout>
<p>Authorization Wrapper Test!</p>
</AuthLayout>
);
export default WrapperTest;
hey @Timer is there a progress on this?
I resolve my issue using https://github.com/vercel/next.js/issues/9022#issuecomment-609969178 as a solution.
My issues was with using my library repo and yarn link
ing it with my app repo
example
package.json
{
"dependencies": {
"next": "9.5.1",
"myUILibrary": "git+ssh://[email protected]/MyRepo/library-web-ui.git#master",
"react": "16.13.1",
"react-dom": "16.13.1"
}
}
and I yarn link myUILibrary
to my locally checkout MyRepo/library-web-ui
which also has react
installed.
Thanks a lot @johot for posting your solution
5 🌟 out of 3 (yes! All the stars and more!)
I can confirm the same experience as @wasd171 in a Rush + PNPM monorepo. I'll just use ~9.4.4
in the interim.
I'm having the same exact issue with Rush + PNPM 👍
ok I had a very stupid error causing this issue:
import React, { useState } from 'React';
It should be react instead of React:
import React, { useState } from 'react';
Yup. I am seeing this as well in 9.5.x
- Downgrading to 9.4.4
works - You can reproduce this with the next-site
as well
I couldn't fix this error in 9.5.2. I tried everything. But everything works perfectly in 9.5.3 for me without any tricks.
I'm not using pnpm. I'm just trying to npm link
to a package containing some components that get transpiled through next-transpile-modules.
I spoke too soon. I don't think it's working with 9.5.3 either.
It's working reliably in 9.5.3 for me now. 🤷 I don't know what's going on anymore.
9.5.3 doesn't work for me -- same error. I'm using Rush + NPM. Is there a known workaround? (btw lets update the title because it is not about 9.0.6 anymore)
FYI, it was one of the reasons why my org decided to move from npm
to yarn
. It just (unfortunately) plays much nicer. The move is annoying, but we're quite happy now.
Transpiled modules with hooks don't work for me either.
By the way, to anyone experiencing this issue when using next-transpile-modules
and npm
, I wrote a FAQ section that explain the problem and potential solutions: https://www.npmjs.com/package/next-transpile-modules#i-have-trouble-with-duplicated-dependencies-or-the-invalid-hook-call-error-in-react
I managed to solve this by manually adding the version resolution for yarn in the project root. So instead of moving all react dependencies to the root package.json
, I added the following lines:
"resolutions": {
"react": "16.13.1",
"react-dom": "16.13.1"
}
See: https://classic.yarnpkg.com/en/docs/selective-version-resolutions/
It's worth noting that in my case, local builds worked whereas builds on Vercel reported the Invalid hook call
error.
I experimented a similar issue in _app.js
with a catchall page in Next 10
Hey,
In my case I had transpiled modules that were also linked via npm link
.
Dependencies like React need to be included as peerDependencies
instead of regular dependencies because it was downloading multiple instances of it. So if you're having the invalid hooks error, try these steps:
npm install
to get all your modules installed.sudo npm link
.npm link @example/project
. You should see a little arrow icon near your module name inside node_modules if you're using Visual Studio Code.npm run dev
.Again, you must include React as a peerDependency instead of a regular dependency in your @example/project.
I hope that helps!
I have a monorepo with a next.js project inside. Faced the same issue with an invalid hook call after installing storybook
. Got the issue resolved by following @aengl's suggestion – I added the resolutions
to the root level package.json
:
"resolutions": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
I'm not sure if that's going to be a good solution once we're adding more clients and packages though.
Most helpful comment
Hi any update on this issue? We have a monorepo and we are encountering this exact issue.