Upgrade to Babel 8
This document lists the breaking changes introduced in Babel 8.0.0, and how to adapt your usage of Babel to them when upgrading from Babel 7.0.0.
If you are working directly with Babel's API (because, for example, you maintain a custom Babel plugin), please also check the migration guide for integration.
If you are upgrading from Babel 6, please check the Babel 7 migration guide first.
All of Babel
Node.js support
All Babel 8 packages require Node.js ^20.19.0 || >=22.12.0
.
We highly encourage you to use a newer version of Node.js (LTS v22) since the previous versions are not maintained. See nodejs/Release for more information.
This just means Babel itself won't run on older versions of Node. It can still output code that runs on old Node versions.
ESM only
Babel is now shipped as native ECMAScript modules (#11701, #17265).
Peer dependency requirements
- All presets and plugins require
@babel/core@^8.0.0
as peer dependency. Some Babel 7 plugins and presets might work with@babel/core@8
, and some Babel 8 plugins and presets might work with@babel/core@7
, but we do not provide any official support for that. @babel/eslint-parser
and@babel/eslint-plugin
requireeslint@^8.9.0
as peer dependency. (#15563)
Renamed Packages
The following packages has been renamed from ...-proposal-...
to ...-transform-...
, as they have reached Stage 4 (#15614). The rename process has been landed in Babel 7.22 so you can start the migration prior to the upgrade.
Babel 7 | Babel 8 |
---|---|
@babel/plugin-proposal-async-generator-functions | @babel/plugin-transform-async-generator-functions |
@babel/plugin-proposal-class-properties | @babel/plugin-transform-class-properties |
@babel/plugin-proposal-class-static-block | @babel/plugin-transform-class-static-block |
@babel/plugin-proposal-duplicate-named-capturing-groups-regex | @babel/plugin-transform-duplicate-named-capturing-groups-regex |
@babel/plugin-proposal-dynamic-import | @babel/plugin-transform-dynamic-import |
@babel/plugin-proposal-export-namespace-from | @babel/plugin-transform-export-namespace-from |
@babel/plugin-proposal-json-strings | @babel/plugin-transform-json-strings |
@babel/plugin-proposal-logical-assignment-operators | @babel/plugin-transform-logical-assignment-operators |
@babel/plugin-proposal-nullish-coalescing-operator | @babel/plugin-transform-nullish-coalescing-operator |
@babel/plugin-proposal-numeric-separator | @babel/plugin-transform-numeric-separator |
@babel/plugin-proposal-object-rest-spread | @babel/plugin-transform-object-rest-spread |
@babel/plugin-proposal-optional-catch-binding | @babel/plugin-transform-optional-catch-binding |
@babel/plugin-proposal-optional-chaining | @babel/plugin-transform-optional-chaining |
@babel/plugin-proposal-private-methods | @babel/plugin-transform-private-methods |
@babel/plugin-proposal-private-property-in-object | @babel/plugin-transform-private-property-in-object |
@babel/plugin-proposal-unicode-property-regex | @babel/plugin-transform-unicode-property-regex |
Discontinued Packages
@babel/runtime-corejs2
core-js@2
has not been maintained for years, and you should switch to core-js@3
instead.
Please upgrade to @babel/runtime-corejs3
(#11751). After
you install the new runtime, please set the corejs
version to the core-js
version that you are using.
{
"plugins": ["@babel/transform-runtime", {
- corejs: 2
+ corejs: "3.42.0"
}]
}
@babel/plugin-syntax-import-assertions
The proposal evolved into import attributes, which now Babel supports parsing by default. You can remove @babel/plugin-syntax-import-assertions
from your config, and replace the following patterns in your codebase:
- import value from "module" assert { type: "json" };
+ import value from "module" with { type: "json" };
@babel/plugin-proposal-record-and-tuple
The Records and Tuples proposal has been withdrawn in TC39, which means that the syntax is not on track anymore to be included in the language.
You will need to migrate away from the Records and Tuples syntax, and you can directly use the @bloomberg/record-tuple-polyfill
polyfill as if it was a standalone library.
+ import { Record, Tuple } from "@bloomberg/record-tuple-polyfill"
// syntaxType: "hash"
- #{ p: "value" }
+ Record({ p: "value" })
- #[0, 1, 2]
+ Tuple(0, 1, 2)
// syntaxType: "bar"
- {| p: "value" |}
+ Record({ p: "value" })
- [|0, 1, 2|]
+ Tuple(0, 1, 2)
If you have to do a large scale migration. You can run Babel 7 with only the @babel/plugin-proposal-record-and-tuple
plugin to transform the code base:
{
"babelrc": false,
"configFile": false,
"generateOpts": {
"experimental_preserveFormat": true,
"retainLines": true,
"tokens": true
},
"parserOpts": {
"createParenthesizedExpressions": true
},
"plugins": [
"@babel/plugin-syntax-jsx",
"@babel/plugin-syntax-typescript",
// or syntaxType: "bar" if you are using `{||}` or `[||]`
["@babel/plugin-proposal-record-and-tuple", {
"syntaxType": "hash", "importPolyfill": true
}]
]
}
And then run @babel/cli@7
to transform the input source src
, this command will output the transformed source to src-mod
:
- npm
- Yarn
- pnpm
npx @babel/cli@7 --config-file ./babel.record-tuple-migration.config.json src --out-lib src-mod
yarn dlx @babel/cli@7 --config-file ./babel.record-tuple-migration.config.json src --out-lib src-mod
pnpm dlx @babel/cli@7 --config-file ./babel.record-tuple-migration.config.json src --out-lib src-mod
Please manually check whether src-mod
is correct. If everything looks good, overwrite src
with contents in src-mod
.
Syntax plugins
The following syntax plugins are no longer needed, you can safely remove them from your configuration and dependencies:
@babel/plugin-syntax-async-functions
@babel/plugin-syntax-async-generators
@babel/plugin-syntax-bigint
@babel/plugin-syntax-class-properties
@babel/plugin-syntax-class-static-block
@babel/plugin-syntax-dynamic-import
@babel/plugin-syntax-exponentiation-operator
@babel/plugin-syntax-export-extensions
@babel/plugin-syntax-export-namespace-from
@babel/plugin-syntax-import-meta
@babel/plugin-syntax-json-strings
@babel/plugin-syntax-logical-assignment-operators
@babel/plugin-syntax-module-string-names
@babel/plugin-syntax-nullish-coalescing-operator
@babel/plugin-syntax-numeric-separator
@babel/plugin-syntax-object-rest-spread
@babel/plugin-syntax-optional-catch-binding
@babel/plugin-syntax-optional-chaining
@babel/plugin-syntax-private-property-in-object
@babel/plugin-syntax-top-level-await
@babel/plugin-syntax-trailing-function-commas
@babel/plugin-syntax-unicode-sets-regex
Per-package breaking changes
@babel/core
-
Use browserslist's
defaults
as default compilation target (#12989, #15551).If you are already using the
targets
option or have a.browserslistrc
config file, this change won't affect you.Migration: You'll probably be fine with the new behavior since the browserslist's
defaults
covers most modern browsers. If you need to support legacy browsers, create a.browserslistrc
config, or specify thetargets
option.
-
The root AMD/UMD/SystemJS options, namely
moduleIds
,getModuleId
,moduleRoot
,moduleId
andfilenameRelative
are moved to plugin options (#5473, #12724).Migration: Move these options to the plugin you are using to transform modules. For example, if you are using
@babel/plugin-transform-modules-systemjs
:babel.config.jsmodule.exports = {
plugins: [
['@babel/plugin-transform-modules-systemjs', {
moduleIds: true,
moduleRoot: 'myApp',
getModuleId (name) {
return name + "suffix";
},
}],
],
};Adapt the example above if you are using
@babel/plugin-transform-modules-amd
or@babel/plugin-transform-modules-umd
. You can start the migration prior to Babel 8.0.If you are using
@babel/cli
and passing--module-ids
,--module-root
and--module-id
from command line, please create a Babel configbabel.config.js
and specify options there.If you are transforming ESM through
@babel/preset-env
, you will need to explicitly use one of the@babel/plugin-transform-modules-...
plugins. -
targets.esmodules: true
option now behaves as same astargets.esmodules: "intersect"
(#17188)Migration: In Babel 7, specifying
esmodules: true
will override thebrowsers
target orbrowserslist
's targets, while specifying"intersect"
will intersect with such targets.If your app targets modern browsers released after 2019, you can safely remove the
esmodules
option as they all support ES modules.If your app targets legacy browsers such as IE, you can also remove the
esmodules
option as IE requires more transforms than any other browser.If your app targets browsers released before 2019 and you want to preserve the Babel 7
esmodules: true
behavior, remove theesmodules
option and set the followingbrowsers
target:babel.config.json{
"targets": {
"browsers": "chrome 61, firefox 60, safari 10.1, node 13.2"
}
} -
Remove
uglify
target (#12594)Migration: The
uglify
target had been deprecated since 7.0.0. If you are using it to force@babel/preset-env
to transpile down to ES5 and you still need to do so, you can use itsforceAllTransforms
option.
@babel/parser
-
Remove the
estree
parser plugin'sclassFeatures
option (#13752)Migration: Remove the option from your config, since it's now enabled by default. Previously the
classFeatures
plugin enables@babel/parser
to produce class properties AST compatible with ESLint 8, following the ESTree specification. In Babel 8 theeslint-parser
only works with ESLint 8 and above. -
Remove the
decimal
parser plugin (#16741)Migration: Migrate your project to the latest proposal and remove the plugin from your config since the proposal doesn't include syntax anymore.
example.js- 1.03m
+ new Decimal("1.03")
- decimal1 + decimal2
+ decimal1.add(decimal2) -
Remove the
importAssertions
parser plugin (#16770)This plugin was for an old version of the import attributes proposal, using the
assert
keyword instead ofwith
. The proposal moved ahead without theassert
keyword.Migration: If possible, replace
assert
withwith
in your code. If doing so is not possible, you can temporarily use thedeprecatedImportAssert
parser plugin. -
Remove the
importReflection
parser plugin (#16808)The "import reflection" proposal does not exist anymore, and it was superseeded by the "source phase imports" proposal, which uses the
source
modifier for imports instead ofmodule
.Migration: Replace the plugin with
sourcePhaseImports
, and migrate your code to usesource
instead ofmodule
in import declarations.example.js- import module x from "foo";
+ import source x from "foo";
-
Disallow sequence expressions inside JSX attributes (#12447)
Migration: Find and replace the following code patterns. You can start the migration prior to Babel 8:
input.jsx- <p key={foo, bar}></p> // Invalid
+ <p key={(foo, bar)}></p> // Valid -
Disallow
{
,}
,<
and>
in JSX text (#12451)Migration: Use
{'{'}
,{'}'}
,{'<'}
and{'>'}
instead. Find and replace the following code patterns. You can start the migration prior to Babel 8:input.jsx- <p>">" is greater than.</p>
+ <p>"{'>'}" is greater than.</p>Notes: This is technically a spec compliance fix because the JSX specification already forbids them. However, we have chosen to postpone it until Babel 8 because it could break someone's code.
@babel/generator
-
Remove the
jsonCompatibleStrings
option (#9943, #12477)Migration:
@babel/generator
allows to specify options for jsesc, a library used to escape printed values. If you are using thejsonCompatibleStrings
option, you can replace it withjsescOption: { json: true }
.
-
Output non-ASCII characters as-is in string literals (#12675)
If you are using any one of
@babel/cli
, webpack, Rollup, or other Node.js powered bundlers, the transformed code is always encoded withutf-8
and your app will not be affected. The issue only affects if you are manually calling thebabel.transform
API and your web server is not serving js files in theutf8
encoding.Migration: Ensure your server is always serving js files in the
utf8
encoding. If you can not control the server output, specify thecharset
attribute of thescript
tag in the html files.<script charset="utf-8" src="your-app.js"></script>
You can also restore to the Babel 7 behaviour by setting
babel.config.js{
generatorOpts: {
jsescOption: {
minimal: false
}
}
}
@babel/preset-env
@babel/preset-env
's targets
option inherits its behavior from the top-level targets
option, so make sure to check the @babel/core section.
-
The
loose
andspec
options have been removed (#16043)Migration: You can use the
assumptions
top-level option instead. See "Migrating from @babel/preset-env's "loose" and "spec" modes" for the ready-to-copy equivalent configuration.
-
includes
andexcludes
respect renamed package names (#15576)Migration: If
includes
orexcludes
contain any plugins mentioned in the Packages Renames section, change it to the new name. For example,babel.config.json{
"presets": [[
"@babel/preset-env",
{
- "includes": ["proposal-optional-chaining"]
+ "includes": ["transform-optional-chaining"]
}
]]
}
-
Enable the
bugfixes
option by default (#13866)Migration: You will probably be fine with the new behaviour as Babel now tries to compile the broken syntax to the closest non-broken modern syntax supported by your target browsers. If anyhow you want to restore the Babel 7 behaviour, you can specify
bugfixes: false
. -
Removed syntax plugins can not be used in
includes
andexcludes
(#15810)Migration: You can safely remove them if you are using any of syntax plugins listed above in the
includes
andexcludes
options.
@babel/preset-react
and @babel/plugin-transform-react-jsx
@babel/parser
also includes some JSX-related breaking changes, so make sure to check the @babel/parser section.
-
Use the new JSX implementation by default (#12630)
Migration: If you are using a modern version of React or Preact, it should work without any configuration changes. Otherwise, you can pass the
runtime: "classic"
option to@babel/preset-react
or@babel/plugin-transform-react-jsx
to be explicit about your usage ofcreateElement
(or the equivalent function in other libraries). -
Make the
development
option default to the configured environment (#16927)Note that Babel's environment, set through
envName
option, defaults toprocess.env.BABEL_ENV || process.env.NODE_ENV || "development"
: if you don't specify neither theenvName
option nor theBABEL_ENV
orNODE_ENV
environment variables, it will default todevelopment
.Migration: In production builds, set the
BABEL_ENV
orNODE_ENV
environment variables, or theenvName
Babel option, to"production"
. If you want to run only this preset in production mode, then you can explicitly set thedevelopment
option tofalse
.
-
Remove
useSpread
anduseBuiltIns
options (#12593)Migration: Babel 8 always compiles JSX spread elements to object spread:
input.jsx<div {...props}></div>
// transforms to
jsx("div", { ...props })If your app targets to modern browsers released after 2019, you can safely remove these options as object spread has less code footprint.
If your code needs to run in an environment which doesn't support object spread, you can either use
@babel/preset-env
(recommended) or@babel/plugin-transform-object-rest-spread
. If you want to transpileObject.assign
down, you also need to enable@babel/plugin-transform-object-assign
.In Babel 7.7.0, you can opt into the Babel 8 behavior by using the
useSpread: true
option. -
Type check input options (#12460)
Migration: The preset will also report invalid option names. Refer to the docs and ensure valid usage.
-
Disallow
filter
option in automatic runtime (#15068)Migration: The
filter
option can only be used with theclassic
runtime. If you have switched toautomatic
runtime, you can safely remove this option. Otherwise please specifyruntime: "classic"
.
@babel/preset-typescript
Make sure to also check the @babel/plugin-transform-typescript changes changes, since it's what this preset uses under the hood.
-
Remove
isTSX
andallExtensions
options (#14955)Migration:
-
isTSX: true
andallExtensions: true
If you are already using
@babel/preset-react
,@babel/plugin-transform-react-jsx
or any other third-party jsx presets such as@vue/babel-preset-jsx
, and you want to transpile.tsx
files, you can safely remove these two options. Babel 8 will automatically handle.tsx
files using this preset and the other JSX plugin.babel.config.json{
"presets": [
["@babel/preset-react", { "runtime": "automatic" }],
- ["@babel/preset-typescript", { "allExtensions": true, "isTSX": true }]
+ ["@babel/preset-typescript"]
]
}If you want to transpile files other than
.tsx
, such as.vue
, useignoreExtensions: true
:babel.config.js{
overrides: [{
include: /\.vue$/,
presets: [
['@babel/preset-typescript', {
- allExtensions: true, isTSX: true
+ ignoreExtensions: true
}]
]
}]
}If you want to preserve the JSX format but transpile the TypeScript part, use
ignoreExtensions: true
and enable the@babel/plugin-syntax-jsx
plugin. -
isTSX: false
andallExtensions: true
Use
ignoreExtensions: true
, see the example above. -
isTSX: false
andallExtensions: false
You can safely remove them.
-
-
Remove
allowDeclareFields
option (#12461)Migration: Remove the option from your config, since it's now enabled by default.
-
Type check input options (#12460)
Migration: The preset will also report invalid option names. Refer to the docs and ensure valid usage. For example,
runtime
is not a validpreset-typescript
option and thus should be removed.
@babel/plugin-transform-typescript
-
Preserve uninitialized class fields (#12461)
Migration: If you don't want fields to be initialized to
undefined
use thedeclare
syntax, introduced in TypeScript 3.7:input.tsclass A {
foo: string | void; // initialized to undefined
declare bar: number; // type-only, will be removed
}
-
Remove
allowDeclareFields
option (#12461)Migration: Remove the option from your config, since it's now enabled by default.
@babel/plugin-syntax-typescript
-
Remove
isTSX
option (#14955)Migration: If you are using
isTSX: true
, remove this option and enable the@babel/plugin-syntax-jsx
plugin:{
"plugins": [
- ["@babel/plugin-syntax-typescript", { "isTSX": true }]
+ "@babel/plugin-syntax-typescript",
+ "@babel/plugin-syntax-jsx"
]
}If you are using
isTSX: false
, you can safely remove it.
@babel/preset-flow
Make sure to also check the @babel/plugin-transform-flow-strip-types changes, since it's what this preset uses under the hood.
-
Type check input options (#12460)
Migration: The preset will also report invalid option names. Refer to the docs and ensure valid usage.
-
Remove the
allowDeclareFields
andenums
options (#12457, #16792)Migration: Remove these options from your config, since they are now enabled by default.
@babel/plugin-transform-flow-strip-types
-
Preserve uninitialized class fields (#12457)
Migration: Use the new
declare
syntax, introduced in Flow 0.120, if you don't want fields to be initialized toundefined
:input.jsclass A {
foo: string | void; // initialized to undefined
declare bar: number; // type-only, will be removed
}
-
Remove the
allowDeclareFields
andenums
options (#12457, #16792)Migration: Remove these options from your config, since they are now enabled by default.
@babel/plugin-transform-runtime
-
The
corejs
option has been removed (#16311)Migration: To inject polyfills, you can use
babel-plugin-polyfill-corejs3
or the deprecatedbabel-plugin-polyfill-corejs2
directly.
-
The
useESModules
option has been removed (#16141)Migration: Delete it from your configuration.
@babel/runtime
will now automatically expose ES modules when needed, usingpackage.json#exports
. -
The
runtime
andhelpers
options have been removed (#16311)Migration: Delete them from your configuration:
@babel/runtime
will now always import helpers. If you don't want to inject imports to helpers, remove@babel/plugin-transform-runtime
from your config.
@babel/plugin-transform-modules-systemjs
-
Require
@babel/plugin-transform-dynamic-import
when transformingimport()
to SystemJS (#12700)Migration: Add
@babel/plugin-transform-dynamic-import
to your config: you can already do it in Babel 7. If you are using@babel/preset-env
, you don't need to do anything.babel.config.js.diff{
"plugins": [
+ "@babel/plugin-transform-dynamic-import",
"@babel/plugin-transform-modules-systemjs",
]
}Notes: All the other plugins which support dynamic import (
transform-modules-commonjs
andtransform-modules-amd
) require the separate plugin since it was introduced. We couldn't change it fortransform-modules-systemjs
because that package did already support dynamic import.
@babel/plugin-transform-regenerator
-
Do not replace global
regeneratorRuntime
references (#17237)Babel 7 used to replace references to a
regeneratorRuntime
global with Babel'sregeneratorRuntime
helper:input.jsconsole.log(regeneratorRuntime.isGeneratorFunction(fn));
output.jsimport _regeneratorRuntime from "@babel/runtime/regenerator";
console.log(_regeneratorRuntime.isGeneratorFunction(fn));This doesn't happen anymore in Babel 8.
Migration: The recommended approach is to update your code to not rely on a non-existent
regeneratorRuntime
global. If that's not possible, you can either import the unmaintainedregenerator-runtime
package in your application entrypoint, which will define theregeneratorRuntime
global, or usebabel-plugin-polyfill-regenerator
to automatically inject that import for you.
@babel/plugin-proposal-decorators
-
Only support the
legacy
and2023-11
versions of the proposal. In addition to that, the plugin now requires aversion
option (#12712, #15676)Migration: You should migrate to the latest version of the proposal,
"2023-11"
.babel.config.json{
"plugins": [
["@babel/plugin-proposal-decorators", {
- "decoratorsBeforeExport": true,
- "version": "2018-09",
+ "version": "2023-11"
}]
]
}The syntax is the same, but you will need to rewrite your decorator functions. The spec repo provides comparison between the latest version and the
2018-09
version. You can already migrate since Babel 7.22.0, using the"version": "2023-11"
option of@babel/plugin-proposal-decorators
.Although Babel 8 still supports the
legacy
version, it is advisable to migrate to the2023-11
version regardless: both Babel 8 and TypeScript 5.0 support the2023-11
version, while there are a few behaviour differences in thelegacy
version between Babel andtsc
's implementation. Browsers will only implement the latest version of the proposal.
@babel/eslint-parser
-
Remove
allowImportExportEverywhere
option (#13921)Migration: Use
babelOptions.parserOpts.allowImportExportEverywhere
instead..eslintrc{
"parser": "@babel/eslint-parser",
"parserOptions": {
- "allowImportExportEverywhere": true,
+ "babelOptions": {
+ "parserOpts": {
+ "allowImportExportEverywhere": true
+ }
+ }
}
}
-
parserOpts.allowSuperOutsideMethod
defaults tofalse
(#13921)Migration: If you want to restore to Babel 7 behaviour, set
babelOptions.parserOpts.allowSuperOutsideMethod
totrue
. -
allowReturnOutsideFunction
is inferred fromecmaFeatures.globalReturn
(#13921)Migration: If you want to enable
allowReturnOutsideFunction
, setecmaFeatures.globalReturn
totrue
..eslintrc{
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaFeatures": {
"globalReturn": true
}
}
}
@babel/node
-
The
-gc
and-d
command-line flags have been removed (#15956) Migration: Use the--expose-gc
and--inspect
Node.js flags respectively. Note that although-d
was short for--debug
, the latter has been deprecated since Node.js 7.7.0. -
Command-line flags for Node.js and Babel must now be passed before the filename, while flags for the script itself must be passed after. (#16706):
babel-node --flag-for-node --flag-for-babel script.js --flag-for-script