01 article

Iframe Auto-Resize Took 17 Years — Chrome 154 Finally Ships It

Chrome 154's frame-sizing property and the responsive-embedded-sizing meta tag finally kill the postMessage iframe-resize hack. Here's how the API works, why the opt-in and requestResize design matter, and how to adopt it today without breaking Firefox and Safari.

Iframe Auto-Resize Took 17 Years — Chrome 154 Finally Ships It

Iframe auto-resize took 17 years — Chrome 154 finally ships it

In January 2009, a developer posted a question on Stack Overflow: how do you make an iframe's height match the content inside it? The question is still open, still getting answers, and its top reply has been upvoted tens of thousands of times. Seventeen years of copy-pasting that answer is how web developers have shipped embeds and comment widgets. Chrome 154 shipped a CSS property that makes the whole hack obsolete.

The hack we have all been shipping

An iframe is a fixed viewport. The browser gives it the width and height you set, and the document inside scrolls within that box. Nothing in that model tells the embedding page how tall the inner document actually is, especially across origins, where the two pages can't read each other's DOM.

So we built a workaround, and it looks the same everywhere: the embedded page measures its own height with document.body.scrollHeight or a ResizeObserver, posts that number to the parent with postMessage(), and the parent validates the origin and writes it into the iframe's inline style. Libraries exist purely to automate this dance. The same-origin shortcut skips the messaging, but cross-origin embeds have no choice. Every step has a failure mode: the measurement races the last font load, the message arrives before the listener registers, a poll fires between re-renders and reports a height that was never real.

What Chrome 154 actually shipped

On September 16, 2026, the Chrome team announced responsively-sized iframes in Chrome 154. The API has three small pieces. On the embedding page, a new CSS property names which dimension should come from the content:

.embed {
  width: 100%;
  frame-sizing: content-height;
}

On the embedded page, a meta tag opts the document in:

<meta name="responsive-embedded-sizing" content="allow-origins=*">

That's the complete integration. No listener, no measurement, no messaging. The browser renders both documents, so it can size the frame from the inner layout directly. The pattern this replaces, manual measure plus postMessage plus manual resize, was always a simulation of something the engine could do natively. It just took 17 years for the engine to catch up.

How frame-sizing works

The property takes five values:

frame-sizing: auto;                /* default, classic iframe */
frame-sizing: content-width;
frame-sizing: content-height;
frame-sizing: content-inline-size;
frame-sizing: content-block-size;

auto keeps the existing behavior, so nothing breaks when you adopt the property selectively. For the usual case of a vertical embed growing to fit, content-height is what you want. The inline and block variants do the same job along the document's writing axes, which matters for vertical writing modes. You can keep normal CSS constraints in play. Cap the frame and the inner document scrolls past the cap, like any overflow:

.embed {
  width: 100%;
  max-height: 80vh;
  frame-sizing: content-height;
}

A comment widget that grows to fit three comments, or shows an inner scrollbar after the fortieth, is exactly the behavior you'd want and never got from the postMessage hack without extra code.

Dynamic content and requestResize

Here is the design decision that makes this actually ship: the browser does not continuously observe layout inside the embedded document. If your widget loads more comments or expands a panel after the initial render, the embedded page asks for a new size calculation itself:

async function loadMore() {
  const items = await fetchMoreItems();
  renderItems(items);
  window.requestResize();
}

Call requestResize() once, after all the DOM changes are made. That explicit update model is not laziness. Continuous observation invites resize loops: the frame grows, which changes the inner viewport, which rewraps text and changes the content height, which grows the frame again. The postMessage versions of this loop drove people to debouncing and minimum-height hacks. Having the embedded document declare "I'm done changing, re-measure now" cuts the whole class of problems at the root, and puts control with the widget author, the one party who knows when a render is finished.

A two-way opt-in, and why

Notice that both sides have to agree. The parent asks for content-based sizing in CSS, and the embedded document lists which origins may size it, either with a wildcard or a space-separated list:

<meta name="responsive-embedded-sizing"
      content="allow-origins=https://publisher.example">

Cross-origin embeds work fine with the list in place. The restriction exists because a frame's dimensions leak information about the content inside it. A site that embedded your page and derived its height could probe what your document contains, how many items a user has, whether an error state is showing. That's a side channel, and the opt-in closes it for anyone not on the list. It complements Content Security Policy's frame-ancestors: that directive controls which sites can embed your page at all, while allow-origins controls which of those sites learn your document's dimensions. If you operate an embed, set the allow list deliberately rather than reaching for the wildcard.

Browser support and progressive enhancement

Chrome 154 means Chrome and Edge, on Chrome's newer two-week release cadence, so coverage on those engines moves fast. Firefox and Safari have not shipped it, and the feature is not in Baseline yet. Treat it as progressive enhancement, which the API makes easy. Keep your existing fallback and layer the new behavior on top:

.widget {
  width: 100%;
  height: 500px; /* today's fallback */
}

@supports (frame-sizing: content-height) {
  .widget {
    height: auto;
    frame-sizing: content-height;
  }
}

For dynamic widgets, feature-detect the JS half the same way:

if ("requestResize" in window) {
  window.requestResize();
}

Your postMessage listener can stay exactly where it is. In Chrome it will be redundant, in Firefox and Safari it stays load-bearing, and when the feature reaches Baseline you delete the fallback in one commit.

What it does not fix

Let me lower the temperature with the limits. First, the embedded document has to opt in. If you embed a third-party widget whose provider never adds the meta tag, you're back to the hack, so expect this to spread as embed providers add one line to their template. Second, a growing iframe changes page height, so a late requestResize() can shift the layout under a reader who is mid-scroll. Reserve space where you can. Third, content-width is the odd value out: iframes in flow need a known width for the inner layout to exist, so height is the direction that works cleanly.

And it's Chrome-only today. With Interop 2026 focused elsewhere, I wouldn't hold my breath for Firefox or Safari this year, though the whatwg/html issue tracking this has been open since 2016 and the interoperability pressure is real.

Should you migrate now

If you control both sides of an embed, add the meta tag and the @supports block this week. The meta tag is a no-op in unsupporting browsers, and the CSS block can't break a fallback that lives outside it. If you run an embed product, put the meta tag in your widget template and tell customers the frame will fit content in Chrome today. If you embed content you don't control, nothing changes yet, keep the library.

I keep thinking about how long the workaround outlived any reason it had to exist. Engines always knew the inner layout. They just never exposed it. One property, one meta tag, one function call, and a Stack Overflow question from the Obama administration's first year can finally be closed.

Comments