Metalayer Bridge Widget
A React component for cross-chain token bridging, providing seamless access to anything-to-anything swaps with composable routes, powered by Caldera's Metalayer.
Features
- Cross-chain token bridging
- Wallet connectivity with viem/wagmi based providers (e.g. @rainbow-me/rainbowkit)
- Token selection interface
- Responsive design
- Framework agnostic (works with Next.js, Create React App, etc.)
- Solana support (requires additional configuration)
Installation
Using pnpm (recommended)
pnpm add @metalayer/widget
Required Dependencies
Peer dependencies
pnpm add @metalayer/viem-chains @tanstack/react-query react react-dom viem@2.x wagmi
Temporary icon dependency
pnpm add material-symbols
Optional (for RainbowKit integration)
pnpm add @rainbow-me/rainbowkit
Ensure compatible versions:
- viem: ^2.0.0
- wagmi: ^2.14.0
- @rainbow-me/rainbowkit: ^2.2.0
Solana Support
To enable Solana bridging support, you need to:
- Include Solana chains in your wagmi configuration (see example below)
- Provide a Solana signer to the Widget component
- Ensure your backend supports Solana (check with your Metalayer API configuration)
import { METALAYER_TESTNETS, solana, solanaDevnet } from '@metalayer/viem-chains';
const config = getDefaultConfig({
appName: 'Your App',
projectId: 'your-project-id',
chains: [
...METALAYER_TESTNETS,
solana,
solanaDevnet,
],
});
Required CSS
import '@metalayer/widget/styles.css';
import '@rainbow-me/rainbowkit/styles.css';
import 'material-symbols/rounded.css';
Important: The widget CSS must be imported before using the component to avoid styling delays and ensure proper rendering.
Component Architecture
The widget consists of two main components:
WidgetProvider
- Configuration provider (Top-level configurations)
Widget
- UI component that renders the actual bridge interface
WidgetProvider Props
<WidgetProvider
sdkConfig={{
apiKey: "your-api-key",
environment: "development",
}}
onError={(error) => {
console.error('Widget error:', error);
}}
/>
Widget Component Props
<Widget
config={{
onOpenConnectModal: openConnectModal,
solanaSigner: solanaSigner,
}}
/>
Solana Signer
For Solana support, you need to provide a solanaSigner
that implements the WidgetSolanaSigner
interface:
interface WidgetSolanaSigner {
address: string;
isConnected(): boolean;
signTransaction(transaction: Transaction): Promise<Transaction>;
signAllTransactions(transactions: Transaction[]): Promise<Transaction[]>;
}
This is typically provided by Solana wallet adapters like @solana/wallet-adapter-react
.
Error Handling
The widget provides an onError
callback to handle errors that occur during bridge operations:
<WidgetProvider
sdkConfig={{ apiKey: 'your-api-key', environment: 'production' }}
onError={(error) => {
console.error('Widget error:', error);
toast.error(error.message);
analytics.track('widget_error', { message: error.message });
}}
>
{children}
</WidgetProvider>
Basic Integration Example
import { WidgetProvider, Widget } from '@metalayer/widget';
import { METALAYER_TESTNETS, solana, solanaDevnet } from '@metalayer/viem-chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider, createConfig, http } from 'wagmi';
import type { Chain } from 'viem/chains';
import { RainbowKitProvider, ConnectButton, useConnectModal, getDefaultConfig } from '@rainbow-me/rainbowkit';
import '@metalayer/widget/styles.css';
import '@rainbow-me/rainbowkit/styles.css';
import 'material-symbols/rounded.css';
const config = getDefaultConfig({
appName: 'Metalayer Bridge Widget Example',
projectId: 'YOUR_WALLETCONNECT_PROJECT_ID',
chains: [
...METALAYER_TESTNETS as unknown as readonly [Chain, ...Chain[]],
solana,
solanaDevnet,
],
});
const queryClient = new QueryClient();
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
{/* RainbowKit provider can be replaced with your preferred wallet connector */}
<RainbowKitProvider>
<WidgetProvider
sdkConfig={{
apiKey: 'your-api-key',
environment: 'development',
}}
onError={(error) => {
console.error('Widget error:', error);
// Use your preferred toast library
// toast.error(error.message);
}}
>
<Page />
</WidgetProvider>
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
function Page() {
const { openConnectModal } = useConnectModal();
return (
<div className="app">
<header>
{/* ConnectButton can be replaced with your preferred wallet connection UI */}
<ConnectButton />
</header>
<main>
<Widget config={{ onOpenConnectModal: openConnectModal }} />
</main>
</div>
);
}
Framework-specific Examples
For implementation examples in specific frameworks, see the example directories:
Framework-specific Notes
Next.js App Router
Import CSS in your root layout.tsx
:
import '@metalayer/widget/styles.css';
Next.js Pages Router
Import CSS in your _app.tsx
:
import '@metalayer/widget/styles.css';
Compatibility
Works with any React-based application including Next.js, Create React App, Vite, Remix, and more.