Photo by Thomas Tastet on Unsplash

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!


Hey. I'm Thuan.
I write anything to help others.