-
Notifications
You must be signed in to change notification settings - Fork 3
feat(docs): add embedded widget guide #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,211 @@ | ||
| --- | ||
| title: "Embedding the Bridge Widget" | ||
| description: "Embed Hyperlane's bridge widget in your app using the @hyperlane-xyz/warp-widget SDK or a plain iframe" | ||
| --- | ||
|
|
||
| You can add Hyperlane bridging directly into your site so users never have to leave your app. The [`@hyperlane-xyz/warp-widget`](https://www.npmjs.com/package/@hyperlane-xyz/warp-widget) SDK handles iframe creation, theming, and event listening. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| pnpm add @hyperlane-xyz/warp-widget | ||
|
paulbalaji marked this conversation as resolved.
|
||
| # or | ||
| npm install @hyperlane-xyz/warp-widget | ||
| # or | ||
| yarn add @hyperlane-xyz/warp-widget | ||
| ``` | ||
|
|
||
| ## React | ||
|
|
||
| ```tsx | ||
| 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" | ||
| /> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| | Prop | Type | Default | Description | | ||
| | ----------- | ------------------ | ----------- | --------------------------- | | ||
| | `config` | `WarpWidgetConfig` | `undefined` | Theme, defaults, and routes | | ||
| | `onEvent` | `(event) => void` | `undefined` | Event callback | | ||
| | `width` | `string` | `'100%'` | Iframe width | | ||
| | `height` | `string` | `'600px'` | Iframe height | | ||
| | `className` | `string` | `undefined` | CSS class for the container | | ||
| | `style` | `CSSProperties` | `undefined` | Inline styles | | ||
|
|
||
| ## Vanilla JS | ||
|
|
||
| Works with Vue, Angular, Svelte, or plain HTML — anything that can give you a DOM element. | ||
|
|
||
| ```ts | ||
| 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: { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| 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(); | ||
| ``` | ||
|
|
||
| | Option | Type | Default | Description | | ||
| | ----------- | ------------------ | ----------- | --------------------------- | | ||
| | `container` | `HTMLElement` | required | DOM element to mount into | | ||
| | `config` | `WarpWidgetConfig` | `undefined` | Theme, defaults, and routes | | ||
| | `width` | `string` | `'100%'` | Iframe width | | ||
| | `height` | `string` | `'600px'` | Iframe height | | ||
|
|
||
| Returns `{ iframe, destroy, on }` — `destroy()` removes the iframe and cleans up listeners, `on(event, callback)` subscribes to events and returns an unsubscribe function. | ||
|
Xaroz marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Theme Colors | ||
|
|
||
| Control the widget's look by passing a `theme` object. All color values are hex strings **without the `#` prefix**. | ||
|
|
||
| ```ts | ||
| config: { | ||
| theme: { | ||
| accent: '3b82f6', | ||
| bg: '0f172a', | ||
| card: '1e293b', | ||
| text: 'e2e8f0', | ||
| buttonText: 'ffffff', | ||
| border: '334155', | ||
| error: 'ef4444', | ||
| mode: 'dark', | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| | Property | Description | Default | | ||
|
paulbalaji marked this conversation as resolved.
|
||
| | ------------ | ----------------------------------------------- | ----------- | | ||
| | `accent` | Primary color for buttons, headers, and links | `9a0dff` | | ||
| | `bg` | Page background | transparent | | ||
| | `card` | Card and surface backgrounds | `ffffff` | | ||
| | `text` | Main text color | `010101` | | ||
| | `buttonText` | Text color inside buttons | `ffffff` | | ||
| | `border` | Border color | `bfbfbf40` | | ||
| | `error` | Error state color | `dc2626` | | ||
| | `mode` | `'dark'` or `'light'` — sets preset defaults | `light` | | ||
|
|
||
| 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. | ||
|
|
||
| ```ts | ||
| config: { | ||
| defaults: { | ||
| origin: 'ethereum', | ||
| destination: 'arbitrum', | ||
| originToken: 'USDC', | ||
| destinationToken: 'USDC', | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| | Property | Description | | ||
| | ------------------ | ------------------------------------------ | | ||
| | `origin` | Origin chain name (e.g. `'ethereum'`) | | ||
| | `destination` | Destination chain name (e.g. `'arbitrum'`) | | ||
| | `originToken` | Origin token symbol (e.g. `'USDC'`) | | ||
| | `destinationToken` | Destination 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: | ||
|
|
||
| ```ts | ||
| config: { | ||
| routes: ['ETH/viction', 'USDC/eclipsemainnet'], | ||
| } | ||
| ``` | ||
|
|
||
| Route IDs are derived from the [Hyperlane Registry deployments](https://github.com/hyperlane-xyz/hyperlane-registry/tree/main/deployments/warp_routes) — 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. | ||
|
|
||
| | Event | Payload | Description | | ||
| | ------- | --------------- | -------------------------------- | | ||
| | `ready` | `{ timestamp }` | Fires when the widget has loaded | | ||
|
paulbalaji marked this conversation as resolved.
|
||
|
|
||
| ## Self-Hosting | ||
|
|
||
| The widget points to the Hyperlane-hosted Warp UI by default. If you [fork and deploy the Warp UI template](https://github.com/hyperlane-xyz/hyperlane-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: | ||
|
|
||
| ```text | ||
| NEXT_PUBLIC_EMBED_ALLOWED_ORIGINS=https://app-a.com https://app-b.com | ||
| ``` | ||
|
|
||
| If not set, any site can embed the widget (default: `*`). | ||
|
Xaroz marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## 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: | ||
|
|
||
| ```html | ||
| <iframe | ||
| src="https://nexus.hyperlane.xyz/embed?accent=3b82f6&mode=dark&origin=ethereum&destination=arbitrum" | ||
|
paulbalaji marked this conversation as resolved.
Outdated
|
||
| width="420" | ||
| height="600" | ||
| style="border: none; border-radius: 12px;" | ||
| sandbox="allow-scripts allow-forms allow-same-origin allow-popups" | ||
|
Xaroz marked this conversation as resolved.
|
||
| allow="clipboard-write" | ||
| /> | ||
| ``` | ||
|
|
||
| All [theme properties](#theme-colors) work as URL params, plus `origin`, `destination`, `originToken`, `destinationToken`, and `routes` (comma-separated). | ||
|
Xaroz marked this conversation as resolved.
|
||
|
|
||
| ## 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`: | ||
|
|
||
| ```text | ||
| 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](https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/main/typescript/warp-widget/examples): | ||
|
paulbalaji marked this conversation as resolved.
|
||
|
|
||
| - **`vanilla/`** — Single HTML file, no build tools. Open in a browser. | ||
| - **`react-app/`** — Vite + React app showing both the React component and imperative API. | ||
|
|
||
| ```bash | ||
| cd typescript/warp-widget/examples/react-app | ||
| pnpm install | ||
| pnpm dev | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.