Skip to content

andsafe-AG/iframe-messaging

 
 

Repository files navigation

@andsafe/iframe-messaging

A lightweight, framework-agnostic library for automatic iframe height resizing. Perfect for embedded applications that need to communicate their size to parent windows.

npm version License: MIT CI CodeQL Coverage

Features

  • 📦 Tiny Bundle Size - Minimal dependencies, optimized for performance
  • 🔄 Automatic Resizing - Uses ResizeObserver for efficient size monitoring
  • 💬 PostMessage Protocol - Secure cross-origin communication
  • 🎯 TypeScript Support - Full type definitions included
  • 🌐 Universal - Works with vanilla JS, TypeScript, and all frameworks
  • 📤 Triple Exports - CommonJS, ES Module, and UMD (browser globals) support
  • 🔒 SSR Safe - Server-side rendering compatible

Installation

npm install @andsafe/iframe-messaging
yarn add @andsafe/iframe-messaging
pnpm add @andsafe/iframe-messaging

Quick Start

ES Modules

import { autoInitIFrameResizing } from '@andsafe/iframe-messaging';

// Initialize with automatic DOM ready detection
const cleanup = autoInitIFrameResizing();

// Cleanup when needed (optional)
// cleanup();

CommonJS

const { autoInitIFrameResizing } = require('@andsafe/iframe-messaging');

const cleanup = autoInitIFrameResizing();

Browser (UMD)

You can use the library directly in the browser without a build tool:

<!-- From CDN (unpkg) -->
<script src="https://unpkg.com/@andsafe/iframe-messaging@1.5.0/dist/iframe-messaging.umd.js"></script>

<!-- Or from jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@andsafe/iframe-messaging@1.5.0/dist/iframe-messaging.umd.js"></script>

<script>
  // The library is available globally as IFrameMessaging
  const cleanup = IFrameMessaging.autoInitIFrameResizing();
</script>

Or download and host the file yourself:

<script src="path/to/iframe-messaging.umd.js"></script>
<script>
  const cleanup = IFrameMessaging.autoInitIFrameResizing({
    onError: (error) => console.error(error)
  });
</script>

Usage

Basic Usage

The simplest way to use this library is with autoInitIFrameResizing, which handles DOM ready state automatically:

import { autoInitIFrameResizing } from '@andsafe/iframe-messaging';

const cleanup = autoInitIFrameResizing();

Manual Initialization

If you need more control over when initialization happens:

import { initIFrameResizing } from '@andsafe/iframe-messaging';

document.addEventListener('DOMContentLoaded', () => {
  const cleanup = initIFrameResizing();
});

With Error Handling

import { autoInitIFrameResizing } from '@andsafe/iframe-messaging';

const cleanup = autoInitIFrameResizing({
  onError: (error) => {
    console.error('Failed to resize iframe:', error);
  },
  captureError: (error) => {
    // Send to your monitoring service
    // Example: Sentry.captureException(error);
  }
});

Cleanup

The initialization functions return a cleanup function that disconnects the ResizeObserver:

const cleanup = autoInitIFrameResizing();

// Later, when you want to stop resizing
cleanup();

// Or cleanup on page unload
window.addEventListener('beforeunload', cleanup);

API Reference

pushToDataLayer(event, options?)

Pushes an event object from within an iframe to the parent window's data layer (e.g., Google Tag Manager).

Parameters:

  • event: Record<string, unknown> - The event object to push
  • options?: IFrameCommandOptions - Optional error handling callbacks

Returns: void

Behavior:

  • No-ops on server side or when not running inside an iframe
  • Sends a pushToDataLayer command to the parent window and awaits acknowledgment
  • Calls onError / captureError on timeout or failure

Example:

import { pushToDataLayer } from '@andsafe/iframe-messaging';

pushToDataLayer({ event: 'pageView' });

pushToDataLayer(
  { event: 'purchase', value: 100 },
  {
    onError: (error) => console.error('Push failed:', error),
    captureError: (error) => Sentry.captureException(error)
  }
);

autoInitIFrameResizing(options?) ⭐ Recommended

Automatically initializes iframe resizing when the DOM is ready. This is the recommended method for most use cases.

Parameters:

  • options?: IFrameResizingOptions - Optional configuration object

Returns:

  • () => void - Cleanup function to disconnect the observer

Behavior:

  • Smart initialization: Checks document.readyState
  • If DOM is loading: Waits for DOMContentLoaded event
  • If DOM is ready: Initializes immediately
  • Safe to call anytime: Works even if called before DOM is ready

When to use:

  • Always, unless you need manual control over timing
  • In module scripts loaded at the top of the page
  • When you want convenience and safety

Example:

// Can be called anywhere - handles DOM ready state automatically
const cleanup = autoInitIFrameResizing({
  onError: (error) => console.error(error)
});

initIFrameResizing(options?)

Initializes iframe resizing immediately. The DOM must be ready before calling this.

Parameters:

  • options?: IFrameResizingOptions - Optional configuration object

Returns:

  • () => void - Cleanup function to disconnect the observer

Behavior:

  • ⚠️ Immediate initialization: No DOM ready check
  • ⚠️ Assumes documentElement exists: Will log warning if document.documentElement is not available
  • ⚠️ Timing matters: Must be called after DOM is loaded

When to use:

  • When you need manual control over initialization timing
  • Inside a DOMContentLoaded event handler
  • In scripts with defer attribute where DOM is guaranteed to be ready
  • In frameworks that handle DOM ready state for you

Example:

// Only call after you know DOM is ready
document.addEventListener('DOMContentLoaded', () => {
  const cleanup = initIFrameResizing();
});

Function Comparison

Feature autoInitIFrameResizing initIFrameResizing
DOM Ready Check ✅ Automatic ❌ Manual (your responsibility)
Safe Early Call ✅ Yes ❌ No (may fail)
Waits for DOM ✅ If needed ❌ Never waits
Convenience ✅ High ⚠️ Medium
Use Case General purpose Manual control
Recommended ✅ Yes Only if needed

IFrameCommandOptions

Base configuration options shared by all commands.

type IFrameCommandOptions = {
  /** Error callback function called when a command fails */
  onError?: (error: Error) => void;

  /** Error capture function for monitoring/logging services */
  captureError?: (error: Error) => void;
};

IFrameResizingOptions

Configuration options for iframe resizing. Extends IFrameCommandOptions.

type IFrameResizingOptions = IFrameCommandOptions & {
  /**
   * Method used to calculate the iframe height
   * - 'contentRect': Uses the height from ResizeObserver's contentRect (default)
   * - 'scrollHeight': Uses document.documentElement.scrollHeight
   */
  heightCalculationMethod?: 'contentRect' | 'scrollHeight';
};

How It Works

  1. ResizeObserver - Creates a ResizeObserver that monitors the document documentElement for size changes
  2. Size Detection - When the documentElement size changes, captures the new height. By default, it uses contentRect.height from the observer entry, but can be configured to use document.documentElement.scrollHeight.
  3. Message Passing - Sends a resize command to the parent window using postMessage
  4. Acknowledgment - Waits for acknowledgment from the parent window (20-second timeout)
  5. Error Handling - Optionally calls error handlers if the resize fails

Message Protocol

The library uses a structured message protocol:

{
  id: string,           // Unique command ID (UUID)
  sender: 'child',      // Always 'child' for embedded content
  receiver: 'parent',   // Always 'parent' for the parent window
  name: 'resize',       // Command name
  payload: [[height]]   // The new height in pixels
}

Parent Window Integration

The parent window must handle the resize command.:

Vanilla JavaScript

const iframe = document.querySelector('iframe');

window.addEventListener('message', (event) => {
  const { name, payload, id, sender } = event.data;

  if (name === 'resize' && sender === 'child') {
    const height = payload[0][0];
    iframe.style.height = `${height}px`;

    // Send acknowledgment
    event.source.postMessage({
      id: generateUUID(),
      correspondingCommandId: id,
      sender: 'parent',
      receiver: 'child',
      payload: undefined
    }, '*');
  }
});

Browser Compatibility

  • ResizeObserver: Chrome 64+, Firefox 69+, Safari 13.1+, Edge 79+
  • postMessage: Universal support
  • UUID: Requires the uuid package (included as dependency)

For older browsers, consider adding a ResizeObserver polyfill.

Use Cases

1. Embedded Forms

Perfect for embedding forms in legacy applications:

<!DOCTYPE html>
<html>
<head>
  <title>Registration Form</title>
  <script type="module">
    import { autoInitIFrameResizing } from '@andsafe/iframe-messaging';
    autoInitIFrameResizing();
  </script>
</head>
<body>
  <form id="registration">
    <!-- form fields -->
  </form>
</body>
</html>

2. Dynamic Content

Automatically resize as content changes:

import { autoInitIFrameResizing } from '@andsafe/iframe-messaging';

// Initialize resizing
autoInitIFrameResizing();

// Content changes are automatically detected
function addContent() {
  const element = document.createElement('div');
  element.textContent = 'New content';
  document.documentElement.appendChild(element);
  // Resize happens automatically!
}

3. Single Page Applications

import { initIFrameResizing } from '@andsafe/iframe-messaging';

class MyApp {
  private resizeCleanup?: () => void;

  init() {
    this.resizeCleanup = initIFrameResizing({
      captureError: (error) => this.logger.error(error)
    });
  }

  destroy() {
    this.resizeCleanup?.();
  }
}

TypeScript

Full TypeScript support with type definitions:

import type {
  IFrameResizingOptions,
  IFrameCommandOptions,
  Participant,
  Command,
  CommandResponse
} from '@andsafe/iframe-messaging';

const options: IFrameResizingOptions = {
  onError: (error: Error) => console.error(error),
  captureError: (error: Error) => Sentry.captureException(error)
};

Troubleshooting

Resize Not Working

  1. Check iframe detection: Ensure your code is running inside an iframe
  2. Verify parent handler: Confirm the parent window has a handler for resize commands
  3. Console errors: Check browser console for warnings or errors
  4. Cross-origin: Verify postMessage is allowed between origins

Performance Issues

If experiencing frequent resize events:

  1. Content optimization: Minimize layout thrashing in your application
  2. ResizeObserver debouncing: The ResizeObserver API naturally debounces events
  3. Monitor timeouts: Check if resize commands are timing out (20s limit)

TypeScript Errors

Ensure you're importing types correctly:

import type { IFrameResizingOptions } from '@andsafe/iframe-messaging';

Package Exports

This package provides CommonJS, ES Module, and UMD builds:

{
  "main": "./dist/iframe-messaging.cjs",
  "module": "./dist/iframe-messaging.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/iframe-messaging.js",
      "require": "./dist/iframe-messaging.cjs"
    }
  }
}

The UMD build (iframe-messaging.umd.js) exposes the IFrameMessaging global for direct browser usage without a bundler.

Testing

This library has comprehensive test coverage using Vitest:

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests with UI
npm run test:ui

Coverage: 97.67% statements, 96.42% branches, 100% functions

Code Quality

This project uses Biome for linting and formatting:

# Check code quality
npm run check

# Auto-fix issues
npm run check:fix

# Lint only
npm run lint

# Format only
npm run format:fix

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

Development Setup

# Install dependencies
npm install

# Run tests
npm test

# Build the library
npm run build

# Run example server
npm run server

License

MIT © andsafe AG

Support

For issues and questions, please open an issue on GitHub.

About

Standalone messaging script for applications embedded via iframe

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages

  • TypeScript 100.0%