Communicate with Iframe using React Hooks
📅April 12, 2022
During development, we may need to embed the other website on our web with an Iframe.
In most cases, we just put the Iframe link/HTML for React to do the rendering stuff. But sometimes we want to send requests/data to parents as well.
Setup
To start, let’s create a React app that renders an Iframe.
import React, { useState, useEffect } from 'react'
export default function App() {
const [message, setMessage] = useState('')
// This hook is listening an event that came from the Iframe
useEffect(() => {
const handler = (ev: MessageEvent<{ type: string, message: string }>) => {
console.log('ev', ev)
if (typeof ev.data !== 'object') return
if (!ev.data.type) return
if (ev.data.type !== 'button-click') return
if (!ev.data.message) return
setMessage(ev.data.message)
}
window.addEventListener('message', handler)
// Don't forget to remove addEventListener
return () => window.removeEventListener('message', handler)
}, [])
return (
<div className="App">
<h4>{message}</h4>
<iframe src="/iframe.html" title="Demo Iframe" />
</div>
)
}
Then, we also create a simple Iframe UI that hold an input and a button.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
body {
background-color: silver;
}
input,
button {
margin: 0.5em;
}
input {
display: block;
width: 15rem;
}
</style>
</head>
<body>
<div>
<h4>
Enter some text in the input box and then click the button to see the
message appear on the parent.
</h4>
<input id="user-input" type="input" />
<button id="btn">Send</button>
</div>
<script src="iframe.js" />
</body>
</html>
To make the input
field and Send
button work, let’s create iframe.js
file
// This function is listening a "click" event from iframe.html
document.getElementById('btn').addEventListener('click', e => {
/** @type HTMLInputElement */
const input = document.getElementById('user-input')
window.parent.postMessage(
{
type: 'button-click',
message: input.value,
},
'*'
)
input.value = ''
})
Here, we’re listening for an click
event of Send button. We also get the input value from the input field.
Then window.parent.postMessage
will send the object data to the parent (which is App
component in this case)
After all, we reset the input field to empty after click Send
button.
How Iframe communicate with its parent?
Using postMessage
method
Syntax
targetWindow.postMessage(message, targetOrigin)
Name | Description |
---|---|
targetWindow | reference to a window or Iframe that we want to send a message |
message | is a data to send to targetOrigin, can be string, JSON, binary or object, etc |
targetOrigin | is a place where the message will be received. (ex: https://example.com) or can be "*" (will send data to all windows that are targeted to this origin) |
Note: For targetOrigin value. We using the
"*"
for the demo but in the real world application. This is not recommended.
More detail about postMessage
you can see here.
How parent received the data from Iframe?
Using addEventListener
method
In React, we need catch the event that has been created by Iframe after user click
. We catch theme whenever Component is mounted.
In this case, we will using React hooks.
useEffect(() => {
const handler = (ev: MessageEvent<{ type: string, message: string }>) => {
console.log('ev', ev)
if (typeof ev.data !== 'object') return
if (!ev.data.type) return
if (ev.data.type !== 'button-click') return
if (!ev.data.message) return
setMessage(ev.data.message)
}
window.addEventListener('message', handler)
// Don't forget to remove addEventListener
return () => window.removeEventListener('message', handler)
}, [])
This hook will run when the Component is mounted. It will listen to an event called "message"
which is an event that we built on iframe.js
file.
Then, we create a function handler
to get the data from Iframe and set the result to the message by using setMessage
Now we can able to see the result that the user was typing on input in the Iframe. Awesome !!
For more detail, you can check my codesandbox. Hope this help!