postMessage for Cross-Context Communication

In this latest blog from the developers on our Product Traction team, they share how they're using postMessage for cross-context communication like an iframe.

In modern web apps, you’ll often find yourself needing to communicate between browser contexts — like a parent window and an iframe — without direct DOM access. That’s where window.postMessage comes in: a built-in browser API that allows secure cross-origin messaging between windows, iframes, and even popups.

This post walks through how postMessage works, how to implement both sides of the communication, and what kinds of problems it can solve — like dynamically resizing an embedded iframe based on its content.

Why Use postMessage?

You can’t directly reach into an iframe’s DOM if it’s served from another origin. The browser blocks this for security reasons. But sometimes you still need to exchange data — resize events, form submissions, status updates, etc.

postMessage is your tool for that. It lets one window send a message to another, even across origins, as long as both sides are set up to send and receive.

The Sender Side

Let’s say you’ve got an app embedded inside an iframe — maybe it’s a scheduler, a dashboard, or a form. It renders dynamic content and needs to tell the parent how tall it is so the iframe can resize appropriately.

Here’s a simple sender implementation that posts a resize message to the parent:

// inside the iframe context
function sendResize() {
  const height = document.body.scrollHeight;
  window.parent.postMessage(
    { type: 'resize', height },
    '*' // In production, use a specific origin string
  );
}

window.addEventListener('load', sendResize);
window.addEventListener('resize', sendResize);

In a real-world case, we encountered this exact setup with Zoom’s embedded calendar scheduler. Zoom handles this logic internally — they emit a resize message from inside the iframe when the content height changes.

The Receiver Side (React Example)

On the parent page, we need to listen for that postMessage event and apply the new height to the iframe. Here’s a clean implementation using React:

import React, { useEffect, useRef, useState } from 'react';

const EmbeddedIframe = ({ src, id }) => {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [height, setHeight] = useState<number>(600); // fallback height

  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      // Filter by origin and message type for safety
      if (
        event.origin === 'https://scheduler.zoom.us' && // Replace as needed
        event.data?.type === 'resize' &&
        typeof event.data.height === 'number'
      ) {
        setHeight(event.data.height);
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);

  return (
    <iframe
      id={id}
      ref={iframeRef}
      src={src}
      style={{
        width: '100%',
        border: 'none',
        height: `${height}px`,
        transition: 'height 0.3s ease'
      }}
    />
  );
};

This gives you a fully reactive iframe that adjusts itself based on whatever the embedded app reports — without layout bugs or manual hacks.

What This Enables

With this setup, you can:

  • Embed third-party content cleanly without hardcoded heights

  • Coordinate actions between different parts of a complex app (e.g., parent → iframe → popup)

  • Trigger parent-side behavior based on iframe events, such as submitting a form or completing a booking

  • Build safer integrations by filtering messages by origin and type

Tips for Using postMessage Well

  • Always validate event.origin to ensure you're not accepting untrusted input.

  • Namespace your messages with a type field to avoid conflicts ({ type: 'resize', height: ... }).

  • Clean up listeners on unmount to avoid memory leaks in single-page apps.

  • Debounce or throttle resize messages if the embedded content resizes frequently.

  • Avoid sending large payloadspostMessage is for coordination, not data transport.

The postMessage API is an underrated workhorse for modern web integrations. It gives you a clean, browser-native way to bridge the sandbox between isolated browser contexts — safely and predictably.

Whether you’re working with Zoom’s embedded scheduler, custom widgets, or internal tools, knowing how to handle postMessage properly will save you time and help your apps feel more seamless.


The Thin Air Labs Product Traction team provides strategic product, design and development services for companies of all sizes, with a specific focus on team extensions where they seamlessly integrate into an existing team. Whether they are deployed as a team extension or as an independent unit, they always work with a Founder-First Mindset to ensure their clients receive the support they need.

To learn more about our Product Traction service, go here.

Build what's next with us