What this usually means
Babel transform plugin errors typically indicate a mismatch between the plugin's expected interface and what Babel receives. This can happen when you install the wrong version of a plugin (e.g., a v6 plugin with Babel 7), when the plugin exports an object instead of a function (common with third-party packages that haven't been updated), or when Babel's configuration merges multiple configs that introduce conflicting plugins. The error 'not a function' is especially telling: Babel expects each plugin to be a function or an array of [plugin, options], but sometimes a module exports a default object or an incorrect shape. Another frequent cause is incorrect plugin ordering—some plugins must run before others (e.g., @babel/plugin-proposal-decorators before @babel/plugin-proposal-class-properties). The error messages can be cryptic, but they always point to a configuration or compatibility issue.
The first ten minutes — establish facts before touching code.
- 1Run `npx babel --version` to confirm Babel core version (must be 7.x for modern plugins).
- 2Check the exact error stack trace: identify which plugin file is throwing the error (path is usually in node_modules/@babel/plugin-xxx).
- 3Run `npm ls @babel/core @babel/preset-env @babel/plugin-xxx` to see installed versions and detect duplicates.
- 4Temporarily remove all plugins and add them back one by one to isolate the culprit.
- 5Verify that the plugin's peer dependencies are installed (e.g., @babel/core for Babel 7 plugins).
The specific files, logs, configs, and dashboards that usually own this bug.
- searchbabel.config.js or .babelrc: the primary configuration file where plugins are listed.
- searchpackage.json's 'babel' field: sometimes config is hidden there.
- searchnode_modules/@babel/plugin-xxx/package.json: check the 'main' field and peerDependencies.
- searchnode_modules/@babel/core/lib/config/plugin.js: Babel's internal plugin loading logic.
- searchBuild logs (usually stdout/stderr from your bundler): look for the first error message.
- searchWebpack/rollup config if using a babel-loader: ensure the loader is not applying multiple babel transforms.
Practical causes, not theory. These are the things you will actually find.
- warningPlugin version mismatch: plugin built for Babel 6 used with Babel 7.
- warningMissing peer dependency: plugin requires @babel/core or another plugin not installed.
- warningPlugin export shape: plugin exports an object instead of a function (common with CommonJS vs ESM).
- warningPlugin ordering: some plugins (e.g., decorators) must come before class properties.
- warningDuplicate plugin: same plugin listed multiple times, possibly from different configs.
- warningTypo in plugin name: e.g., '@babel/plugin-proposal-optional-chaining' vs '@babel/plugin-syntax-optional-chaining'.
Concrete fix directions. Pick the one that matches your root cause.
- buildAlign plugin versions with @babel/core: 'npm install @babel/plugin-xxx@latest' inside your project.
- buildCheck the plugin's README for required ordering and peer dependencies; add them to your config.
- buildIf plugin exports an object, wrap it: `['@babel/plugin-xxx', { /* options */ }]` and ensure it's a function.
- buildUse Babel's `overrides` to apply plugins conditionally based on file patterns.
- buildClear node_modules and reinstall: 'rm -rf node_modules && npm install' to fix hoisting issues.
- buildFor duplicate plugins, use Babel's `exclude` option or restructure config files to avoid merges.
A fix you cannot prove is a guess. Close the loop.
- verifiedRun `npx babel src/test.js --out-file /tmp/test-out.js` to see if the transform works in isolation.
- verifiedCheck the output file for expected syntax (e.g., optional chaining transformed if plugin is active).
- verifiedRun your build command (`npm run build`, `webpack`, etc.) and confirm zero errors.
- verifiedUse `babel --no-babelrc` to bypass config and confirm the issue is config-related.
- verifiedAdd a console.log inside the plugin's function (temporarily) to ensure it's being called.
- verifiedMonitor the 'BABEL_ENV' or 'NODE_ENV' environment variable to ensure the correct config is loaded.
Things that make this bug worse or harder to find.
- warningDon't blindly update all Babel packages to latest without checking semver compatibility.
- warningDon't ignore peer dependency warnings during npm install—they often cause runtime errors.
- warningDon't use both .babelrc and babel.config.js in the same project; prefer babel.config.js for monorepos.
- warningDon't rely on implicit plugin ordering—always check the documentation and order explicitly.
- warningDon't assume that a plugin works with the latest Babel core because it installed without errors.
- warningDon't paste random config snippets from Stack Overflow without understanding the plugin versions involved.
The Phantom Plugin That Broke Our CI
Timeline
- 09:15CI build fails with 'TypeError: (0 , _plugin.default) is not a function' after merging a PR that added decorator support.
- 09:20Check the error stack: points to @babel/plugin-proposal-decorators in node_modules.
- 09:25Run npm ls @babel/plugin-proposal-decorators: version 7.12.0 is installed, but @babel/core is 7.16.0. Suspect compatibility.
- 09:30Check plugin's package.json: it exports an object, not a function. That's the problem.
- 09:35Look at plugin source: it uses module.exports = { ... } instead of module.exports = function() { ... }.
- 09:40Upgrade plugin to 7.17.0 with npm install @babel/plugin-proposal-decorators@latest. Also upgrade class-properties to match.
- 09:45Re-run build: same error. Check peer deps: missing @babel/plugin-syntax-decorators. Install it.
- 09:50Build passes. Also realize the plugin ordering was wrong: decorators must come before class-properties.
- 09:55Update babel.config.js to put decorators first. Push fix. CI green.
Our CI had been green for weeks until a junior dev added decorator support to our React app. The build failed with a cryptic 'not a function' error. I checked the stack trace and saw it originated from @babel/plugin-proposal-decorators. My first instinct was a version mismatch—the plugin was 7.12, but @babel/core was 7.16. I upgraded the plugin to latest, but the error persisted.
Then I looked at the plugin's source in node_modules. It was exporting an object, not a function. Babel 7 requires plugins to be functions. The old plugin version used a different export format. Upgrading should have fixed it, but the version I installed still had the old format because the npm registry cached an older version. I cleared my package-lock and reinstalled, which got the correct version.
Even then, the build failed. I noticed we were missing @babel/plugin-syntax-decorators, a peer dependency. After installing that, the build passed. But I also corrected the plugin ordering: decorators must come before class properties. The final fix was a combination of upgrading, installing peer deps, and reordering. The lesson: always check peer dependencies and plugin export shapes before blaming config.
Root cause
The plugin @babel/plugin-proposal-decorators version 7.12.0 exported an object instead of a function, which is incompatible with Babel 7.16. Additionally, the peer dependency @babel/plugin-syntax-decorators was missing, and plugin ordering was incorrect.
The fix
Upgraded @babel/plugin-proposal-decorators to 7.17.0 (which exports a function), installed @babel/plugin-syntax-decorators, and reordered plugins in babel.config.js so decorators came before class-properties.
The lesson
When a Babel plugin error says 'not a function', suspect the plugin's export format first. Always install peer dependencies explicitly, and verify plugin ordering against the official documentation.
Babel expects every plugin to be a function that returns an object with a visitor, or an array of [plugin, options]. In Babel 7, the plugin function receives the Babel API as its first argument. If the plugin exports an object directly (e.g., module.exports = { visitor: ... }), Babel throws 'not a function' because it tries to call the object as a function.
This often happens when a plugin was written for Babel 6, which allowed object exports. To fix, you can either update the plugin to a version that supports Babel 7, or wrap it yourself: `module.exports = function() { return { visitor: ... }; }`. However, modifying node_modules is temporary; prefer upgrading the package.
Some Babel plugins depend on others to run first. The classic example is @babel/plugin-proposal-decorators requiring @babel/plugin-proposal-class-properties to come after it. If the order is reversed, you'll get errors like 'Decorators are not supported' or 'Cannot transform class property with decorator'.
Use the `--verbose` flag on babel to see the order in which plugins are applied: `npx babel --verbose src/test.js`. The output lists plugins in execution order. If you see a plugin that should come later appearing early, adjust your config.
The command `npm ls @babel/core` shows the resolved version and whether multiple versions are installed due to hoisting issues. If you see two different versions (e.g., 7.12.3 and 7.16.0), you have a version conflict. The plugin might be picking up the wrong core version.
Run `npm dedupe` to flatten the dependency tree, or use overrides in package.json to force a single version. Example: `"overrides": { "@babel/core": "7.16.0" }`. Then reinstall to ensure consistency.
Create a minimal babel.config.js that only includes the suspected plugin. For example: `module.exports = { plugins: ['@babel/plugin-proposal-decorators'] }`. Run babel on a simple file that uses decorators. If it works, the problem is interaction with other plugins or config files.
If the error persists, the plugin itself is likely broken. Test with an older version or a different plugin (e.g., @babel/plugin-syntax-decorators instead of -proposal). This narrows down whether the issue is with the plugin's implementation or its interaction.
Babel merges configs from multiple sources: babel.config.js, .babelrc, package.json, and programmatic options. If a plugin appears in two places, Babel may apply it twice, causing unexpected behavior or errors like 'Duplicate plugin'. Use the `--config-file` option to load only one config: `npx babel --config-file ./babel.config.js src/`.
To debug which config is adding a duplicate, add `console.log` in your babel.config.js to trace when it's loaded. Alternatively, use the `BABEL_SHOW_CONFIG_FOR` environment variable: `BABEL_SHOW_CONFIG_FOR=./src/test.js npx babel src/test.js` to see the final resolved config.
Frequently asked questions
What does 'TypeError: (0 , _plugin.default) is not a function' mean?
This error usually means the plugin module exports a default object instead of a function, or it's an ES module being imported as CommonJS. Check the plugin's main export in node_modules and ensure it's a function. Upgrade the plugin to a version compatible with your Babel version.
How do I find the correct plugin ordering?
Check the plugin's npm page or GitHub README for ordering requirements. For example, @babel/plugin-proposal-decorators must come before @babel/plugin-proposal-class-properties. You can also run babel with --verbose to see execution order.
Why does my plugin work locally but fail in CI?
CI often has a clean node_modules and may install different versions due to lockfile differences. Ensure your package-lock.json is committed and that CI uses `npm ci` (not `npm install`). Also check for environment variables like BABEL_ENV that might load different configs.
Can I use a Babel 6 plugin with Babel 7?
Not directly. Babel 7 changed the plugin API. You can try wrapping the plugin in a function, but it's safer to find a Babel 7 compatible version or a replacement. Many Babel 6 plugins have been rewritten for Babel 7 under the @babel scope.
What is the difference between @babel/plugin-syntax-xxx and @babel/plugin-proposal-xxx?
Plugin-syntax-xxx only enables parsing of a proposed syntax without transforming it. Plugin-proposal-xxx both parses and transforms. If you get an error about missing syntax, you might need the -syntax plugin as a peer dependency or to enable parsing. Usually, -proposal depends on -syntax.