How to make an iframe chart embed responsive
Quick answer
To make an iframe chart embed responsive, set its width to 100% and handle height separately. If the chart keeps a fixed shape, the CSS aspect-ratio property on the iframe is enough. If its height changes with width, for example when a legend wraps on a phone, the chart page has to report its own height to the host page with postMessage, and a small script on the host page resizes the iframe. That script only works where the platform lets you add JavaScript; elsewhere, pick a fixed height that fits the narrowest screen.
Making an iframe chart responsive is two separate problems. Width is solved with one line of CSS: give the iframe a width of 100% so it fills whatever column it sits in. Height is the hard one, because the host page cannot see inside an iframe from another domain, so it has no way of knowing how tall the chart wants to be.
There are two honest answers to the height problem. If the chart keeps the same shape at every size, fix the aspect ratio in CSS. If it does not, the chart has to tell the page its height, which takes a few lines of JavaScript on each side.
Most of the platform guides on this blog, from WordPress to Squarespace to SharePoint, run into the same catch: the iframe does not size itself. This post is the answer to that catch.
Why iframes do not resize themselves
An image has a natural size, so the browser can scale it and keep its proportions. An iframe has none. Without a height, browsers give it a default of 150 pixels, which is why an embed with no height set shows as a thin strip with a scroll bar.
The obvious fix, reading the height of the page inside the frame, is blocked by the browser's same-origin policy whenever the chart is hosted on a different domain from your page. That is the normal case for any embedded chart. The page is not allowed to look inside, so the information has to be sent out from the inside.
Option 1: a fixed aspect ratio with CSS
If your chart is drawn to fill whatever box it is given, and its proportions do not need to change on a small screen, CSS is all you need. Modern browsers support the aspect-ratio property on iframes directly:
iframe.chart { width: 100%; height: auto; aspect-ratio: 16 / 10; border: 0; }
The height: auto matters. Without it, a height attribute in the embed code would win and the ratio would be ignored. Older guides wrap the iframe in a div with percentage padding-top to fake the same effect. That still works, but it is no longer necessary.
Where this breaks is the case that matters most for financial charts. On a phone, a legend with six series wraps onto three lines, axis labels stack, and a table under the chart gets longer. The chart needs more height as the width shrinks, which is the opposite of what a fixed ratio gives it. The bottom of the chart ends up clipped, or the frame grows its own scroll bar inside the page.
Option 2: let the chart report its height with postMessage
The standard fix is for the page inside the iframe to measure itself and send the number to the host page. The browser's postMessage API exists for exactly this: it lets two windows on different domains pass messages to each other safely.
This is the pattern Datawrapper uses. Its developer documentation describes the chart posting a message carrying its required height, keyed by chart ID, and a script on the host page that listens for those messages and sets the matching iframe's height. Datawrapper's own troubleshooting note is the key limitation: if the host platform strips the script, the chart stops resizing.
If you control the page inside the iframe, the chart side is short. After the chart renders, observe its size and post the height to the parent window:
const send = () => parent.postMessage({ type: "chart-height", height: document.body.scrollHeight }, "*");
new ResizeObserver(send).observe(document.body);
The host page listens, checks the message came from the chart's domain, finds the iframe that sent it and sets its height:
window.addEventListener("message", (e) => { if (e.origin !== "https://charts.example.com" || e.data?.type !== "chart-height") return; for (const f of document.querySelectorAll("iframe")) { if (f.contentWindow === e.source) f.style.height = e.data.height + "px"; } });
Three details decide whether this works in practice.
- Check the origin on the receiving side. Any page can post a message to yours, so the listener should ignore anything that does not come from the domain that hosts your charts. A height is not sensitive, which is why the sending side can use "*" as the target.
- Match the message to its iframe by comparing e.source with each iframe's contentWindow. With several charts on one page, that is how each message finds the right frame.
- Do not size the chart to the frame's viewport. If the chart's container is set to 100% of the viewport height inside the iframe, every resize makes the page taller, which triggers another resize. The height has to come from the content.
Option 3: a resizing library
If you would rather not maintain the script, iframe-resizer is the best-known library for this and handles the edge cases above. Check the license before you ship it. According to the project's own licensing page, it is available under GPL v3 or a paid commercial license, and the commercial license is the one intended for sites and applications that are not open source.
When the platform will not run your script
Options 2 and 3 need a script on the host page, and plenty of platforms will not run one. Substack does not allow custom iframes at all, which is why its route is a static image with a link to the interactive version, covered in how to embed a financial chart in Substack. Notion embeds a URL in a resizable block rather than running your code, covered in the Notion guide. Email clients strip both iframes and scripts, so the answer there is always an image, covered in the email guide.
On those platforms, pick the fixed height that fits the chart at its narrowest likely width, usually a phone, and accept some white space on a desktop. A little empty space is a better failure than a clipped axis, because the reader can still see every number.
A short checklist that avoids most of the problems:
- Set width to 100% and never a fixed pixel width.
- Test at 360 pixels wide before you publish, because that is where legends wrap and heights break.
- Give the iframe a title attribute so screen readers can announce what the frame contains.
- Add loading="lazy" to charts below the fold so they do not slow the page down.
[QUADESTO-EMBED: One multi-series rate chart embedded three times side by side at phone, tablet and desktop widths, showing a fixed-ratio frame clipping the wrapped legend on the phone view next to a postMessage-sized frame that grows to fit]
Where Quadesto fits
Quadesto charts are shared three ways: an iframe embed for platforms that accept one, a public link, and a static image for the platforms that do not. That covers every route in this post, including the image-plus-link fallback for Substack and email.