Return Data from a Popup Window with postMessage

A popup can return a newly created record ID, file URL, or other small result to the page that opened it. Use window.postMessage() instead of editing the opener DOM directly, and validate both the sender and the message before using the data.

Last updated: November 12, 2026.

// Parent page
const popup = window.open('/uploads/new', 'upload', 'width=720,height=640');

window.addEventListener('message', (event) => {
  if (event.origin !== window.location.origin) return;
  if (event.source !== popup) return;

  const message = event.data;
  if (message?.type !== 'upload-complete') return;
  if (!Number.isInteger(message.fileId) || typeof message.url !== 'string') return;

  document.querySelector('#file_id').value = String(message.fileId);
  document.querySelector('#file_url').value = message.url;
});

// Popup page, after the server confirms the upload
window.opener?.postMessage(
  { type: 'upload-complete', fileId: 417, url: '/media/417' },
  window.location.origin,
);
window.close();

The popup sends structured data only after the server operation succeeds. The parent accepts the message only from the expected origin and the exact window reference returned by window.open().

Treat the message as untrusted input

The MDN postMessage reference recommends an exact target origin instead of *. On receipt, verify event.origin, and check event.source when the parent retains the popup reference. Then validate the message type and every field before changing the page.

Do not send passwords, session tokens, uploaded file bytes, or complete database rows. Return only the identifiers and display values the parent needs. The server must still authorize later requests that use the returned ID.

Handle popup lifecycle failures

The window.open documentation notes that browsers can block popups that are not opened directly from a user action. Check whether window.open() returns null, and provide a normal link or same-page workflow as a fallback.

Keep the original form state on the parent page while the popup is open. If the user closes the popup or the upload fails, leave that state unchanged and show a retry option. For a same-origin application, postMessage() still provides a clean boundary and avoids tightly coupling the popup to specific parent DOM elements.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov