Global core-js versus core-js-pure for a distributable component library
0 reputation · 09 Jan 2021, 04:49 UTC
0 reputation · 09 Jan 2021, 04:49 UTC
A distributable component library must support IE11 alongside modern browsers. Assuming a recent core-js major version, the unresolved decision is whether to rely on global core-js polyfills at the application entry point or to import core-js-pure within each module. Global polyfills ensure that third-party dependencies expecting standard prototype methods function correctly, but they mutate the host environment and risk collisions if consumers already load a different core-js version. Pure imports avoid prototype pollution, yet they must be repeated in every module and may increase bundle size when granular features are imported individually.
The uncertainty increases because the library is consumed by multiple applications, some of which already include global polyfills and others that do not. Mixing both strategies in the same project can produce inconsistent behavior and duplicate code. A strategy is needed that supports IE11 without forcing host applications to alter their build configuration or inspect generated bundles to reconcile differences.
Should the library declare core-js as a peer dependency and document global setup, or should it self-contain core-js-pure imports? Is there a documented pattern for detecting existing global polyfills before applying pure ones? What bundle-size trade-offs arise when granular pure imports are used across many modules?
34275 reputation · 09 Jan 2021, 11:07 UTC
For a distributable component library that must run in IE11 and modern browsers without forcing host applications to change their builds, self-contain the library with core-js-pure imports. Do not declare global core-js as a peer dependency, and do not rely on application-level global polyfills as part of your library's runtime contract. Import only the specific built-ins your modules actually use, preferably through a single internal re-export module.
Global core-js mutates the host environment by patching prototypes and globals. When your library does that, every consumer inherits those mutations. If the host application already loads a different core-js version—or a competing polyfill—you get version collisions, duplicate code, and hard-to-debug behavior. A library should not decide that its consumers must accept global side effects. core-js-pure returns the same functionality without touching prototypes, so it works identically whether the host has global polyfills, has none, or has a conflicting version. This is the well-established separation between the global and pure entry points.
Assuming a recent core-js major version, the pure entry points live under core-js-pure/features/...; confirm the exact paths against your installed version. Create one internal module, for example polyfills.js, that re-exports only the features your library needs:
// internal/polyfills.js
export { default as Promise } from 'core-js-pure/features/promise';
export { default as ObjectAssign } from 'core-js-pure/features/object/assign';
// ... only what you use
Then import from that module in your library code. This keeps the dependency list auditable and makes it easy for bundlers to deduplicate when multiple modules import the same feature. Declare core-js-pure as a regular dependency so the library works with zero host setup. If you prefer a peer dependency, you are requiring every consumer to install it, which reintroduces setup friction without solving the global-collision problem.
The common fear that granular pure imports bloat the bundle is usually misplaced. When all modules resolve to the same core-js-pure version, bundlers deduplicate the underlying helpers, so repeated import statements are not the main cost. The real costs are:
core-js-pure versions in the same dependency tree.sideEffects metadata that defeat tree-shaking.Mitigate this by transpiling with a browserslist or build-target pipeline that injects only the pure imports your targets actually need, marking your package sideEffects accurately, and optionally shipping separate modern and legacy builds so modern consumers do not receive IE11-only polyfills.
There is no supported public pattern for detecting whether a global core-js polyfill is already present before deciding to use pure imports. Feature detection cannot prove that a global is native, complete, or a compatible core-js version, and core-js internal version markers are not stable API. Do not build ad-hoc global checks into your library.
If any runtime dependency bundled with your component library requires global prototype methods—such as a legacy third-party widget that calls Array.prototype.includes without importing it—then pure imports inside your library will not save that dependency. In that case, document an explicit application-level global-polyfill requirement separately from your library's own pure imports. This is a consumer-facing setup note, not a peer dependency on global core-js.
core-js import and confirm no behavioral or bundle regressions.npm ls core-js-pure in the consumer to detect duplicate versions.Array.prototype or Promise assignments; a pure build should not contain them.This approach keeps your library neutral: it works in bare IE11 environments, coexists with hosts that already use global core-js, and avoids pushing build-configuration changes onto consumers.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.