Polyfill
Tool
Code that implements a capability a browser lacks, so code written against the newer capability still runs on older browsers.
Why it exists
On the web the people building something don't choose the runtime. The same code executes in the user's browser, and which browser that is belongs to the user. A capability entering the standard takes years to be implemented across browsers, and after it is implemented, plenty of people stay on older versions for years more. Neither of those clocks is under your control, and together they run about five years.
That left two options. The first is not using the new capability at all. It's safe, and it means writing ten lines where one would do, with code accumulating only in the style of several years ago. The second is hard-coding a different route per browser. That collapses quickly: conditionals splitting one capability three ways end up scattered across a dozen files, and every new browser means finding and editing all twelve.
A polyfill is the third route. Rather than routing around the missing capability, you implement it and drop it in as though it had been there all along. After that the rest of the code uses it without thinking about browsers. The branching collects into one place and the other twelve files get clean.
What it solves
All it does is make a missing capability present, and the payoff is that the rest of the code stops carrying conditionals. Four steps.
- Check for the capability itself, not the browser. Branching on a browser name means editing the list every time a new browser appears; asking whether the capability exists needs no list at all, and it keeps working for browsers nobody has heard of yet.
- If it's missing, implement it under the same name. The name and the behavior have to match the standard. Same name with different behavior just relocates the problem: results diverge between browsers that support it and browsers that don't.
- The rest of the code uses it with no branching. This is where the value is. Browser handling collects into one file, and the remaining JavaScript gets written against the specification. New team members read the spec instead of a compatibility table.
- Remove it once support is broad enough. A polyfill nobody needs is code to delete, and this last step is the one teams skip. Nothing breaks when a stale polyfill stays, which is exactly why it stays.
The cost is download size. A 60-kilobyte polyfill bundle is 60 kilobytes every user downloads. If the users who actually need it are 2 percent of the total, the other 98 percent are receiving code they never execute, and that difference lands directly on how fast the page appears. So the bundle comes up regularly in web performance reviews, alongside the practice of shipping it only to the browsers that need it. Measuring the share before deciding takes about ten minutes in the access logs.
What can't be filled is equally clear. New syntax can't be covered this way, because a browser that can't parse the syntax fails while reading the very file the polyfill lives in. Syntax belongs to the tooling that rewrites code into an older form.
Alternatives
There are four, and they differ in what gets given up.
The first is rewriting syntax into an older form at build time. A bundler, which packs many files into deployable output, usually handles this too, so listing the target browsers in the configuration takes care of the rest. It doesn't compete with polyfills; the two pair up. Syntax goes to this side, capabilities to the polyfill.
The second is not using the capability. It's the cheapest answer and the correct one more often than people expect. Do the same job with something every browser has supported for ten years and download size doesn't grow and no code needs deleting later. The trade is a few extra lines today against a dependency someone has to maintain.
The third is leaving the gap open. Keeping the core usable when the environment is poor is graceful degradation, and on older browsers only the trimmings — a smooth transition, an autosave — go missing. Reading and writing documents still works, which avoids 98 percent of users carrying a heavy file for the other 2 percent.
The fourth is narrowing the support list. Once a browser's share in your traffic drops under 0.3 percent, dropping support becomes available as a decision. What that needs is not a technical judgment but a number and an agreement. The number settles nothing on its own; somebody has to accept that a small group of users gets turned away.
How it sounds at work
"A polyfill can't cover this. It's syntax, so it has to happen at build time."
Pointing out that capabilities and syntax are different problems.
"This bundle is 60 kilobytes and 2 percent of users need it."
Who pays the cost and who gets the benefit don't line up.
"There's a polyfill in here from three years ago. Everything supports it now."
The removal step got skipped.
Theo cleans this list up every six months on the Pagedeck frontend. He pulls browser shares from the access logs, updates the support list, and deletes the polyfills nobody needs. Last time three of four came out and the download shrank by 48 kilobytes. The pass takes an afternoon and pays for itself in load time.
The one that stayed was there for an enterprise customer. One account couldn't upgrade its browsers under its own security policy, and the contract was large enough that dropping it wasn't an option. Theo changed that polyfill to load only on matching browsers, so everyone else stopped receiving it.
What people get wrong
The most common reading is that a polyfill closes every gap between browsers. The name says it fills in what's missing, so the assumption is natural. What it can actually fill is capabilities expressible as code — functions and objects. Syntax it can't, as above, and neither can differences in how pages get laid out. Restoring an unsupported layout rule through a polyfill is effectively impossible, and that territory gets handled separately as cross-browser work. Teams that assume everything is fillable and use new capabilities freely end up rewriting half of it just before release.
The other frequent view is that installing one settles the matter. Once it's in, it works quietly and causes no trouble, so it gets forgotten. But this code's lifespan is tied to browser support, and support shifts substantially inside a year. Four polyfills added three years ago, all of them now unnecessary, still going out to every user is not an unusual state. Better to schedule pulling the support numbers again every six or twelve months and deleting what's no longer needed. One calendar reminder replaces the discipline nobody has. Without that pass, download size only grows year over year.
Where to go next
- Frontend
see where the compatibility branch sits inside the whole picture
- Cross-browser compatibility
the full body of work on browser differences
Last updated 2026-09-16