Iframe – change window size (cross domain)
CHALLENGE: change iframe size from the inside
SOLUTION: use browser window.postMessage() Web API
We would like to embed our React App inside iframe on a third-party domain. How to add the ability to dynamically change window size from inside the iframe? Let’s find out.
Websites are protected by CORS policy, which forbids iframes from making changes in the parent site DOM. This security policy blocks malicious actions from third-party embeds.
postMessage
By default, scripts can modify DOM of the page only if they share the same host and protocol (same origin policy). The window.postMessage() method enables cross-origin communication between a page and an iframe embedded within it. It allows to dispatch MessageEvent from one window and to receive it in another.
Iframe – changing window size
We need to take 2 steps to have the ability to change iframe size (width and height) and trigger it from iframe code:
- add iframe code to the parent page and window.addEventListener
- use postMessage inside iframe to trigger size changes
Iframe embed
We’re going to paste our iframe embed code on the parent website. There is parent div “iframe-parent” that controls the width and height, and iframe displays our React App content. The last part is window.addEventListener that is waiting to receive messages from iframe. When a message is received, inline styles are applied to the div.
<!-- public/parent.html -->
<html>
<head></head>
<body style="background:#ccc; border:4px solid #999; padding:10px; margin:0;">
<h1>This is parent page</h1>
<div id="iframe-parent"
style="border: 0px;
background-color: #282c34;
pointer-events: none;
z-index: 2147483639;
position: fixed;
bottom: 0px;
width: 300px;
height: 400px;
overflow: hidden;
opacity: 1;
max-width: 100%;
right: 0px;
max-height: 100%;">
<iframe src="http://localhost:3001" id="iframe-source"
style="pointer-events: all;
background: none;
border: 0px;
float: none;
position: absolute;
inset: 0px;
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
min-height: 0px;">
</iframe>
</div>
<script>
// communication
const iframeParent = document.getElementById("iframe-parent");
window.addEventListener('message', function (e) {
let message = e.data;
if (!iframeParent) {
return;
}
iframeParent.style.height = message.height;
iframeParent.style.width = message.width;
iframeParent.style.background = message.background;
}, false);
</script>
</body>
</html>
Change iframe parent size
Iframe uses React to showcase an example of using window.parent.postMessage. But it’s just javascript, it can be used without React. It is enough to use plain javascript to establish a connection with iframe. On button click, we’re sending a message to the parent page to adjust the styles of div with ID #iframe-parent. Active class is just for better presentation (highlighting the recently clicked button).
// src/App.jsx
import React, {useState} from 'react';
import './App.css';
function App() {
const [active, setActive] = useState('button0');
const changeIframeParentSize = (width = '300px', height = '300px', background = '#282c34') =>{
let message = { height: height, width: width, background: background };
window.parent.postMessage(message, "*");
}
return (
<div className="App">
<header className="App-header">
<div className={'App-body'}>
<h2>Change window size from iframe</h2>
<button id={'button0'}
key={'button0'}
className={active === 'button0' ? "active" : undefined}
onClick={(event)=> { setActive(event.target.id); changeIframeParentSize('300px','400px')}}>300px x 400px</button>
<button id={'button1'}
key={'button1'}
className={active === 'button1' ? "active" : undefined}
onClick={(event)=> { setActive(event.target.id); changeIframeParentSize('800px','800px')}}>800px x 800px</button>
<button id={'button2'}
key={'button2'}
className={active === 'button2' ? "active" : undefined}
onClick={(event)=> { setActive(event.target.id); changeIframeParentSize('400px','400px', 'pink')}}>400px x 400px and pink</button>
<button id={'button3'}
key={'button3'}
className={active === 'button3' ? "active" : undefined}
onClick={(event)=> { setActive(event.target.id); changeIframeParentSize('600px','600px')}}>600px x 600px</button>
<button id={'button4'}
key={'button4'}
className={active === 'button4' ? "active" : undefined}
onClick={(event)=> { setActive(event.target.id); changeIframeParentSize('300px','500px', 'red')}}>300px x 500px and red</button>
</div>
</header>
</div>
);
}
export default App;
and styles needed for the Demo example:
.App {
text-align: center;
background-color: transparent;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color:#fff;
padding:20px;
}
.App-body button {
cursor:pointer;
margin:10px;
border:0;
padding:5px 10px;
background:#fff;
color:#282c34;
}
.App-body button.active {
background:darkslategrey;
color:#fff;
}
Changing styles from iframe
Changing width and height of div is only one option. You can also change any other styles from iframe. It’s just a matter of passing an additional parameter to the changeIframeParentSize function and adding an extra line in parent.html :
<script>
// communication
..
iframeParent.style.background = message.background;
</script>
Cross domain
In our example, we’re using the same domain: http://localhost:3001/parent.html and http://localhost:3001 for iframe source. So, technically, we don’t have proof that this will work cross-origin (having a parent website and iframe using different domains).
If we have the React application, we can upload our code to Netlify really fast and test it cross domain. For Netlify deployment, we need to execute the following in the console:
// One time:
npm install netlify-cli -g
// To deploy the code:
npm run build
netlify deploy
build (Publish directory)
netlify deploy --prod
Now, we can visit the /parent.html site hosted on Netlify, and iframe will be still pointing to the localhost url. I have good news, it is still working! PostMessage API provides an easy way to enable communication with iframe having the parent website hosted on a different domain.
That’s it for today’s tutorial. Make sure to follow us for other useful guidelines and don’t forget to sign up for our newsletter.