Skip to main content
Socket Widget is a swap and bridge widget for moving tokens across blockchains. The @socket.tech/widget package exposes a React component you can embed in your app.
v1.0.0: The widget is wallet-provider-agnostic. Your app provides a minimal wallet adapter via config.wallet, and the widget handles routing, reads, writes, balances, receipts, and signing internally.

Before You Start

Socket Widget handles swap and bridge flows, while your app owns wallet connection.
  • Provide wallet state and wallet connect UI.
  • Mount QueryClientProvider above <SocketWidget />.
  • Pass a minimal wallet adapter via config.wallet.
  • Support EVM only, Solana only, Tron only, or any combination by providing the matching adapter methods. You only need to pass the methods for the chains you support — e.g. for EVM-only apps, provide getEVMWalletClient and switchChain; you do not need to pass getSolanaSigner or getTronWeb.
Minimal React Query setup:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

root.render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>
);

Install

pnpm install @socket.tech/widget react react-dom viem @tanstack/react-query
Peer dependencies are not installed automatically. You must already have wallet infrastructure in your app.
Import the widget styles in your app entrypoint:
import "@socket.tech/widget/styles.css";
import "@socket.tech/widget/fonts.css";

Quickstart

  1. Install the package and peer dependencies.
  2. Mount QueryClientProvider.
  3. Import the widget CSS.
  4. Add a minimal wallet adapter.
  5. Render <SocketWidget config={config} />.
Choose the smallest path that matches your app:
  • EVM only: provide accounts, getEVMWalletClient, and usually switchChain.
  • Solana only: provide accounts, getSolanaSigner, and rpcs.solana.
  • Tron: provide accounts and getTronWeb.
  • EVM + Solana: combine both adapters in the same wallet object.
  • EVM + Tron: add getTronWeb and TRON accounts for Tron-supported routes.

EVM-Only Example

import { SocketWidget, type WidgetConfig } from "@socket.tech/widget";
import { getWalletClient } from "@wagmi/core";
import { useMemo } from "react";
import { useAccount, useConfig, useSwitchChain } from "wagmi";

export function Widget() {
  const { address, chain, isConnected } = useAccount();
  const { switchChainAsync } = useSwitchChain();
  const wagmiConfig = useConfig();

  const accounts = useMemo(
    () =>
      address && isConnected
        ? [{ address, chainType: "EVM" as const, chainId: chain?.id, isConnected: true }]
        : [],
    [address, chain?.id, isConnected],
  );

  const config: WidgetConfig = {
    affiliateId: "your-affiliate-id",
    wallet: {
      accounts,
      getEVMWalletClient: async (chainId) => getWalletClient(wagmiConfig, { chainId }),
      switchChain: async (chainId) => {
        await switchChainAsync({ chainId });
        return true;
      },
    },
    eventHandlers: {
      onOpenWalletConnect: () => {
        openYourWalletModal();
      },
    },
  };

  return <SocketWidget config={config} />;
}

Solana-Only Example

import { SocketWidget, type WidgetConfig } from "@socket.tech/widget";
import { useMemo } from "react";
import { useWallet } from "@solana/wallet-adapter-react";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";

export function Widget() {
  const wallet = useWallet();
  const { setVisible } = useWalletModal();

  const accounts = useMemo(
    () =>
      wallet.publicKey
        ? [{ address: wallet.publicKey.toBase58(), chainType: "SVM" as const, isConnected: wallet.connected }]
        : [],
    [wallet.connected, wallet.publicKey],
  );

  const config: WidgetConfig = {
    affiliateId: "your-affiliate-id",
    rpcs: {
      solana: "https://api.mainnet-beta.solana.com",
    },
    wallet: {
      accounts,
      getSolanaSigner: async () => {
        if (!wallet.connected) return null;
        return {
          signAndSendTransaction: wallet.sendTransaction ?? undefined,
          signTransaction: wallet.signTransaction ?? undefined,
        };
      },
    },
    eventHandlers: {
      onOpenWalletConnect: () => setVisible(true),
    },
  };

  return <SocketWidget config={config} />;
}
To support both EVM and Solana, combine this signer with the EVM adapter shown above in the same wallet object.

Adding Tron (optional)

  • Include TRON accounts in wallet.accounts (with chainType: "TRON")
  • Provide getTronWeb returning your app’s TronWeb instance (must satisfy TronWebLike)
  • Route eventHandlers.onOpenWalletConnect("tron") to open your Tron wallet UI
If you do not pass getTronWeb, the widget works normally; Tron routes won’t be available.

Configuration Reference

WidgetConfig

Required

PropertyTypeDescription
walletWalletAdapterMinimal wallet adapter. The only mandatory property.
affiliateIdstringIntegrator tracking identifier. Required for every widget instance.

Optional

PropertyTypeDescription
apiKeystringYour Socket API key. Optional.
baseUrlstringAPI base URL. Defaults to https://backend.socket.tech/v3/swap.
themeThemeTheming configuration. See Theme Configuration.
widgetTitlestringTitle shown in the widget header. Default: "Swap".
rpcs.solanastringSolana RPC URL. Required for Solana support.
initialValuesobjectDefault chain, token, and amount values.
supportedTokensobjectRestrict available tokens by chain.
supportedChainsobjectRestrict available source/destination chains.
eventHandlersEventHandlersEvent callbacks. See Event Handlers.
feeParams.feeTakerAddressstringWallet address that receives the integrator fee.
feeParams.feeBpsnumberFee in basis points (e.g. 10 = 0.1%).
featuresobjectFeature flags. See Feature Flags.

Feature Flags

  • internalToasts (boolean): Show or hide internal toasts (default: true)
  • internalTxHistory (boolean): Show or hide transaction history (default: true)
  • internalInflight (boolean): Show or hide the inflight screen as a non-blocking toast (default: true)
  • internalTokenSelector (boolean): Render the built-in token selector, or delegate token selection to your app via eventHandlers.onOpenTokenSelector (default: true)
  • internalTokenSelectorBounds ("widget" | "viewport"): Constrain the built-in token selector to the widget or viewport (default: "widget")

Base URL and API key / affiliate requirements

The widget uses a single default backend: https://backend.socket.tech/v3/swap.
  • If you do not pass baseUrl, the widget uses the Socket backend URL above.
  • affiliateId is required in every config.
  • apiKey is optional and only sent when present.
  • You may still override baseUrl explicitly if you need to point the widget at a different compatible backend.

Wallet Adapter

The wallet object is the only host integration point the widget needs. You provide connected accounts and the network-specific wallet hooks you support; the widget handles the rest internally.
PropertyTypeRequiredDescription
accountsAccountInfo[]YesConnected wallet accounts
getEVMWalletClient(chainId: number) => Promise<WalletClient | null>EVM onlyReturns a viem WalletClient for the requested chain
getSolanaSigner() => Promise<SolanaSigner | null>Solana onlyReturns a Solana signer for signing transactions
getTronWeb() => TronWebLike | undefinedTron onlyReturns the wallet-connected TronWeb instance for Tron transactions and deposit flows
switchChain(chainId: number, account?) => Promise<boolean>Recommended for EVMSwitches the wallet’s active EVM chain

What the widget handles internally

When you provide getEVMWalletClient, the widget automatically implements:
  • sendTransaction (including EIP-5792 batched calls)
  • signTypedData
  • writeContract
  • readContract (via viem public clients)
  • getBalance (native + ERC-20)
  • getTransactionReceipt
  • getBytecode
  • getEnsAddress
  • getWalletCapabilities (EIP-5792)
  • getCallsStatus (EIP-5792)
When you provide getSolanaSigner and config.rpcs.solana, the widget additionally handles:
  • Solana transaction building and sending (VersionedTransaction with lookup tables)
  • SOL and SPL token balance fetching (including Token-2022)
  • Solana transaction receipt polling
When you provide getTronWeb, the widget additionally handles:
  • Tron transaction building, signing, and broadcasting (for deposit and direct-deposit flows)
  • TRX and TRC-20 balance and transaction receipt via the host-supplied TronWeb instance

accounts

An array of currently connected wallet accounts:
accounts: Array<{
  address: string;
  chainType: "EVM" | "SVM" | "TRON";
  chainId?: number;
  isConnected: boolean;
  connector?: { id?: string; name?: string; icon?: string };
}>

getEVMWalletClient

Returns a viem WalletClient for the requested chain. The widget uses this to send transactions, sign typed data, write contracts, and query EIP-5792 capabilities.
getEVMWalletClient: (chainId: number) => Promise<WalletClient | null>
The return type is any in the adapter interface so hosts are free to use any viem version; the widget casts internally.

getSolanaSigner

Returns a Solana signer object. The widget builds the transaction internally (instructions, lookup tables, blockhash) and passes it to the signer. If the signer provides signAndSendTransaction, the widget prefers it; otherwise it falls back to signTransaction + sendRawTransaction via its internal Connection.
getSolanaSigner: () => Promise<{
  signAndSendTransaction?: (...args: any[]) => Promise<any>;
  signTransaction?: (...args: any[]) => Promise<any>;
} | null>

getTronWeb

Returns the wallet-connected TronWeb instance from your app. The widget uses it to build, sign, and broadcast Tron transactions for deposit and direct-deposit flows. The instance must implement the minimal TronWebLike interface (see type export). If you do not support Tron, omit this property.
getTronWeb: () => TronWebLike | undefined

switchChain

Switches the wallet’s active EVM chain. The optional account parameter targets a specific wallet when multiple are connected. Treat this as a success-or-throw contract in practice:
  • resolve when the chain switch succeeds
  • reject or throw if the switch is cancelled or fails
switchChain: (
  chainId: number,
  account?: { address?: string; chainType: "EVM" | "SVM" | "TRON" }
) => Promise<boolean>

Token Configuration Details

Token Interface

interface Token {
  address: string;
  chainId: number;
  decimals: number;
  logoURI: string;
  name: string;
  symbol: string;
  isVerified?: boolean; // Must be true to avoid warnings
}

Using supportedTokens

Important notes:
  • When supportedTokens are provided, the widget will not automatically set a default token.
  • To show a default source token, you must pass initialValues.fromChain and initialValues.inputTokens.
  • Set isVerified: true for tokens in supportedTokens to avoid warnings.
  • inputTokens addresses must match tokens from your supportedTokens.from[chainId] list.

Fetching Token Data

When using supportedTokens, you can fetch token data from the Socket API endpoints:
  • /tokens/list: Returns a curated list of trending tokens (verified) or the complete token list
  • /tokens/search: Search for tokens by address, name, or symbol
Tokens fetched from these endpoints include the isVerified field. For detailed request and response formats, see the token-list API documentation.

Theme Configuration

Customize the widget appearance with the theme property:
const theme = {
  width: 420, // or "full" for full width
  borderRadius: "base", // "none" | "sm" | "base" | "md" | "lg"
  fonts: {
    primary: "Inter, sans-serif",
    secondary: "Roboto, sans-serif",
  },
  colors: {
    text: {
      primary: "#FFFFFF",
      secondary: "#A0A0A0",
      button: "#000000",
      theme: "#3B82F6",
      accent1: "#10B981",
      accent2: "#F59E0B",
      accent3: "#EF4444",
      accent4: "#8B5CF6",
    },
    bg: {
      layer1: "#1F2937",
      layer2: "#374151",
      layer3: "#4B5563",
      accent1: "#10B981",
      accent3: "#EF4444",
      accent4: "#8B5CF6",
      main: "#111827",
      theme: "#3B82F6",
    },
    border: {
      strong: "#6B7280",
      theme: "#3B82F6",
    },
    icon: {
      primary: "#FFFFFF",
      secondary: "#A0A0A0",
      theme: "#3B82F6",
    },
  },
};
Colors support both hex (#FFFFFF) and RGB (rgb(255, 255, 255)) formats.

Event Handlers

Handle user interactions and widget events:
const eventHandlers = {
  onFromTokenChange: (token: Token) => {
    console.log("Source token changed:", token);
  },
  onToTokenChange: (token: Token) => {
    console.log("Destination token changed:", token);
  },
  onFromChainChange: (chain: Chain) => {
    console.log("Source chain changed:", chain);
  },
  onToChainChange: (chain: Chain) => {
    console.log("Destination chain changed:", chain);
  },
  onEvent: (eventType: "success" | "error" | "warning", eventData: EventData) => {
    console.log("Widget event:", eventType, eventData);
  },
  onOpenWalletConnect: (network: "solana" | "eip155" | "tron") => {
    openWalletModal(network);
  },
  onSwapInitiated: (data: { chainId: number; hash: string; type: OrderFlowType }) => {
    console.log("Swap initiated:", data);
  },
  onTransactionStatusChange: (data: { requestHash: string; statusCode: SwapStatus }) => {
    console.log("Transaction status:", data);
  },
  onOpenTokenSelector: (data: TokenSelectorOpenData) => {
    openExternalTokenSelector(data);
  },
  onTokenSelectorDataChange: (data: TokenSelectorOpenData) => {
    syncExternalTokenSelector(data);
  },
  logEvent: (log: { event: Event; error: unknown; context?: Record<string, unknown> }) => {
    console.log("Analytics event:", log);
  },
  toggleExternalHistorySection: () => {
    toggleHistorySidebar();
  },
  showToast: (title, message, type, duration) => {
    // Display a custom toast notification
  },
};

Imperative API

The imperative API allows parent components to control widget state programmatically. Use case example: when a user clicks a history item in a sidebar, the parent can navigate the widget to the inflight screen for that transaction.
import { useRef } from "react";
import { SocketWidget, type WidgetImperativeAPIType } from "@socket.tech/widget";

export default function App() {
  const bungeeRef = useRef<WidgetImperativeAPIType>(null);

  const handleSetInflightData = (data: OrderData) => {
    bungeeRef.current?.setInflightData(data);
  };

  return <SocketWidget config={config} ref={bungeeRef} />;
}
Available methods:
  • setInflightData(data: OrderData): Track pending transactions and navigate to inflight screen
  • openQrDeposit(quoteId: string): Open the QR deposit screen for a quote
  • selectToken(token: Token, isSource: boolean): Programmatically select the source or destination token
  • setSourceWalletMode(mode: SourceWalletMode): Update the source wallet mode used by the widget

Type Exports

import type {
  WidgetConfig,
  WidgetImperativeAPIType,
  WalletAdapter,
  TronWebLike,
} from "@socket.tech/widget";

Troubleshooting

IssueFix
No QueryClient set errorMount QueryClientProvider above <SocketWidget />
Clicking connect does nothingImplement eventHandlers.onOpenWalletConnect
Transactions fail on wrong chainEnsure getEVMWalletClient(chainId) returns a client for the requested chain, not just the active one
Chain switch feels inconsistentMake switchChain reject or throw on cancellation, not resolve silently
Solana actions fail immediatelyMount Solana wallet-adapter providers above the widget; pass both getSolanaSigner and rpcs.solana
Tron deposit unavailablePass getTronWeb and include TRON accounts in wallet.accounts
No widget stylingImport @socket.tech/widget/styles.css and @socket.tech/widget/fonts.css