20

Trying out Next.js but I'm struggling with the following. Just tried to install react-hook-mousetrap and imported it like I normally would:

import useMousetrap from "react-hook-mousetrap";

This gives me the following error:

SyntaxError: Cannot use import statement outside a module
1 > module.exports = require("react-hook-mousetrap");

I am not sure what this means? I then thought it might be a problem with Nextjs's SSR, since my library enables hotkeys and will use some browser APIs. If you already know that I am on the wrong track here you can stop reading now.

What I did next however was this, I tried dynamic imports:

1. Dynamic import with next/dynamic

First thing I came across was next/dynamic, but this seems to be for JSX / React Components only (correct me if I'm wrong). Here I will be importing and using a React hook.

2. Dynamic import with await (...).default

So I tried dynamically importing it as a module, but I'm not sure how to do this exactly.

I need to use my hook at the top level of my component, can't make that Component async and now don't know what to do?

const MyComponent = () => {  
    
 // (1) TRIED THIS:
 const useMousetrap = await import("react-hook-mousetrap").default;
 //can't use the hook here since my Component is not async; Can't make the Component async, since this breaks stuff
 
 // (2) TRIED THIS:
    (async () => {
 const useMousetrap = (await import("react-hook-mousetrap")).default;
 // in this async function i can't use the hook, because it needs to be called at the top level.

    })()

 //....
}

Any advice here would be appreciated!

1
  • Thanks for sharing this answer. It really helped me out! Commented Mar 25, 2022 at 10:37

3 Answers 3

52

The error occurs because react-hook-mousetrap is exported as an ESM library.

Before Next.js 13.1

You can have Next.js transpile it using next-transpile-modules in your next.config.js.

const withTM = require('next-transpile-modules')(['react-hook-mousetrap']);

module.exports = withTM({ /* Your Next.js config */});

From Next.js 13.1

From Next.js 13.1, transpilation is natively supported, using next-transpile-modules is no longer needed.

module.exports = {
    transpilePackages: ['react-hook-mousetrap'],
    /* Your Next.js config */
};
Sign up to request clarification or add additional context in comments.

6 Comments

as a library maintainer is there a way to make it so out users don't need to do this step?
@Daniel From Next.js 12, as long as your ESM library has "type": "module" in its package.json you can import it in a Next.js project without using next-transpile-modules. See nextjs.org/blog/next-11-1#es-modules-support.
Hey @juliomalves could there be any reason that I'm still getting this error, even though I've changed the "type" to "module" in my package.json ?
@TochiBedford Is that the Next.js package.json, or the external library you're trying to consume package.json?
This is deprecated github.com/martpie/next-transpile-modules/releases/tag/the-end the link has how to use transpiling in next.js immediately
|
4

If you come across errors such as "SyntaxError: Unexpected token 'export'" or "'SyntaxError: Cannot use import statement outside a module'", along with similar issues, it's necessary to include the module causing the problem in the transpilePackages configuration.

next.config.mjs or next.config.js

const nextConfig = {
  reactStrictMode: false,
  transpilePackages: [
    "antd",
    "rc-util",
    "@babel/runtime",
    "@ant-design/icons",
    "@ant-design/icons-svg",
    "rc-pagination",
    "rc-picker",
    "rc-tree",
    "rc-table",
  ],
};

export default nextConfig;

Comments

3

I don't know if my answer is useful, but I faced this problem today, and this is what I did:

//test component for modal 
const Button: React.FC<{close?: () => void}> = ({ close }) => (
 <React.Fragment>
    <button type="button" onClick={ close }>Close</button>
 </React.Fragment>
);

// async call import react custom hook in next js whithout a dynamic 
//import
let newHook;

(async () => {
 const { hookFromNodeModules } = 
 await import('path/to/hook');

 newHook = hookFromNodeModules;
})();

export default function Home() {
// check if hook is available
const openModal = newHook && newHook();

const { t } = useTranslation('common');

// useCallback for update call function when hook is available
const handleOpen = React.useCallback(() => {
    openModal?.openModal(Button, {});
}, [openModal]);

 return ( ...your code )
}

I hope this helps!

Screenshot from next.js app

1 Comment

This work for me with Nextjs 13.5 and react-jsx-parser in Macos Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.