Why More Libraries Are Dropping IE/Legacy Browser Support in 2026
TL;DR
IE11 is below 0.5% global usage. Most packages dropped it. The real question is Safari/iOS legacy. Internet Explorer 11 was officially retired by Microsoft in June 2022. By 2026, it's irrelevant for almost all developers. The more nuanced discussion: older iOS versions (iOS 14/15) and Android WebView are now the actual legacy browser constraint. Packages dropping ES5 transpilation and adopting modern APIs (ES2020+, top-level await, dynamic import) is unambiguously good for performance and developer experience.
Key Takeaways
- IE11 < 0.3% global usage — irrelevant for most web apps in 2026
- iOS Safari 14/15 — ~3% usage; the real constraint for modern JavaScript features
- ES2022 target — the new baseline for most npm packages
browserslist— the standard for declaring browser support in your project- Dropping legacy = smaller bundles — fewer polyfills = less JavaScript shipped
The Legacy Browser Timeline
Browser End-of-Life (EOL) dates:
IE11: June 2022 (Microsoft official EOL)
Chrome 90: Auto-updated; "legacy" doesn't apply
Firefox ESR: ~1 year release cycle
iOS Safari: ~2 years of support per iOS version
Global usage shares (March 2026):
IE11: ~0.25% (mostly government/enterprise locked systems)
iOS Safari 14: ~1.2% (iPhone 6s users, released 2015)
iOS Safari 15: ~2.8% (still receiving security updates)
Chrome 100+: ~65%
Safari 16+: ~18%
Firefox 120+: ~4%
What "Dropping Legacy Support" Actually Means
Modern JavaScript Features Major Packages Now Require
// Features enabled by dropping IE11/ES5 target:
// 1. Native ESM (import/export)
import { useState } from 'react'; // No CommonJS wrapper needed
// 2. Optional chaining and nullish coalescing (ES2020)
const name = user?.profile?.name ?? 'Anonymous';
// Instead of: (user && user.profile && user.profile.name) || 'Anonymous'
// 3. Logical assignment operators (ES2021)
settings ||= { theme: 'dark' };
// Instead of: settings = settings || { theme: 'dark' }
// 4. Top-level await (ES2022)
const data = await fetch('/api').then(r => r.json());
// Instead of: IIFE or module-level init function
// 5. Class fields (ES2022)
class Store {
#state = {}; // Private field (# prefix)
count = 0; // Public field
}
// 6. structuredClone (2021)
const copy = structuredClone(deepObject);
// Instead of: JSON.parse(JSON.stringify(deepObject)) or lodash.cloneDeep
// 7. Object.hasOwn (2022)
if (Object.hasOwn(obj, 'key')) { ... }
// Instead of: Object.prototype.hasOwnProperty.call(obj, 'key')
Bundle Size Impact
Polyfill savings when dropping IE11 support:
@babel/polyfill (IE11 compat): ~90KB gzipped
core-js/stable (IE11 compat): ~55KB gzipped
No polyfill (modern browsers): 0KB
A 500KB bundle with IE11 compat → ~420KB without
That's a 16% reduction from polyfills alone.
Plus: modern syntax is more compressible than ES5 transpiled code
ES2022 arrow functions, destructuring → smaller gzip output than var/function
Key Packages That Dropped Legacy Support
# React 19 (Dec 2024)
# Dropped: IE11, legacy React APIs
# Requires: modern browser with Promises, fetch, ResizeObserver
# Vite 5+ (2023)
# Requires: Node.js 18+, modern browsers
# No longer ships polyfills by default
# build.target defaults to 'es2015' (can set to 'es2022' for smaller output)
# Next.js 15 (2025)
# Drops Node.js 16 (requires 18.17+)
# Client target: modern browsers (ES2020+)
# TanStack Query v5 (2023)
# Dropped: IE11, older React versions
# Requires: React 18+
# Prettier 3 (2023)
# Node.js 14+ required (dropped Node.js 12)
# Output uses more modern syntax
# Storybook 8 (2024)
# Dropped: IE11, legacy Webpack configs
# Vitest 2 (2024)
# Node.js 18+ required
# Native ESM only
Configuring Browser Support
browserslist (The Standard)
# .browserslistrc — tells bundlers/Babel what to target
# Modern: no IE, no legacy Android
> 1% in US # >1% usage in US
last 2 versions # Last 2 versions of each browser
not dead # Exclude browsers with 0% usage
not IE 11 # Explicit IE11 exclusion
not iOS < 15 # Drop iOS 14 and below
// package.json — browserslist field
{
"browserslist": {
"production": [
">0.5% in US",
"last 2 Chrome versions",
"last 2 Firefox versions",
"last 2 Safari versions",
"not IE 11"
],
"development": [
"last 1 Chrome version",
"last 1 Firefox version"
]
}
}
// vite.config.ts — set build target
export default defineConfig({
build: {
target: 'es2022', // Modern — no IE transpilation
// target: 'esnext', // Bleeding edge
// target: ['chrome90', 'firefox90', 'safari14'] // Explicit versions
},
});
Handling the iOS Safari Gap
iOS Safari 14/15 still miss some ES2023 features:
// Check caniuse.com before using new syntax in iOS-heavy apps
// iOS 14 missing:
// - Error.cause (use message wrapping instead)
// - Array.prototype.findLast (polyfill or use array.length-1)
// - Object.fromEntries needs polyfill on iOS 12
// iOS 15 missing:
// - structuredClone (use polyfill or JSON.parse/stringify)
// - Import assertions
// iOS 16+ (85%+ of active iOS users in 2026):
// - All ES2022 features supported
// - No polyfills needed
// Strategy: target ios >= 15.0 in browserslist
// Only ~1% of iOS users would be excluded
What to Do If You Support Legacy Browsers
Some enterprises and government apps must support IE11 or older Android:
# Option 1: Use older package versions (CRA approach)
# Pin to versions that still support legacy:
# react@17 (last IE-supporting major)
# Create React App v4 (last Webpack 4 version with legacy targets)
# Option 2: Transpile at build time
# @vitejs/plugin-legacy — adds legacy support to Vite output
npm install -D @vitejs/plugin-legacy
# vite.config.ts
import legacy from '@vitejs/plugin-legacy';
export default defineConfig({
plugins: [
legacy({
targets: ['ie 11', 'since 2019', 'edge >= 18'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
}),
],
});
# Result: two bundles — modern (fast) + legacy (IE compat)
# Browser gets the right bundle via module/nomodule
# Option 3: Use a CDN that polyfills
# polyfill.io — serves only needed polyfills based on browser UA
<script src="https://polyfill.io/v3/polyfill.min.js"></script>
The Real Legacy Browser Problem: Android WebView
Android WebView statistics (2026):
Android 8 (2017): ~3% of Android — WebView based on Chrome 58
Android 9 (2018): ~5% of Android — WebView based on Chrome 68
Android 10+: ~85% — modern WebView, auto-updates
Note: Android WebView on older Android versions doesn't auto-update.
If you build mobile web apps (React Native WebView, PWAs, Capacitor):
- Test on Android 9 (Chrome 68 equivalent)
- Missing: CSS Grid (partial), async/await transpilation may be needed
Practical Decision Guide
Does your app serve government/healthcare/enterprise IE11 users?
├── Yes → Use @vitejs/plugin-legacy or pin to older package versions
└── No → Set target to es2022, drop polyfills
Does your app serve users in India, Southeast Asia, Africa?
├── Android WebView matters → test on Android 9+ devices
└── Mostly tech-savvy users → modern targets fine
Does your npm library need to support legacy consumers?
├── Yes → Ship CJS + ESM, target ES2015 minimum in output
└── No (internal tool) → ES2022 target, ESM only
Compare package browser compatibility on PkgPulse — build targets and browser support data included.
See the live comparison
View vite vs. webpack on PkgPulse →