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
- Install the package and peer dependencies.
- Mount
QueryClientProvider.
- Import the widget CSS.
- Add a minimal wallet adapter.
- 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
Required
| Property | Type | Description |
|---|
wallet | WalletAdapter | Minimal wallet adapter. The only mandatory property. |
affiliateId | string | Integrator tracking identifier. Required for every widget instance. |
Optional
| Property | Type | Description |
|---|
apiKey | string | Your Socket API key. Optional. |
baseUrl | string | API base URL. Defaults to https://backend.socket.tech/v3/swap. |
theme | Theme | Theming configuration. See Theme Configuration. |
widgetTitle | string | Title shown in the widget header. Default: "Swap". |
rpcs.solana | string | Solana RPC URL. Required for Solana support. |
initialValues | object | Default chain, token, and amount values. |
supportedTokens | object | Restrict available tokens by chain. |
supportedChains | object | Restrict available source/destination chains. |
eventHandlers | EventHandlers | Event callbacks. See Event Handlers. |
feeParams.feeTakerAddress | string | Wallet address that receives the integrator fee. |
feeParams.feeBps | number | Fee in basis points (e.g. 10 = 0.1%). |
features | object | Feature 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.
| Property | Type | Required | Description |
|---|
accounts | AccountInfo[] | Yes | Connected wallet accounts |
getEVMWalletClient | (chainId: number) => Promise<WalletClient | null> | EVM only | Returns a viem WalletClient for the requested chain |
getSolanaSigner | () => Promise<SolanaSigner | null> | Solana only | Returns a Solana signer for signing transactions |
getTronWeb | () => TronWebLike | undefined | Tron only | Returns the wallet-connected TronWeb instance for Tron transactions and deposit flows |
switchChain | (chainId: number, account?) => Promise<boolean> | Recommended for EVM | Switches the wallet’s active EVM chain |
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
| Issue | Fix |
|---|
No QueryClient set error | Mount QueryClientProvider above <SocketWidget /> |
| Clicking connect does nothing | Implement eventHandlers.onOpenWalletConnect |
| Transactions fail on wrong chain | Ensure getEVMWalletClient(chainId) returns a client for the requested chain, not just the active one |
| Chain switch feels inconsistent | Make switchChain reject or throw on cancellation, not resolve silently |
| Solana actions fail immediately | Mount Solana wallet-adapter providers above the widget; pass both getSolanaSigner and rpcs.solana |
| Tron deposit unavailable | Pass getTronWeb and include TRON accounts in wallet.accounts |
| No widget styling | Import @socket.tech/widget/styles.css and @socket.tech/widget/fonts.css |