Load only the CoreJS optional-chaining polyfill you need
Use selective core-js imports to add optional chaining to older browsers without shipping the full standard library.
15 Nov 2025, 05:43 UTC

You ship a modern JavaScript application, and a teammate uses optional chaining (user?.address?.zip) to avoid nested null checks. The code runs fine in Chrome and Firefox, but a support ticket arrives: the page crashes on older Android WebViews and some corporate desktops. The error is usually a syntax error, not a missing property. The parser never understood ?. in the first place.
You do not need the full CoreJS library to fix this. A selective import such as import 'core-js/es2020/optional-chaining'; adds the runtime helper for optional chaining while a bundler can leave out the rest of the standard library. The catch is that recent browsers still download and execute the polyfill unless you gate it or rely on native support.
Optional chaining and the older runtime problem
Optional chaining lets you read deeply nested properties without throwing when an intermediate value is null or undefined. For example, user?.address?.zip returns undefined instead of crashing if user or user.address is missing.
Environments that predate the syntax—Node.js before version 14, older browsers, and some embedded WebViews—parse ?. as invalid JavaScript. A polyfill supplies the missing runtime behavior, but it cannot fix a syntax error by itself. You still need your bundler or compiler to transform the syntax, or you need to serve the polyfill only to targets that can parse it but lack the built-in behavior.
Here “polyfill” means code that adds a feature to older environments that do not support it natively. “Tree-shaking” means a bundler removes code it can prove is unused.
Install and import only what you need
Run this in your project root:
npm install core-js@3 --save-dev
Required permissions: write access to package.json and the ability to create or modify node_modules. Risk: this changes your lockfile and may conflict if another dependency already pins a different major version of CoreJS.
Then add the selective import to your application entry point or a dedicated polyfill bundle:
import 'core-js/es2020/optional-chaining';
// Application code
const zip = user?.address?.zip;
This imports only the optional-chaining helper rather than import 'core-js', which would pull in the entire standard library. After building, inspect your output bundle. You should see the optional-chaining module present and the rest of CoreJS absent. You can verify this with a bundle analyzer such as source-map-explorer, but do not assume the polyfill is unused just because your modern browser handles ?. natively.
CoreJS import versus Babel transform-runtime
| Approach | Bundle impact | Best for | Caveat |
|---|---|---|---|
| CoreJS selective import | Small runtime helper; rest of library excluded | Projects already using CoreJS or needing runtime patching | Recent browsers still download it unless you split bundles |
| Babel optional-chaining transform + transform-runtime | Helper code inlined or shared per bundle | Broad ES5 browser support without runtime feature detection | Adds helper output even when native support exists |
| Native syntax only | Zero polyfill bytes | Modern targets only | Syntax errors in older runtimes |
Babel’s @babel/plugin-transform-optional-chaining rewrites ?. into checks that work in ES5 environments. Pairing it with @babel/plugin-transform-runtime avoids duplicating helper code across files. CoreJS selective import is useful when you want a runtime patch rather than a syntactic rewrite, but it does not remove the need to transform syntax for parsers that reject ?. outright.
Verify the result and know the limits
Check your Node version with:
node --version
Optional chaining is native in Node 14 and later. If your target is older, run your bundled code in that environment and confirm that user?.address?.zip no longer throws a syntax error. For browser targets, compare your supported browser list against native optional-chaining support.
Limitations to remember:
- Unconditional imports execute in recent browsers even when native support exists, wasting a small amount of parse and execution time.
- Tree-shaking depends on static analysis. Code that builds property access dynamically, such as through
eval, can prevent the bundler from proving the polyfill is unused. - The polyfill addresses runtime behavior. It does not teach an old parser to understand
?.syntax.
Practical closing checklist
- Install
core-js@3as a development dependency. - Import
core-js/es2020/optional-chainingonly where needed. - Build and inspect the bundle to confirm only the optional-chaining module is included.
- Test in your oldest supported runtime.
- If most of your traffic is modern, prefer Babel transpilation or differential serving instead of shipping the polyfill to everyone.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.