Sitemap
ScaleUp

The Product Development company that turns your startup ideas into reality

Cross-Domain Communication in React using postMessage

3 min readAug 26, 2025

--

Recently, I had to make two separate React apps exchange data — despite being hosted on different domains. I needed something simple and secure. One of the simplest ways I found to solve this was by using window.postMessage.

The Problem

The goal: when a user is about to access App B, App A should check if the user is logged in and then send a short-lived code securely to App B. App B can then verify this code with the backend. Since these apps are on different domains, they can’t directly share storage or state.

Why postMessage?

With postMessage, a page can talk to an iframe or a popup it created. It’s the safe, built-in way the browser lets different origins send messages to each other.

The Setup

1. Sending a Message

From App A, I open App B in a popup and send a short-lived code, which App B can send to the backend to validate the user’s session:

// AppA.tsx
const openWidget = () => {
// Open App B in a popup
const widgetWindow = window.open(
'https://widget.example.com',
'widget',
'width=800,height=600'
);

if (widgetWindow) {
// Generate or get a short-lived code from your backend
const shortLivedCode = '...'; // Generate this on your backend

// Send the code securely
widgetWindow.postMessage(
{ type: 'LOGIN_CODE', code: shortLivedCode },
'https://widget.example.com'
);
}
};

// Example button
<button onClick={openWidget}>Proceed To Widget</button>

It’s important to explicitly specify the target origin (https://widget.example.com) to keep it secure.

2. Receiving a Message

Over in App B, I set up a listener inside a React component:

// AppB.tsx
import { useEffect } from "react";

export default function AppB() {
useEffect(() => {
const handleMessage = async (event: MessageEvent) => {
// Only accept messages from App A
if (event.origin !== "https://app.example.com") return;

const { type, code } = event.data;
if (type === 'LOGIN_CODE') {
// Verify the code with your backend
const isValid = await verifyCode(code);

if (isValid) {
console.log('User is authenticated, proceeding...');
} else {
console.log('Invalid or expired code, redirecting to login.');
window.location.href = '/login';
}
}
};

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

return <div>Waiting for login verification...</div>;
}

App B receives the data from App A and exchanges the code with the backend to validate the user session and decide whether to proceed or redirect the user.

Bonus: Sending Back a Response

Sometimes App B might need to respond to App A (for example, to confirm the redirect was handled):

event.source?.postMessage(
{ type: "LOGIN_PROCESSED", result: "success" },
event.origin
);

Common Pitfalls

  • Forgetting to check event.origin → Critical for security. Don’t process messages from unknown domains.
  • Using * as the target origin → Okay for testing, but never in production. Always whitelist the exact domain.
  • Message shape mismatch → Both apps should agree on the format (e.g., { type, payload }).

Additional Security Measures

  1. Make sure to validate the code before sending or using, on both sides, to ensure it matches the expected type or pattern.

2. Set a Timeout for Window Reference

Get Neira Durmic’s stories in your inbox

Join Medium for free to get updates from this writer.

If the popup fails or is closed immediately — alert the user to prevent confusion and improve UX and security.

setTimeout(() => {
if (!widgetWindow || widgetWindow.closed) {
alert("Failed to open login widget. Please try again.");
}
}, 3000);

3. Make sure your backend:

  • Issues codes that expire in minutes.
  • Accepts codes only once.
  • Logs or blocks suspicious reuse attempts.

Final Thoughts

Using postMessage is simple, reliable, and works across domains without violating security rules.

If you’re ever in a situation where two React apps (or even one React app and a vanilla JS widget) need to communicate across domains, don’t overcomplicate it. Start with postMessage.

It’s a valuable approach that every frontend developer should be familiar with.

--

--

Published in ScaleUp

The Product Development company that turns your startup ideas into reality