Skip to main content
You can add Hyperlane bridging directly into your site so users never have to leave your app. The @hyperlane-xyz/warp-widget SDK handles iframe creation, theming, and event listening.

Install

pnpm add @hyperlane-xyz/warp-widget
# or
npm install @hyperlane-xyz/warp-widget
# or
yarn add @hyperlane-xyz/warp-widget

React

import { HyperlaneWarpWidget } from '@hyperlane-xyz/warp-widget/react';

function BridgePage() {
  return (
    <HyperlaneWarpWidget
      config={{
        theme: { accent: '3b82f6', mode: 'dark' },
        defaults: { origin: 'ethereum', destination: 'arbitrum' },
        routes: ['USDC/eclipsemainnet'],
      }}
      onEvent={(event) => console.log('Widget event:', event)}
      width="420px"
      height="600px"
    />
  );
}
PropTypeDefaultDescription
configWarpWidgetConfigundefinedTheme, defaults, and routes
onEvent(event) => voidundefinedEvent callback
widthstring'100%'Iframe width
heightstring'600px'Iframe height
classNamestringundefinedCSS class for the container
styleCSSPropertiesundefinedInline styles

Vanilla JS

Works with Vue, Angular, Svelte, or plain HTML — anything that can give you a DOM element.
import { createWarpWidget } from '@hyperlane-xyz/warp-widget';

const container = document.getElementById('widget-root');
if (!container) throw new Error('Missing #widget-root element');

const widget = createWarpWidget({
  container,
  config: {
    theme: { accent: '3b82f6', mode: 'dark' },
    defaults: { origin: 'ethereum', destination: 'base' },
  },
});

widget.on('ready', (payload) => {
  console.log('Widget ready at', payload?.timestamp);
});

// When you're done:
widget.destroy();
OptionTypeDefaultDescription
containerHTMLElementrequiredDOM element to mount into
configWarpWidgetConfigundefinedTheme, defaults, and routes
widthstring'100%'Iframe width
heightstring'600px'Iframe height
Returns { iframe, destroy, on }iframe is the raw HTMLIFrameElement, destroy() removes it and cleans up listeners, on(event, callback) subscribes to events and returns an unsubscribe function.

Theme Colors

Control the widget’s look by passing a theme object. All color values are hex strings without the # prefix.
config: {
  theme: {
    accent: '3b82f6',
    bg: '0f172a',
    card: '1e293b',
    text: 'e2e8f0',
    buttonText: 'ffffff',
    border: '334155',
    error: 'ef4444',
    mode: 'dark',
  },
}
PropertyDescriptionDefault
accentPrimary color for buttons, headers, and links9a0dff
bgPage backgroundtransparent
cardCard and surface backgroundsffffff
textMain text color010101
buttonTextText color inside buttonsffffff
borderBorder colorbfbfbf40
errorError state colordc2626
mode'dark' or 'light' — sets preset defaultslight
Setting mode: 'dark' applies a full dark color scheme. You can still override individual colors on top — for example, mode: 'dark' with accent: '22c55e' gives a dark widget with green accents.

Transfer Defaults

Pre-fill the transfer form so users land on a ready-to-go state instead of picking chains and tokens themselves.
config: {
  defaults: {
    origin: 'ethereum',
    destination: 'arbitrum',
    originToken: 'USDC',
    destinationToken: 'USDC',
  },
}
PropertyDescription
originOrigin chain name (e.g. 'ethereum')
destinationDestination chain name (e.g. 'arbitrum')
originTokenOrigin token symbol (e.g. 'USDC')
destinationTokenDestination token symbol

Route Filtering

By default the widget shows all routes from the Hyperlane registry. To limit which routes your users see, pass their IDs:
config: {
  routes: ['ETH/viction', 'USDC/eclipsemainnet'],
}
Route IDs are derived from the Hyperlane Registry deployments — the folder name is the symbol and the config file name is the route identifier. For example, deployments/warp_routes/USDC/eclipsemainnet-config.yaml becomes USDC/eclipsemainnet. Only specified routes will appear in the token selector.

Events

The widget sends events to your app. Use onEvent in React or widget.on() in vanilla JS.
EventPayloadDescription
ready{ timestamp }Fires when the widget has loaded

Self-Hosting

The widget points to the Hyperlane-hosted Warp UI by default. If you fork and deploy the Warp UI template yourself, the /embed route is included automatically. To restrict which sites can embed your instance, set the NEXT_PUBLIC_EMBED_ALLOWED_ORIGINS environment variable:
NEXT_PUBLIC_EMBED_ALLOWED_ORIGINS=https://app-a.com https://app-b.com
If not set, any site can embed the widget (default: *). Set this in production to prevent unauthorized sites from embedding your instance.

Alternative: No-Code Embed

If you can’t install npm packages (e.g. WordPress, Shopify, Webflow), you can embed the widget with a plain iframe:
<iframe
  src="https://nexus.hyperlane.xyz/embed?accent=3b82f6&mode=dark&origin=ethereum&destination=arbitrum&routes=ETH/viction,USDC/eclipsemainnet"
  width="420"
  height="600"
  style="border: none; border-radius: 12px;"
  sandbox="allow-scripts allow-forms allow-same-origin allow-popups"
  allow="clipboard-write"
></iframe>
All theme properties work as URL params, plus origin, destination, originToken, destinationToken, and routes (comma-separated).

Troubleshooting

Widget doesn’t load

Your site’s Content Security Policy might be blocking the iframe. Add the Warp UI origin to your CSP frame-src:
Content-Security-Policy: frame-src https://nexus.hyperlane.xyz;

Wallet popups don’t open

The iframe needs allow-popups in its sandbox for wallet extensions like MetaMask. The SDK sets this automatically — if using a raw iframe, make sure your sandbox attribute includes it.

Examples

Working examples in the hyperlane-monorepo:
  • vanilla/ — Single HTML file, no build tools. Open in a browser.
  • react-app/ — Vite + React app showing both the React component and imperative API.
cd typescript/warp-widget/examples/react-app
pnpm install
pnpm dev