Like polyfill but with pony pureness
Use ponyfill.com for linking here.
What’s a ponyfill?
A ponyfill is an implementation of a standard, but without pretending to be it.
Unlike polyfills, ponyfills don't pretend to be the native API. They offer the same functionality through explicit imports and usage, keeping your code predictable and side-effect free.
The problem with polyfills
In JavaScript, a polyfill adds missing features by monkey patching the environment, typically by modifying globals like Array.prototype, Object, or window. This approach creates several problems:
- The polyfill is only partially spec-compliant (sometimes unavoidable)
- The spec changes
- Another library polyfills the same thing differently
In general, you should not modify APIs you don’t own. Ponyfills avoid this entirely by staying pure.
It’s not just for JavaScript
- JavaScript ponyfill: Exports functionality via a normal module, doesn’t patch anything
- HTML ponyfill: Uses custom elements or classes to emulate new features
- CSS ponyfill: Uses custom properties to simulate proposed syntax
Polyfill vs Ponyfill
| Feature | Polyfill | Ponyfill |
|---|---|---|
| Patches global environment? | Yes | No |
| Aims to match spec exactly? | Yes | Often |
| Meant to be removed later? | Yes | Not necessarily |
| Causes global side effects? | Yes | No |
Ponyfills are clear. Explicit. Honest. You use them directly and deliberately.
Examples
JavaScript
Polyfill
Number.isNaN ??= function (value) { return value !== value; };
import 'is-nan-polyfill'; Number.isNaN(5);
Ponyfill
export default function isNaN(value) { return value !== value; };
import isNanPonyfill from 'is-nan-ponyfill'; isNanPonyfill(5);
HTML
<!-- Instead of a future <card> element --> <card-ponyfill> <h2 slot="title">Hello</h2> </card-ponyfill> <script> customElements.define('card-ponyfill', class extends HTMLElement { connectedCallback() { this.innerHTML = `<div class="card">${this.innerHTML}</div>`; } }); </script>
CSS
/* Instead of future syntax like @container */ :root { --container-sm: 480px; --container-lg: 768px; } .responsive-text[data-container='small'] { font-size: 0.875rem; } .responsive-text[data-container='large'] { font-size: 1.125rem; }
<div class="responsive-text" data-container="small">Responsive text</div>
When polyfills are still the right choice
Prefer ponyfills for code you control. But when a nested dependency lacks support for a feature, polyfilling the environment may be the only practical option. Forking deep transitive dependencies is rarely feasible.
Why not use the native API in a ponyfill when available?
Ponyfills should avoid relying on native APIs unless unavoidable because:
- Native APIs may behave inconsistently
- Bugs or spec changes undermine confidence
- Reimplementing avoids dependency on the environment
Use native APIs only when:
- No alternative exists
- Reimplementation would hurt performance or bundle size
How to create your own ponyfill
- Read the spec or explainer
- Implement the feature locally, without patching anything global
- Avoid assuming native API correctness unless necessary
- Write tests
- Publish a module or package
- Add
ponyfillto the keywords field - Link to ponyfill.com in your readme
Where can I find ponyfills?
Resources
- Ponyfill definition - Silicon Valley Dictionary
- Polyfills or Ponyfills? - Pony Foo
- Polyfill, Ponyfill and Prollyfill - Kikobeats
License
To the extent possible under law, Sindre Sorhus has waived all copyright and related or neighboring rights to this work.
Header based on work by Mary Winkler.