Skip to main content

Why More Libraries Are Dropping IE/Legacy Browser Support in 2026

·PkgPulse Team

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.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.