Suggest an editImprove this articleRefine the answer for “What is a polyfill?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Polyfill** is JavaScript code that adds a missing browser API exactly as the spec defines it, without changing your application code. ```js if (!Array.prototype.includes) { Array.prototype.includes = function(el) { return this.indexOf(el) >= 0; }; } ``` **Key point:** always guard with `if (!API)` to avoid overwriting the native implementation.Shown above the full answer for quick recall.Answer (EN)Image**Polyfill** is JavaScript code that implements a standard web API missing in an older browser, making modern features work as if they were natively supported. ## Theory ### TL;DR - A polyfill is like a power adapter for your phone charger: it converts the interface so your existing code connects without changes. - Polyfill adds a missing API entirely. A shim patches a broken native one. A transpiler like Babel rewrites syntax. - Use when targeting browsers without ES6+ support and your build tool doesn't cover it automatically. - Always guard with `if (!API)` before defining, or you'll overwrite a faster native method. ### Quick example ```js // IE11 has no Array.prototype.includes const arr = [1, 2, 3]; arr.includes(2); // TypeError in IE11 // Add the polyfill at the top of your entry file if (!Array.prototype.includes) { Array.prototype.includes = function(searchElement, fromIndex) { return this.indexOf(searchElement, fromIndex) >= 0; }; } arr.includes(2); // true - works in IE11 now ``` The guard `if (!Array.prototype.includes)` is the entire trick. If the browser already has the method natively, it skips the block. If not, the method is added to the prototype chain and every array in that runtime picks it up automatically. ### Polyfill vs shim vs transpiler These three come up together in interviews. A polyfill implements a missing API completely, matching the spec's method signature, return values, and edge cases. A shim fixes an existing but broken or partial implementation. A transpiler like Babel rewrites syntax at build time (`async/await` to promise chains, for example) but adds no runtime APIs. The distinction matters here: transpiling `arr.includes()` to `arr.indexOf() >= 0` handles the syntax. But if `includes` is genuinely absent from the prototype at runtime, you still need a polyfill. ### How it works internally When you assign to `Array.prototype.includes`, the JS engine registers it as a property on the Array prototype object. Any call to `arr.includes()` walks the prototype chain, finds your function, and invokes it. No recompilation. The polyfill patches the global object live, so all code in that runtime shares one definition. That also explains the order rule: the polyfill must be registered before any call to the method. ### When to use - You need to support browsers missing a specific API (check caniuse.com first). - No build tool, so Babel isn't in the picture. - Node.js below a version that includes the feature natively, for example `Array.prototype.flat` below v11. - Already using `@babel/preset-env` with `useBuiltIns: 'usage'` and core-js: it handles this automatically, no manual polyfills needed. For most new projects, polyfill.io is worth knowing about. It's a CDN that reads the User-Agent header and injects only the polyfills that specific browser actually needs. ### Common mistakes **Polyfilling after first use** ```js arr.includes(2); // TypeError - method doesn't exist yet if (!Array.prototype.includes) { /* polyfill */ } // too late ``` The polyfill must run before any code that calls the method. Put it in the entry file or load it via a `<script>` tag before the app bundle. **Overwriting the native method** ```js // No guard - always replaces, even in Chrome Array.prototype.includes = function() { return true; }; // always true! ``` This breaks spec behavior (ignores `fromIndex`, breaks NaN handling) and removes the JIT-optimized native version. Always wrap with `if (!Array.prototype.includes)`. **Transpile without polyfill** Babel can rewrite syntax but IE11 still has no `fetch` at runtime. You need both: `@babel/preset-env` for syntax and a polyfill like `whatwg-fetch` for the API. Using `useBuiltIns: 'usage'` with core-js handles both in one step. **Writing your own Promise polyfill** A minimal Promise polyfill using `setTimeout` stacks up calls and crashes the stack, because it doesn't handle microtasks the way the spec requires. Use tested libraries like core-js instead of writing your own. ### Real-world usage - **core-js**: powers most Babel setups. React apps targeting IE11 pull it via `@babel/preset-env`. - **whatwg-fetch**: used in Create React App before 2018, still common in legacy enterprise codebases. - **polyfill.io**: CDN that delivers only the polyfills each browser needs based on User-Agent. - **React Native**: polyfills `requestAnimationFrame` for Android below version 5. ### Follow-up questions **Q:** What is the difference between a polyfill and Babel? **A:** Babel rewrites syntax at build time. A polyfill adds missing APIs at runtime. You often need both at the same time. **Q:** Why might a polyfill make your app slower after load? **A:** Polyfills assign plain functions to prototypes. The JS engine can't apply the same JIT optimizations it uses for native methods. The difference is small for most cases but measurable on hot code paths. **Q:** How do you include polyfills in a modern project? **A:** Add `core-js/stable` to your entry file and set `useBuiltIns: 'usage'` in `@babel/preset-env`. It tree-shakes and injects only the polyfills your target browsers actually need. **Q:** In a micro-frontend setup, what breaks if two shells load different Promise polyfills? **A:** They conflict on the global object. One polyfill overwrites the other's `Promise`, causing `.then()` chains to behave incorrectly. Fix it with a shared runtime or a single polyfill entry point loaded before any micro-frontend code runs. ## Examples ### Basic: Array.prototype.includes polyfill ```js // Without polyfill - throws in IE11 const fruits = ['apple', 'banana', 'orange']; fruits.includes('banana'); // TypeError: fruits.includes is not a function // Add polyfill before first use if (!Array.prototype.includes) { Array.prototype.includes = function(searchElement, fromIndex) { return this.indexOf(searchElement, fromIndex) >= 0; }; } // Works in any browser now fruits.includes('banana'); // true fruits.includes('grape'); // false ``` The check `if (!Array.prototype.includes)` means this code runs only in browsers that actually need it. In Chrome or Firefox, `includes` is already native and the block is skipped entirely. ### Intermediate: fetch polyfill in a React component ```js // fetch is missing in IE11 and early Edge // This pattern mirrors how whatwg-fetch works if (!window.fetch) { window.fetch = function(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onload = () => { if (xhr.status === 200) { resolve({ json: () => JSON.parse(xhr.response) }); } else { reject(new Error(xhr.statusText)); } }; xhr.onerror = () => reject(new Error('Network error')); xhr.send(); }); }; } // React component - works in IE11 with the polyfill above function UserList() { const [users, setUsers] = React.useState([]); React.useEffect(() => { fetch('https://jsonplaceholder.typicode.com/users') .then(r => r.json()) .then(setUsers); }, []); return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>; } // Output: renders user list in IE11 without errors ``` In production you'd use the full `whatwg-fetch` package rather than this simplified version. The pattern is the same: check for the global, add it if missing, leave native untouched. I've seen this exact setup in enterprise React codebases that had to support IE11 well into 2022.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.