Skip to content
TokenFlight SDK

Next.js

Live project Open in StackBlitz

Create a client component for the widget:

components/PaymentWidget.tsx
'use client';
import { useEffect, useRef, useMemo } from 'react';
import type { Config } from '@wagmi/core';
import { TokenFlightWidget } from '@tokenflight/swap';
import { WagmiWalletAdapter } from '@tokenflight/adapter-wagmi';
interface PaymentWidgetProps {
wagmiConfig: Config;
theme?: 'light' | 'dark' | 'auto';
}
export function PaymentWidget({ wagmiConfig, theme = 'dark' }: PaymentWidgetProps) {
const containerRef = useRef<HTMLDivElement>(null);
const walletAdapter = useMemo(
() => new WagmiWalletAdapter(wagmiConfig),
[wagmiConfig]
);
useEffect(() => {
if (!containerRef.current) return;
const widget = new TokenFlightWidget({
container: containerRef.current,
config: {
toToken: { chainId: 8453, address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
amount: '100',
tradeType: 'EXACT_OUTPUT',
theme,
},
walletAdapter,
callbacks: {
onSwapSuccess: (data) => {
console.log('Payment completed!', data.orderId, data.txHash);
},
onSwapError: (error) => {
console.error(`[${error.code}] ${error.message}`);
},
},
});
widget.initialize();
return () => widget.destroy();
}, [walletAdapter, theme]);
return <div ref={containerRef} style={{ minHeight: 560 }} />;
}

Use it in a server or client page:

app/payment/page.tsx
import { PaymentWidget } from '../../components/PaymentWidget';
import { wagmiConfig } from '../../lib/wagmi';
export default function PaymentPage() {
return (
<main>
<h1>Payment</h1>
<PaymentWidget wagmiConfig={wagmiConfig} />
</main>
);
}
lib/wagmi.ts
import { createConfig, http } from '@wagmi/core';
import { mainnet, base, arbitrum } from '@wagmi/core/chains';
import { injected } from 'wagmi/connectors';
export const wagmiConfig = createConfig({
chains: [mainnet, base, arbitrum],
connectors: [injected()],
transports: {
[mainnet.id]: http(),
[base.id]: http(),
[arbitrum.id]: http(),
},
});

Use next/dynamic with ssr: false:

pages/payment.tsx
import dynamic from 'next/dynamic';
const PaymentWidget = dynamic(
() => import('../components/PaymentWidget').then((mod) => mod.PaymentWidget),
{ ssr: false, loading: () => <div style={{ minHeight: 560 }}>Loading...</div> }
);
export default function PaymentPage() {
return (
<main>
<h1>Payment</h1>
<PaymentWidget wagmiConfig={wagmiConfig} />
</main>
);
}

If your Next.js app already manages wallet connections (via RainbowKit, ConnectKit, etc.), use the widget without an adapter:

'use client';
import { useEffect, useRef } from 'react';
import { TokenFlightWidget } from '@tokenflight/swap';
export function PaymentWidget() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const widget = new TokenFlightWidget({
container: containerRef.current,
config: {
toToken: { chainId: 8453, address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
amount: '100',
tradeType: 'EXACT_OUTPUT',
theme: 'dark',
},
callbacks: {
onConnectWallet: () => {
// Trigger your existing wallet connection UI
document.querySelector('[data-connect-wallet]')?.click();
},
onSwapSuccess: (data) => {
console.log('Payment completed:', data);
},
},
});
widget.initialize();
return () => widget.destroy();
}, []);
return <div ref={containerRef} style={{ minHeight: 560 }} />;
}
  • Always use 'use client' for components that reference @tokenflight/swap
  • Use useRef for the container — avoids React re-render conflicts with Shadow DOM
  • Cache walletAdapter with useMemo — prevents reconnection on re-renders
  • Call destroy() in the useEffect cleanup — cleans up the Shadow DOM on unmount
  • See React Integration for more patterns and TypeScript setup