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.
Getting ready
Before migrating to Babel 8, we recommend that you update your Babel 7 configuration to align it with Babel 8's new defaults. Once your build process is working again with Babel 7, you can then upgrade to Babel 8.
You should read this full document to understand what options you need to change and which plugins you need to remove, but here are the most important steps to modernize your config:
- If you are not using a
.browserslistrcfile, define a top-leveltargetsoption. Babel 7 defaults totargets: ">= 0%"(all browsers), while Babel 8 defaults totargets: "defaults". If you are using@babel/preset-env'stargetsoption, copy its value to the top-level configuration (next topresets). - If you use
@babel/preset-env, enable itsbugfixesoption. - If you use
@babel/preset-env'slooseorspecoptions, migrate toassumptions. - If you use
@babel/preset-reactor@babel/plugin-transform-react-jsx, explicitly set theirruntimeoption (Babel 7 defaults to"classic", Babel 8 to"automatic"). If you keep using the classic runtime, set theuseSpreadoption totrue. - If you use the TypeScript or Flow presets, replace the
isTSXandallExtensionsoptions withignoreExtensions. - If you are transforming TypeScript or Flow, set the
allowDeclareFieldsoption totrue(see TypeScript). - If you use
@babel/plugin-transform-runtime'scorejsor@babel/preset-env'suseBuiltInsoptions, remove them and usebabel-plugin-polyfill-corejs3instead. - If you use
@babel/plugin-proposal-decorators, set itsversionoption tolegacyor2023-11. - Remove the
@babel/plugin-proposal-record-and-tupleand@babel/plugin-syntax-import-assertionsplugins if you are using them.
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.0as 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-parserand@babel/eslint-pluginrequireeslint@^8.9.0as 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-explicit-resource-management | @babel/plugin-transform-explicit-resource-management |
@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 }]
+ "@babel/transform-runtime",
+ ["babel-plugin-polyfill-corejs3", { method: "usage-pure", version: "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
- Bun
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
bun x @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
defaultsas default compilation target (#12989, #15551).If you are already using the
targetsoption or have a.browserslistrcconfig file, this change won't affect you.Migration: You'll probably be fine with the new behavior since the browserslist's
defaultscovers most modern browsers. If you need to support legacy browsers, create a.browserslistrcconfig, or specify thetargetsoption.
-
The root AMD/UMD/SystemJS options, namely
moduleIds,getModuleId,moduleRoot,moduleIdandfilenameRelativeare 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-amdor@babel/plugin-transform-modules-umd. You can start the migration prior to Babel 8.0.If you are using
@babel/cliand passing--module-ids,--module-rootand--module-idfrom command line, please create a Babel configbabel.config.jsand 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: trueoption now behaves as same astargets.esmodules: "intersect"(#17188)Migration: In Babel 7, specifying
esmodules: truewill override thebrowserstarget 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
esmodulesoption as they all support ES modules.If your app targets legacy browsers such as IE, you can also remove the
esmodulesoption 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: truebehavior, remove theesmodulesoption and set the followingbrowserstarget:babel.config.json{
"targets": {
"browsers": "chrome 61, firefox 60, safari 10.1, node 13.2"
}
} -
Remove
uglifytarget (#12594)Migration: The
uglifytarget had been deprecated since 7.0.0. If you are using it to force@babel/preset-envto transpile down to ES5 and you still need to do so, you can use itsforceAllTransformsoption.
@babel/parser
-
Remove the
estreeparser plugin'sclassFeaturesoption (#13752)Migration: Remove the option from your config, since it's now enabled by default. Previously the
classFeaturesplugin enables@babel/parserto produce class properties AST compatible with ESLint 8, following the ESTree specification. In Babel 8 theeslint-parseronly works with ESLint 8 and above. -
Remove the
decimalparser 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
importAssertionsparser plugin (#16770)This plugin was for an old version of the import attributes proposal, using the
assertkeyword instead ofwith. The proposal moved ahead without theassertkeyword.Migration: If possible, replace
assertwithwithin your code. If doing so is not possible, you can temporarily use thedeprecatedImportAssertparser plugin. -
Remove the
importReflectionparser plugin (#16808)The "import reflection" proposal does not exist anymore, and it was superseeded by the "source phase imports" proposal, which uses the
sourcemodifier for imports instead ofmodule.Migration: Replace the plugin with
sourcePhaseImports, and migrate your code to usesourceinstead ofmodulein 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
jsonCompatibleStringsoption (#9943, #12477)Migration:
@babel/generatorallows to specify options for jsesc, a library used to escape printed values. If you are using thejsonCompatibleStringsoption, 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-8and your app will not be affected. The issue only affects if you are manually calling thebabel.transformAPI and your web server is not serving js files in theutf8encoding.Migration: Ensure your server is always serving js files in the
utf8encoding. If you can not control the server output, specify thecharsetattribute of thescripttag 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
looseandspecoptions have been removed (#16043)Migration: You can use the
assumptionstop-level option instead. See "Migrating from @babel/preset-env's "loose" and "spec" modes" for the ready-to-copy equivalent configuration.
-
includesandexcludesrespect renamed package names (#15576)Migration: If
includesorexcludescontain 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
bugfixesoption 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
includesandexcludes(#15810)Migration: You can safely remove them if you are using any of syntax plugins listed above in the
includesandexcludesoptions.
@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-reactor@babel/plugin-transform-react-jsxto be explicit about your usage ofcreateElement(or the equivalent function in other libraries). -
Make the
developmentoption default to the configured environment (#16927)Note that Babel's environment, set through
envNameoption, defaults toprocess.env.BABEL_ENV || process.env.NODE_ENV || "development": if you don't specify neither theenvNameoption nor theBABEL_ENVorNODE_ENVenvironment variables, it will default todevelopment.Migration: In production builds, set the
BABEL_ENVorNODE_ENVenvironment variables, or theenvNameBabel option, to"production". If you want to run only this preset in production mode, then you can explicitly set thedevelopmentoption tofalse.
-
Remove
useSpreadanduseBuiltInsoptions (#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.assigndown, 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: trueoption. -
Type check input options (#12460)
Migration: The preset will also report invalid option names. Refer to the docs and ensure valid usage.
-
Disallow
filteroption in automatic runtime (#15068)Migration: The
filteroption can only be used with theclassicruntime. If you have switched toautomaticruntime, 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
isTSXandallExtensionsoptions (#14955)Migration:
-
isTSX: trueandallExtensions: trueIf you are already using
@babel/preset-react,@babel/plugin-transform-react-jsxor any other third-party jsx presets such as@vue/babel-preset-jsx, and you want to transpile.tsxfiles, you can safely remove these two options. Babel 8 will automatically handle.tsxfiles 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: trueand enable the@babel/plugin-syntax-jsxplugin. -
isTSX: falseandallExtensions: trueUse
ignoreExtensions: true, see the example above. -
isTSX: falseandallExtensions: falseYou can safely remove them.
-
-
Remove
allowDeclareFieldsoption (#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,
runtimeis not a validpreset-typescriptoption 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
undefineduse thedeclaresyntax, introduced in TypeScript 3.7:input.tsclass A {
foo: string | void; // initialized to undefined
declare bar: number; // type-only, will be removed
}
-
Remove
allowDeclareFieldsoption (#12461)Migration: Remove the option from your config, since it's now enabled by default.
@babel/plugin-syntax-typescript
-
Remove
isTSXoption (#14955)Migration: If you are using
isTSX: true, remove this option and enable the@babel/plugin-syntax-jsxplugin:{
"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
allowDeclareFieldsandenumsoptions (#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
declaresyntax, 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
allowDeclareFieldsandenumsoptions (#12457, #16792)Migration: Remove these options from your config, since they are now enabled by default.
@babel/plugin-transform-runtime
-
The
corejsoption has been removed (#16311)Migration: To inject polyfills, you can use
babel-plugin-polyfill-corejs3or the deprecatedbabel-plugin-polyfill-corejs2directly.
-
The
useESModulesoption has been removed (#16141)Migration: Delete it from your configuration.
@babel/runtimewill now automatically expose ES modules when needed, usingpackage.json#exports. -
The
runtimeandhelpersoptions have been removed (#16311)Migration: Delete them from your configuration:
@babel/runtimewill now always import helpers. If you don't want to inject imports to helpers, remove@babel/plugin-transform-runtimefrom your config.
@babel/plugin-transform-modules-systemjs
-
Require
@babel/plugin-transform-dynamic-importwhen transformingimport()to SystemJS (#12700)Migration: Add
@babel/plugin-transform-dynamic-importto 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-commonjsandtransform-modules-amd) require the separate plugin since it was introduced. We couldn't change it fortransform-modules-systemjsbecause that package did already support dynamic import.
@babel/plugin-transform-regenerator
-
Do not replace global
regeneratorRuntimereferences (#17237)Babel 7 used to replace references to a
regeneratorRuntimeglobal with Babel'sregeneratorRuntimehelper: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
regeneratorRuntimeglobal. If that's not possible, you can either import the unmaintainedregenerator-runtimepackage in your application entrypoint, which will define theregeneratorRuntimeglobal, or usebabel-plugin-polyfill-regeneratorto automatically inject that import for you.
@babel/plugin-proposal-decorators
-
Only support the
legacyand2023-11versions of the proposal. In addition to that, the plugin now requires aversionoption (#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-09version. 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
legacyversion, it is advisable to migrate to the2023-11version regardless: both Babel 8 and TypeScript 5.0 support the2023-11version, while there are a few behaviour differences in thelegacyversion between Babel andtsc's implementation. Browsers will only implement the latest version of the proposal.
@babel/eslint-parser
-
Remove
allowImportExportEverywhereoption (#13921)Migration: Use
babelOptions.parserOpts.allowImportExportEverywhereinstead..eslintrc{
"parser": "@babel/eslint-parser",
"parserOptions": {
- "allowImportExportEverywhere": true,
+ "babelOptions": {
+ "parserOpts": {
+ "allowImportExportEverywhere": true
+ }
+ }
}
}
-
parserOpts.allowSuperOutsideMethoddefaults tofalse(#13921)Migration: If you want to restore to Babel 7 behaviour, set
babelOptions.parserOpts.allowSuperOutsideMethodtotrue. -
allowReturnOutsideFunctionis inferred fromecmaFeatures.globalReturn(#13921)Migration: If you want to enable
allowReturnOutsideFunction, setecmaFeatures.globalReturntotrue..eslintrc{
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaFeatures": {
"globalReturn": true
}
}
}
@babel/node
-
The
-gcand-dcommand-line flags have been removed (#15956) Migration: Use the--expose-gcand--inspectNode.js flags respectively. Note that although-dwas 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
@babel/standalone
-
Use browserslist's
defaultsas default compilation target (#12989, #15551).If you are already using the
data-targetsattribute, this change won't affect you.Migration: You'll probably be fine with the new behavior since the browserslist's
defaultscovers most modern browsers. If you need to support legacy browsers, specify thedata-targetsattribute.