babel.config.js and vue.config.js to ensure necessary ES6+ features are transpiled correctly for Internet Explorer 11 support.
Understanding the Breaking Changes
When you upgraded Vue CLI to version 5, the underlying Babel preset changed its default behavior significantly. Previously, @vue/cli-plugin-babel would automatically include all necessary polyfills for older browsers if you specified them in your package.json. In Vue CLI 5, this automatic inclusion was removed to reduce bundle size for modern browser users.
Similarly, while Core-js 3.8.3 is a robust library, it does not automatically inject polyfills into your application bundle unless explicitly requested by the Babel configuration. This means that even if you have imported Core-js, the specific methods used by Vue or your code (like Promise, Array.prototype.includes, or Object.assign) might not be present in the final output for IE11, leading to cryptic console errors such as "Object doesn't support property or method" or "Expected function".
This shift forces developers to take an active role in defining their browser targets. If you do not configure these settings, your build will assume a modern environment, stripping out any code deemed unnecessary for Chrome, Firefox, or Safari. For projects requiring legacy support, this manual intervention is mandatory.
Configuring Babel for Legacy Support
The primary step to restoring IE11 compatibility is updating your babel.config.js file. You need to tell Babel exactly which browsers you intend to support using the browserslist query. This ensures that the transpiler knows to convert modern JavaScript syntax into ES5-compatible code.
Create or modify your babel.config.js at the root of your project with the following content:
module.exports = {
presets: [
['@vue/cli-plugin-babel/preset', {
useBuiltIns: 'usage',
corejs: {
version: 3,
proposals: true
}
}]
]
};
The useBuiltIns: 'usage' option is critical here. It instructs Babel to analyze your source code and only include the specific polyfills that are actually used. This is more efficient than 'entry', which includes all polyfills regardless of usage, but it requires that you import Core-js in your main entry point. The corejs: { version: 3 } setting ensures compatibility with the Core-js 3.x series you installed.
Additionally, you must add the Core-js import to your main entry file, typically src/main.js or src/main.ts. Without this import, Babel cannot hook into the polyfill injection process:
// src/main.js
import 'core-js/stable';
import 'regenerator-runtime/runtime'; // Optional, for async/await support in older engines
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');
Note that regenerator-runtime is often needed for IE11 because it handles the transformation of async/await functions. While Core-js covers standard ECMAScript features, regenerator-runtime covers the syntactic transformations required for asynchronous execution patterns.
Updating Vue CLI Configuration
Beyond Babel, Vue CLI itself needs to know about your target browsers. This is handled via the vue.config.js file or directly in package.json under a browserslist key. It is best practice to define this in package.json so it can be shared across tools like PostCSS and Autoprefixer.
{
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"ie 11"
]
}
By including ie 11 in this list, you signal to the entire ecosystem that legacy support is required. This affects CSS vendor prefixes and JavaScript transpilation. If you have a separate vue.config.js, ensure it does not override these settings with a conflicting transpileDependencies or chainWebpack rule that excludes IE11-specific fixes.
If you are using Webpack directly through Vue CLI's chain API, you might need to ensure that cache-loader or thread-loader configurations do not interfere with the polyfill injection. However, in most standard setups, adjusting the Babel preset and the Browserslist definition is sufficient to resolve the majority of IE11 issues post-upgrade.
Verifying the Build Output
After making these changes, perform a clean build to ensure no cached artifacts from your previous modern-only build remain. Run npm run build or yarn build and inspect the generated files in the dist folder. Look for the presence of polyfilled code. You can also test the application locally using a tool like BrowserStack or by running IE11 in a virtual machine.
Check the browser console for any remaining errors. If you still see issues related to specific APIs, verify that you have imported the correct parts of Core-js. Sometimes, global scope pollution is required for certain libraries, in which case you may need to adjust the useBuiltIns strategy or manually import specific modules.
Frequently Asked Questions
Why did my app work before but break after upgrading Vue CLI?
Versions prior to Vue CLI 5 included polyfills automatically based on your package.json targets. Version 5 removed this default behavior to optimize bundle sizes for modern browsers, requiring explicit configuration of Babel and Core-js to restore legacy support.
Do I need to install regenerator-runtime separately?
Yes, if your application uses async/await or generators, you should install regenerator-runtime and import it in your main entry file. Core-js does not cover the runtime requirements for these specific ES2017+ features in IE11.
Can I use useBuiltIns: entry instead of usage?
You can, but it is less efficient. entry includes all possible polyfills, increasing your bundle size significantly. usage only includes what your code actually calls, resulting in a smaller download for users, which is preferable even for IE11 where bandwidth might be limited.
How do I check if my Browserslist is being read?
You can run npx browserslist in your terminal. It will print the list of browsers matched by your configuration. If ie 11 appears in the output, your configuration is being recognized by the build tools.
What if I still get errors after adding polyfills?
Ensure that you are importing core-js/stable before any other imports in your main.js file. Additionally, check if any third-party dependencies are not being transpiled; you may need to add them to transpileDependencies in vue.config.js.