Fixing 'SyntaxError: Cannot use import statement outside a module' in Next.js + Jest: Troubleshooting rehype-raw with transformIgnorePatterns
If you’ve worked with Next.js and Jest for testing, you may have encountered the frustrating SyntaxError: Cannot use import statement outside a module. This error often arises when testing components that rely on ES Modules (ESM)-only libraries—rehype-raw being a common culprit. Rehype-raw is a popular plugin for processing HTML in markdown (used with tools like Remark/Rehype), but its ESM structure can clash with Jest’s default CommonJS (CJS) environment.
In this guide, we’ll demystify why this error occurs, break down the role of Jest’s transformIgnorePatterns configuration, and walk through a step-by-step solution to resolve it. By the end, you’ll have a clear understanding of how to adapt your Next.js + Jest setup to work seamlessly with ESM dependencies like rehype-raw.
Table of Contents#
- Understanding the Error
- Why rehype-raw Triggers This Issue
- The Role of
transformIgnorePatternsin Jest - Step-by-Step Troubleshooting
- Alternative Fixes
- Common Pitfalls to Avoid
- Conclusion
- References
Understanding the Error#
The error SyntaxError: Cannot use import statement outside a module occurs when Jest (running in CommonJS mode) encounters an ES Module (ESM) syntax—like import or export—in a file it’s trying to execute.
What’s the Difference Between ESM and CommonJS?#
- CommonJS (CJS): The traditional module system in Node.js, using
require()andmodule.exports. It’s synchronous and widely supported but lacks features like top-level await. - ES Modules (ESM): The modern JavaScript module standard, using
importandexport. It’s asynchronous and supported in modern browsers and Node.js (with"type": "module"inpackage.json).
Jest, by default, runs in CJS mode. When it processes a dependency written in ESM (like rehype-raw), it can’t parse import statements, leading to the syntax error.
Why rehype-raw Triggers This Issue#
Rehype-raw is an ESM-only library. This means its source code uses import/export syntax instead of CJS’s require/module.exports. Here’s why this causes problems in Jest:
- Jest Ignores
node_modulesby Default: Jest’s default configuration skips transpiling files innode_modules(viatransformIgnorePatterns), assuming they’re precompiled to CJS. - Rehype-raw Isn’t Transpiled: Since rehype-raw is ESM, its code in
node_modulescontainsimportstatements. When Jest tries to run tests that import rehype-raw, it encounters theseimportstatements and throws the error.
The Role of transformIgnorePatterns in Jest#
To resolve the error, we need to tell Jest to transpile rehype-raw (and its ESM dependencies) so it can parse the import syntax. This is where transformIgnorePatterns comes in.
What is transformIgnorePatterns?#
transformIgnorePatterns is a Jest configuration option that defines a regular expression pattern of files/directories Jest should ignore when transpiling (converting ESM to CJS, or ES6+ to ES5). By default, it’s set to:
transformIgnorePatterns: ["/node_modules/"]This tells Jest: “Don’t transpile anything in node_modules.”
The Fix: Adjust transformIgnorePatterns#
To transpile rehype-raw, we need to exclude it from the ignore list. We’ll modify transformIgnorePatterns to ignore all node_modules except rehype-raw (and any nested ESM dependencies it has).
Step-by-Step Troubleshooting#
Let’s walk through the exact steps to fix the error.
Step 1: Verify Your Jest Configuration#
First, ensure your project uses the official Next.js Jest preset (next/jest), which simplifies setup for Next.js apps. Check your jest.config.js (or jest.config.ts) in the root directory.
A typical Next.js Jest config looks like this:
// jest.config.js
const nextJest = require("next/jest");
const createJestConfig = nextJest({
dir: "./", // Path to your Next.js app
});
const customJestConfig = {
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
testEnvironment: "jest-environment-jsdom",
// Other config...
};
module.exports = createJestConfig(customJestConfig);Step 2: Modify transformIgnorePatterns#
Update customJestConfig to include transformIgnorePatterns, excluding rehype-raw from the ignore list. Use a negative lookahead regex to tell Jest: “Ignore node_modules except for rehype-raw.”
Updated Config:#
// jest.config.js
const nextJest = require("next/jest");
const createJestConfig = nextJest({
dir: "./",
});
const customJestConfig = {
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
testEnvironment: "jest-environment-jsdom",
// Add this line to exclude rehype-raw from transformIgnorePatterns
transformIgnorePatterns: [
"/node_modules/(?!(rehype-raw)/)", // Allow transpiling rehype-raw
],
};
module.exports = createJestConfig(customJestConfig);Step 3: Ensure Proper Transpilation Setup#
Jest transpiles files using a transformer (e.g., babel-jest or @swc/jest). For ESM dependencies like rehype-raw, we need to ensure the transformer can handle ESM syntax.
If Using Babel:#
Ensure you have @babel/preset-env installed to transpile ESM to CJS. Your babel.config.json (or .babelrc) should include:
{
"presets": [
"@babel/preset-env", // Transpiles ESM to CJS
"@babel/preset-react",
"next/babel" // Next.js preset
]
}If Using SWC (Next.js Default):#
Next.js 12+ uses SWC for faster builds. If you’re using @swc/jest, ensure your swcrc (or next.config.js) is configured to handle ESM. For most cases, the default SWC config works, but verify jsc.target is set to a compatible environment (e.g., es2017).
Step 4: Handle Nested Dependencies (If Needed)#
Rehype-raw may depend on other ESM libraries (e.g., hast-util-raw, unist-util-visit). If the error persists, check the stack trace to identify additional ESM dependencies and add them to transformIgnorePatterns.
For example, if hast-util-raw is also causing issues, update transformIgnorePatterns to include it:
transformIgnorePatterns: [
"/node_modules/(?!(rehype-raw|hast-util-raw)/)", // Add nested ESM dependencies here
]Step 5: Clear Jest Cache#
Jest caches transpiled files, so changes to transformIgnorePatterns might not take effect immediately. Clear the cache with:
npx jest --clearCacheThen re-run your tests:
npm testAlternative Fixes#
If modifying transformIgnorePatterns doesn’t work, try these alternatives:
1. Mock Rehype-raw in Tests#
If your test doesn’t need to validate rehype-raw’s functionality, mock it using Jest’s jest.mock:
// In your test file (e.g., MyComponent.test.js)
jest.mock("rehype-raw", () => ({
default: jest.fn(), // Mock the rehype-raw plugin
}));2. Use Dynamic Imports#
In your component, dynamically import rehype-raw with next/dynamic (for client-side code) to avoid loading it during tests:
import dynamic from "next/dynamic";
const rehypeRaw = dynamic(() => import("rehype-raw"), { ssr: false });Note: This only works if rehype-raw is used client-side. For server-side code (e.g., getStaticProps), this won’t help.
3. Enable ESM Mode in Jest#
You can run Jest in ESM mode by:
- Adding
"type": "module"topackage.json(makes the entire project ESM). - Updating Jest config to use ESM:
// jest.config.js export default { preset: "next/jest", transform: {}, // Disable CJS transformers };
Warning: This requires all dependencies to support ESM and may break CJS-only tools.
Common Pitfalls to Avoid#
- Incorrect Regex in
transformIgnorePatterns: Ensure the regex syntax is correct. For example,/(?!rehype-raw)/won’t work—use/node_modules/(?!(rehype-raw)/)to target the specific package. - Forgetting Nested Dependencies: If rehype-raw depends on other ESM libraries (e.g.,
unist-util-visit), add them totransformIgnorePatterns(e.g.,/(?!rehype-raw|unist-util-visit)/). - Using the Wrong Jest Preset: Always use
next/jestas the preset for Next.js apps to ensure compatibility with Next.js features (e.g., CSS modules, image optimization). - Not Clearing the Cache: Run
npx jest --clearCacheafter modifyingtransformIgnorePatternsto avoid stale cached files.
Conclusion#
The SyntaxError: Cannot use import statement outside a module in Next.js + Jest is typically caused by ESM dependencies like rehype-raw being ignored by Jest’s default transformIgnorePatterns. By adjusting transformIgnorePatterns to transpile rehype-raw (and its ESM dependencies), ensuring proper transpilation setup (Babel/SWC), and clearing the Jest cache, you can resolve the error.
Remember to check the module type of dependencies when debugging—ESM-only packages require special handling in Jest’s CJS environment.