A tiny, dependency-free custom element that keeps an iframe, video or any other embed at a fixed aspect ratio while its width follows the layout.
<responsive-embed>
<iframe width="560" height="315" src="https://www.youtube.com/embed/fCLMI5TCcqg" allowfullscreen></iframe>
</responsive-embed>Paste the embed code you were given, wrap it, done. The ratio comes from the
width and height the snippet already has.
If you know the ratio up front and you control the CSS, you do not need this package. Plain CSS does the job:
.video {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}<responsive-embed> is worth it when:
- you paste third-party embed code (YouTube, Vimeo, maps, slides...) and want
the ratio taken from its
width/heightinstead of writing CSS per embed; - the ratio is data (from a CMS, an API) and a
ratio="..."attribute is the simplest place to put it; - you want one declarative wrapper that behaves the same for
iframe,video,object,embedor anything else.
npm install responsive-embedimport 'responsive-embed';Or straight from a CDN, no build step:
<script type="module" src="https://cdn.jsdelivr.net/npm/responsive-embed@2"></script><responsive-embed ratio="21:9">
<iframe src="..." allowfullscreen></iframe>
</responsive-embed>ratio accepts width:height, width/height or a single number:
| Value | Ratio |
|---|---|
16:9 |
16 / 9 |
4/3 |
4 / 3 |
2.35 |
2.35 / 1 |
1 |
square |
Spaces around the separator are allowed (16 / 9).
Without a ratio attribute, the element uses the first child that has numeric
width and height attributes (560, 315px; percentages are ignored).
If there is none, it uses 16:9.
The ratio is resolved in this order:
- a valid
ratioattribute; - the child's
width/height; - 16:9.
An invalid ratio (for example 16x9) is never ignored silently: the element
logs a console.warn and falls back to steps 2 and 3.
Everything is live. Changing the ratio attribute or property, or the child's
width/height, or swapping the child, resizes the box immediately.
const embed = document.querySelector('responsive-embed');
embed.ratio = '4/3'; // same as setAttribute('ratio', '4/3')
embed.ratio = null; // removes the attribute, back to inference
embed.aspectRatio; // the ratio in use, as a number: 1.333...The first-level children are stretched to fill the box, whatever they are:
iframe, video, object, embed, img, a div. Fullscreen works as
usual; the browser lifts the fullscreen element out of the box.
The element is a display: block box sized with CSS aspect-ratio. It never
adds, removes or rewrites your class or style attributes, and CSS you
write on the element wins over its defaults:
responsive-embed {
max-width: 720px;
margin-inline: auto;
}
/* Force a ratio from CSS instead of the attribute. */
responsive-embed.square {
aspect-ratio: 1;
}Setting a fixed height on the element overrides the ratio, as it does for any
element with aspect-ratio.
| Import | Does |
|---|---|
responsive-embed |
Registers <responsive-embed> and exports the class. |
responsive-embed/responsive-embed.js |
Exports the class only. Registers nothing. |
Registration is guarded: if responsive-embed is already defined (a second copy
of this package, or another library using the name), importing the package
again does nothing instead of throwing.
ResponsiveEmbed: the element class.ResponsiveEmbed.define(tagName = 'responsive-embed', registry = customElements)registers the class unlesstagNameis taken, and returns the constructor registered under that name. Use it to pick another tag name.ratio: theratioattribute, as a string ornull.aspectRatio(read-only): the ratio in use, as width divided by height.
parseRatio(value): parses16:9,4/3,2.35... into a number, ornullif invalid.inferRatio(element): the ratio from the first child with numericwidth/height, ornull.DEFAULT_RATIO:16 / 9.
TypeScript declarations are included, and <responsive-embed> is added to
HTMLElementTagNameMap.
Any browser with custom elements, shadow DOM, CSS aspect-ratio and
constructable stylesheets: Chrome/Edge 88+, Firefox 101+, Safari 16.4+.
There is no fallback for older browsers.
1.x was a Polymer 1 element loaded through HTML Imports and Bower, which no current browser supports.
- Drop
webcomponents.jsand the<link rel="import">; import the module instead (see Install). ratio="16:9",4:3,21:9and1:1keep working, and any other ratio now does too.changeRatio('4:3')is gone. Set the attribute or theratioproperty.- The internal
v16-9,v21-9,v4-3andv1-1classes are gone. The element no longer touches theclassattribute at all. - An unknown ratio now logs a warning instead of silently becoming 16:9.
- Any child element is sized, not only
iframe,objectandembed.
The full list of changes is in the changelog.
npm install
npm test # runs the suite in headless Chrome
npm start # serves the demo