Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
Polyfills are a valuable part of Web architecture, because they promote the adoption of new features during the implementation process. However, polyfills can also create problems for standardization efforts and therefore for the future of the Web. This document defines the risks, benefits, and role of polyfills on the Web, and proposes best practices for implementors, spec editors, website developers, polyfill authors, and operators of polyfill distribution platforms.
For deeper background, see relevant TAG mailing list thread and F2F meeting minutes.
This document has been produced by the W3C Technical Architecture Group (TAG).
The TAG approved this finding at its February 2017 face to face meeting. Please send comments on this finding to the publicly archived TAG mailing list www-tag@w3.org (archive).
A polyfill is a piece of JavaScript code that is intended to exactly simulate a native, interoperable feature of the web platform, for the purpose of allowing website developers to use modern web features in older browsers.
The term polyfill was coined by Remy Sharp in the 2009 book Introducing HTML5 (p276), and differentiated from shim:
What makes a polyfill different from the techniques we have already, like a shim, is this: if you removed the polyfill script, your code would continue to work, without any changes required in spite of the polyfill being removed.
Since then, the term prollyfill has been used by some to refer to code which implements features that are yet to be sufficiently standardized to be considered part of the web platform. Ponyfill is occasionally used to refer to code which implements standard platform features but in a private scope. Other terms such as nottifill, shiv, sham, shim, library and patch also exist and are used with varying degrees of popularity and shared semantics.
We find that although the terms library and polyfill are the most well recognized of these, polyfill's etymology is non-obvious and is in part culturally specific to the UK. For the avoidance of confusion, we use the following terms in this finding and offer them as a useful reference:
From a practical standpoint, the development of web platform features goes through a number of distinct phases:
The time lag between the first interoperable implementations (5) and fully universal support (6) can be long (occasionally as long as a decade). Polyfills can often effectively bridge this gap (between stages 5 and 6 above) and allow site developers to write solutions that assume universal support.
Early, speculative polyfills help shape the standards process. However, any JavaScript library that defines a property of the global object or extends a prototype of a global constructor using a proposed or generically useful name, risks creating problems for the development of the Web if that library becomes widely used, prior to the standardization and implementation of the feature it seeks to create or emulate.
Polyfill authors can avoid these problems if their version of the feature can be used under a custom name, regardless of the existence of any native implementation of the same or similar feature under a different name. However, many site developers will canonicalize the name of a library by deferring to a 'fantasy' native implementation under a name that might be standardized in future, or which is standardized but only available in some browsers, e.g.:
requestAnimationFrame = requestAnimationFrame || webkitRequestAnimationFrame || rafEquivLibrary;
Following this pattern sets up an assumption that the standard native implementation, when available, will be functionally identical to a vendor-prefixed implementation or speculative polyfill, which may not be the case.
It is useful to recognize a point in the lifecycle of a feature at which the definition of the feature is stable enough that polyfills no longer present significant risks to the evolution of the specification, and indeed are now essential to help the feature achieve a more rapid rollout.
There is no common agreement on how to identify the tipping point, and it may indeed differ depending on the circumstances and type of feature. We find that the following indicators are relevant:
It may be difficult for polyfill authors to determine whether a feature has achieved consensus, and in cases where it's unclear, authors should consult the chairs or editors of the feature specification. In many cases this can be done by opening an issue in a public issue tracker, if the specification effort is using one, or otherwise by sending email to the appropriate mailing list. A lack of response should be interpreted as an indication that a feature has not passed the tipping point.
As the number of web platform features increases, the amount of polyfill code required to make website developers' application code run in older browsers may become extremely large - in some cases exceeding the size of the application code that depends on it. This has implications for performance, both in terms of parsing the polyfill code, and the time required to download it.
Consideration should be given to whether excessively large polyfill bundles place a punitive cost burden on users with the least ability to pay that cost: those with older devices which may be on metered connections paying for data in very small increments.
Polyfills are often written by individuals, and may not have the infrastructure or support network of a major project or organization, leading to potential security concerns. These concerns are amplified if the polyfill acquires wide adoption, which may leave many unconnected sites vulnerable to the same security exploit.
Website developers should ensure that the most up to date best practices for performance and security are followed when using any script on a website. In addition, they can achieve maximum benefits from polyfills by following these specific best practices.
Use the lifecycle guidance above to understand whether the feature has passed the tipping point, and can be considered stable. When selecting a polyfill, look for signs of quality such as:
If a feature is yet to reach the tipping point, using polyfills that squat on the proposed name for the feature is highly inadvisable, because when the feature is finalized, it might work differently to the polyfill you are using, and your website may break. If a polyfill author has followed good practice for pre-tipping point features by providing a polyfill with a non-conflicting name, don't undo this good practice by aliasing it to the proposed name.
Instead, use the speculative polyfill in private scope or under a custom name, wait until the feature has passed the tipping point, then update your code to use a polyfill that automatically defers to native implementations where they exist.
import rafPolyfill from 'request-animation-frame';
rafPolyfill(function() {
...
});
Shipping unnecessary bytes to users that don't need them wastes the user's time and data allowance, but performing feature detection first can delay the loading of necessary polyfills and adds the overhead of the loader itself. It is generally better to optimize for modern browsers, so performing efficient client-side feature detection and waiting for an extra script to load on older browsers is usually a good trade off. However, if the full set of polyfills you might need in the worst case constitutes a negligible overhead, then you could choose to serve the full set to all browsers.
Polyfill authors may choose to throw a warning if a polyfill is loaded when not needed, though care should be taken not to create unnecessary noise. If unnecessarily loading a particular polyfill creates a significant performance or security concern, a warning is appropriate.
When incorporating polyfills into your codebase, create a mechanism that allows you to easily update the polyfill from source later. Updating polyfills regularly will help to ensure that browsers receiving the polyfilled behavior exhibit the same functionality as those in which you are using the feature natively.
Typically the devices and browsers that require the largest amount of polyfill code are also the least capable of parsing it efficiently, and the most likely to be on a metered and expensive internet connection. Alternatives to polyfilling on these devices include:
If you implement a baseline, detection of browsers that fall below the line must be based on robust feature detection, not proxy indicators that may be easier to measure but which will also exclude capable browsers.
Serve polyfill code to your users in a form that can be cached locally in the user's browser. If available to you, make use of a CDN to cache the polyfill code as close to users as possible. Ideally, use the immutable
directive in the Cache-Control
response header.
Some libraries and frameworks bundle polyfills to achieve the maximum browser compatibility and developer satisfaction. We find that the following best practices apply to library authors:
In deciding whether to ship polyfill-like code inside your library, you should consider questions such as:
Number.isNaN
implementation) then the overheads are negligible and not worth any concern. However, in some cases a polyfill might exceed the size of the library.
Don't modify globals or native object prototypes. If you ship code as part of your library that emulates platform features, do so inside a closure.
Don't lean on bundled polyfill code unconditionally. In browsers that offer a native implementation, use it.
If including a polyfill is not a good solution, consider alternatives such as:
If you run a service or project that promotes and distributes polyfills, you can help the web move forward by adhering to the following best practices.
When considering whether to distribute a polyfill, check whether it meets the best practices outlined in Advice for polyfill authors.
Keep the polyfills that you host up to date. If practical, push updates directly to your customers, otherwise encourage them to move to the latest version.
If you offer a service that bundles multiple polyfills together, make sure people using the service are able to easily find out which polyfills are being included, and can exclude any polyfills for features that they have already polyfilled earlier or by another means. This is especially important if your service automatically includes polyfills for features that are dependencies of features requested by the user.
Serve polyfill code to your users in a form that can be cached locally in the user's browser. If available to you, make use of a CDN to cache the polyfill code as close to users as possible. Ideally, use the immutable
directive in the Cache-Control
response header.
If you are developing a new feature for the web, polyfills can be hugely beneficial in helping to roll out that feature.
New web features need not be constrained by the need to enable polyfill implementations. However, all other things being equal, choosing a design that is more polyfill friendly is desirable.
Ensure that the feature exposes detection hooks that can be effectively used by feature-detection code to determine whether a user agent offers a native implementation of the feature.
Whether before or after the tipping point, development of polyfills should be welcomed, provided that the polyfill author adheres to the best practices included in this finding. Help polyfill authors by sharing feature rationale and taking on feedback based on the polyfill implementation.