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
|
3

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.