Skip to content

React Integration ​

Use the imperative TokenFlightWidget class in React. It works well with useEffect, cleanup, callbacks, and wallet adapters.

Should you read this? ​

Read this page if your app uses React or Next.js and you need a production pattern.

If you only need a plain HTML example, start with Getting Started. If you are still choosing between APIs, read Choose your API.

Why React should use the class ​

React can render custom elements, but React 18 and older turn object props and function props into HTML attributes. That means config objects, callbacks, and wallet adapters can be lost.

The class avoids that problem:

  • Create the widget in useEffect.
  • Pass real objects and functions.
  • Destroy the widget during cleanup.
  • Recreate it when reactive config changes.

Basic component ​

This renders the widget and callbacks. Add a wallet adapter before users make crypto payments.

tsx
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: {
        theme: 'dark',
        toToken: {
          chainId: 8453,
          address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
        },
        tradeType: 'EXACT_OUTPUT',
        amount: '100',
      },
      callbacks: {
        onDepositSuccess: (data) => {
          console.log('Payment complete:', data.orderId);
        },
        onDepositError: (error) => {
          console.error(`[${error.code}] ${error.message}`);
        },
      },
    });

    widget.initialize();
    return () => widget.destroy();
  }, []);

  return <div ref={containerRef} style={{ minHeight: 560 }} />;
}

With a wallet adapter ​

Cache the adapter with useMemo. This prevents wallet reconnection on every render.

tsx
import { useEffect, useMemo, useRef } from 'react';
import { TokenFlightWidget } from '@tokenflight/swap';
import { WagmiWalletAdapter } from '@tokenflight/adapter-wagmi';
import type { Config } from '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: {
        theme,
        toToken: {
          chainId: 8453,
          address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
        },
        tradeType: 'EXACT_OUTPUT',
        amount: '100',
      },
      walletAdapter,
      callbacks: {
        onDepositSuccess: (data) => {
          console.log('Payment complete:', data.orderId);
        },
        onDepositError: (error) => {
          console.error(`[${error.code}] ${error.message}`);
        },
      },
    });

    widget.initialize();
    return () => {
      widget.destroy();
      walletAdapter.destroy?.();
    };
  }, [theme, walletAdapter]);

  return <div ref={containerRef} style={{ minHeight: 560 }} />;
}

When config changes ​

initialize() runs once. If theme, amount, toToken, or the wallet adapter changes, include those values in the dependency list. React will run cleanup, destroy the old widget, and create a new one.

Do not mutate the widget config after initialize(). Recreate the widget instead.

TypeScript JSX support ​

If you still render <tokenflight-widget> in JSX, add the type import in a declaration file so it is not emitted as runtime JavaScript:

ts
import '@tokenflight/swap/custom-elements';

This adds typed custom element attributes. It does not register the element at runtime and does not change the runtime recommendation above.

Next step ​