@shapeshiftoss/swap-widget
v0.5.2
Published
Embeddable swap widget using ShapeShift API
Downloads
523
Readme
@shapeshiftoss/swap-widget
An embeddable React widget that enables multi-chain token swaps using ShapeShift's aggregation API. Drop it into your app to offer EVM, UTXO, and Solana swaps with minimal configuration.
This README is the canonical reference for the swap widget. Other docs (including the ShapeShift Public API docs) link here.
Table of Contents
- Installation
- Quick Start
- Wallet Connection
- Props Reference
- Filtering Chains and Assets
- Theming
- Examples
- Exported Types
- Exported Utilities
- Exported Hooks
- Supported Chains
- Supported Swappers
- Partner Codes & Affiliate Revenue
- Notes and Limitations
Installation
npm install @shapeshiftoss/swap-widgetPeer Dependencies
The widget relies on React, wagmi/viem, React Query, and Reown AppKit (used internally for wallet connection). The widget initializes AppKit with the EVM, Bitcoin, and Solana adapters at load, so all of these peers are required — install them alongside the package:
npm install react react-dom \
wagmi @wagmi/core viem \
@tanstack/react-query \
@reown/appkit @reown/appkit-adapter-wagmi \
@reown/appkit-adapter-bitcoin @reown/appkit-adapter-solana \
@solana/wallet-adapter-wallets @solana/web3.js- React 18 or 19 is supported (
^18.0.0 || ^19.0.0).
Import the stylesheet
The widget ships a stylesheet that must be imported once for it to render correctly:
import '@shapeshiftoss/swap-widget/style.css'Quick Start
import '@shapeshiftoss/swap-widget/style.css'
import { SwapWidget } from '@shapeshiftoss/swap-widget'
function App() {
return (
<SwapWidget
// Initializes the built-in wallet connection. Optional if your app already
// initializes Reown AppKit — see "Wallet Connection" below.
walletConnectProjectId="your-walletconnect-project-id"
// Optional: attribute swaps to your affiliate account.
partnerCode="your-partner-code"
theme="dark"
onSwapSuccess={txHash => console.log('Success:', txHash)}
onSwapError={error => console.error('Error:', error)}
/>
)
}Wallet Connection
The widget connects wallets through Reown AppKit
and provides the WagmiProvider / QueryClient it needs — you don't wrap it in your own.
Initializing AppKit
The widget renders nothing until AppKit is initialized. There are two ways to satisfy this:
Let the widget initialize AppKit (default). Pass
walletConnectProjectIdand the widget creates and owns its own AppKit instance. Get a free project ID at https://dashboard.reown.com.<SwapWidget walletConnectProjectId="your-walletconnect-project-id" />Reuse your app's existing AppKit. If your host app already calls
createAppKit()(with a wagmi EVM adapter), omitwalletConnectProjectId. The widget detects the shared AppKit singleton, reads the wagmi config off it, and provides its ownWagmiProvider/QueryClientfrom that config — you wrap the widget in no providers of your own. Pair this withshowConnectButton={false}to drive connection entirely from your own UI. Seesrc/demo/ExternalWalletApp.tsxfor a full host example.Two requirements for this mode:
- Initialize AppKit before the widget mounts. The widget reads the AppKit singleton when it mounts and does not poll for late initialization — if AppKit isn't up yet, the widget renders nothing.
- Dedupe the AppKit/wagmi packages.
@reown/appkit*,wagmi, andviemmust resolve to a single shared copy in your app, so the widget and your app share one AppKit instance and one wagmi state. A duplicated copy means the widget reads an empty store and shows no connection.
The header shows a built-in Connect button by default; set showConnectButton={false} to hide it
and drive connection from your own UI.
Supported wallet namespaces
Once connected, the widget can sign and broadcast transactions for three wallet namespaces:
| Namespace | Chains | Example wallets |
| --------- | ------------------------------- | -------------------------------- |
| eip155 | All supported EVM chains | MetaMask, WalletConnect, Rabby |
| bip122 | Bitcoin and other UTXO chains | WalletConnect-compatible wallets |
| solana | Solana | Phantom, Solflare |
The header shows a Connect button by default (toggle with showConnectButton) that opens the
AppKit modal. Swaps whose sell asset is not in an executable namespace (see
Supported Chains) redirect to app.shapeshift.com
when allowShapeshiftRedirect is enabled.
Props Reference
SwapWidgetProps
| Prop | Type | Default | Description |
| ------------------------ | ----------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------- |
| walletConnectProjectId | string | – | Reown AppKit / WalletConnect project ID. The widget uses it to initialize AppKit. Required unless your host app already initializes AppKit (see Wallet Connection). |
| partnerCode | string | – | Your registered partner code for affiliate fee attribution. See Partner Codes. |
| apiBaseUrl | string | https://api.shapeshift.com | Override the API base URL. Useful for testing or custom deployments. |
| defaultSellAsset | Asset | ETH on Ethereum | Initial asset to sell. |
| defaultBuyAsset | Asset | USDC on Ethereum | Initial asset to buy. |
| sellFilters | SwapWidgetFilters | {} | Restrict which chains/assets are selectable for the sell side. See Filtering. |
| buyFilters | SwapWidgetFilters | {} | Restrict which chains/assets are selectable for the buy side. |
| allowedSwapperNames | SwapperName[] | all enabled | Limit quotes to specific swappers. See Supported Swappers. |
| allowShapeshiftRedirect| boolean | true | When a swap isn't executable in-widget, redirect to app.shapeshift.com instead of hiding it. |
| isBuyAssetLocked | boolean | false | Prevent the user from changing the buy asset. |
| theme | ThemeMode \| ThemeConfig | "dark" | Theme mode ("light" or "dark") or a full theme configuration object. See Theming. |
| defaultSlippage | string | "0.5" | Default slippage tolerance, as a percentage string. |
| showPoweredBy | boolean | true | Show the "Powered by ShapeShift" footer. |
| showConnectButton | boolean | true | Show the built-in Connect button in the widget header. |
| ratesRefetchInterval | number | 15000 | How often (ms) to refetch swap rates. |
| onSwapSuccess | (txHash: string) => void | – | Called when a swap transaction succeeds. |
| onSwapError | (error: Error) => void | – | Called when a swap transaction fails. |
Filtering Chains and Assets
Restrict the sell and/or buy asset selectors independently via the sellFilters and buyFilters
props. Both accept the same shape:
type SwapWidgetFilters = {
allowedChainIds?: ChainId[] // If set, only these chains are selectable
disabledChainIds?: ChainId[] // Hide these chains
allowedAssetIds?: AssetId[] // If set, only these assets are selectable
disabledAssetIds?: AssetId[] // Hide these assets
}import { EVM_CHAIN_IDS, SwapWidget } from '@shapeshiftoss/swap-widget'
function App() {
return (
<SwapWidget
walletConnectProjectId="your-walletconnect-project-id"
partnerCode="your-partner-code"
// Only allow selling ETH-chain, Polygon, and Arbitrum assets
sellFilters={{
allowedChainIds: [EVM_CHAIN_IDS.ethereum, EVM_CHAIN_IDS.polygon, EVM_CHAIN_IDS.arbitrum],
}}
// Hide a specific buy token
buyFilters={{
disabledAssetIds: ['eip155:1/erc20:0x...'],
}}
theme="dark"
/>
)
}Theming
The widget supports a simple light/dark mode or a full theme configuration object.
Simple theme mode
<SwapWidget walletConnectProjectId="..." theme="dark" />
// or
<SwapWidget walletConnectProjectId="..." theme="light" />Custom theme configuration
import { SwapWidget } from '@shapeshiftoss/swap-widget'
import type { ThemeConfig } from '@shapeshiftoss/swap-widget'
const customTheme: ThemeConfig = {
mode: 'dark', // required
accentColor: '#3861fb',
backgroundColor: '#0a0a14',
cardColor: '#12121c',
textColor: '#ffffff',
borderRadius: '12px',
fontFamily: 'Inter, sans-serif',
buttonVariant: 'filled',
}
function App() {
return <SwapWidget walletConnectProjectId="..." theme={customTheme} />
}ThemeConfig properties
| Property | Type | Description |
| -------------------- | ------------------------ | ---------------------------------------------------- |
| mode | "light" \| "dark" | Base theme mode. Required. |
| accentColor | string | Primary accent color (buttons, focus states). |
| backgroundColor | string | Widget background color. |
| cardColor | string | Card / panel background color. |
| textColor | string | Primary text color. |
| secondaryTextColor | string | Secondary text color. |
| mutedTextColor | string | Muted/tertiary text color. |
| inputColor | string | Input field background color. |
| hoverColor | string | Hover background color. |
| borderColor | string | Border color. |
| borderRadius | string | Base border radius for UI elements (e.g. "12px"). |
| fontFamily | string | Font family for the widget. |
| buttonVariant | "filled" \| "outline" | Primary button style. |
Examples
Basic usage
import { SwapWidget } from '@shapeshiftoss/swap-widget'
function App() {
return <SwapWidget walletConnectProjectId="..." partnerCode="your-partner-code" theme="dark" />
}Custom default assets
import { SwapWidget } from '@shapeshiftoss/swap-widget'
import type { Asset } from '@shapeshiftoss/swap-widget'
const defaultSellAsset: Asset = {
assetId: 'eip155:137/slip44:966',
chainId: 'eip155:137',
symbol: 'POL',
name: 'Polygon',
precision: 18,
}
const defaultBuyAsset: Asset = {
assetId: 'eip155:137/erc20:0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
chainId: 'eip155:137',
symbol: 'USDC',
name: 'USD Coin',
precision: 6,
}
function App() {
return (
<SwapWidget
walletConnectProjectId="..."
partnerCode="your-partner-code"
defaultSellAsset={defaultSellAsset}
defaultBuyAsset={defaultBuyAsset}
theme="dark"
/>
)
}Locking the buy asset
Use isBuyAssetLocked so users can only change the sell side — useful when you want all swaps to
end in a specific token.
<SwapWidget
walletConnectProjectId="..."
partnerCode="your-partner-code"
defaultBuyAsset={defaultBuyAsset}
isBuyAssetLocked
theme="dark"
/>Exported Types
import type {
Asset,
AssetId,
Chain,
ChainId,
SwapWidgetFilters,
SwapWidgetProps,
ThemeConfig,
ThemeMode,
TradeQuote,
TradeRate,
} from '@shapeshiftoss/swap-widget'SwapperName is exported as a runtime value (an enum) — import it from the value position, not as
a type.
Asset
type Asset = {
assetId: AssetId // CAIP-19, e.g. "eip155:1/slip44:60"
chainId: ChainId // CAIP-2, e.g. "eip155:1"
symbol: string // e.g. "ETH"
name: string // e.g. "Ethereum"
precision: number // e.g. 18
icon?: string
color?: string
networkName?: string
networkIcon?: string
explorer?: string
explorerTxLink?: string
explorerAddressLink?: string
relatedAssetKey?: AssetId | null
}Exported Utilities
import {
COSMOS_CHAIN_IDS,
EVM_CHAIN_IDS,
OTHER_CHAIN_IDS,
REDIRECT_ONLY_CHAIN_IDS,
SwapperName,
UTXO_CHAIN_IDS,
formatAmount,
getBaseAsset,
getChainColor,
getChainIcon,
getChainName,
getChainType,
getEvmNetworkId,
getExplorerTxLink,
isEvmChainId,
isWidgetExecutableChainId,
isWidgetSupportedChainId,
parseAmount,
truncateAddress,
} from '@shapeshiftoss/swap-widget'Chain helpers
| Function | Signature | Description |
| --------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------ |
| isEvmChainId | (chainId: string) => boolean | Whether a chain ID is an EVM chain. |
| getEvmNetworkId | (chainId: string) => number | Extract the numeric network ID from a CAIP-2 EVM chain ID. |
| getChainType | (chainId: string) => "evm" \| "utxo" \| "cosmos" \| "solana" \| "other" | Classify a chain by namespace. |
| isWidgetSupportedChainId | (chainId: string) => boolean | Whether the widget lists assets on this chain. |
| isWidgetExecutableChainId | (chainId: string) => boolean | Whether the widget can sign/execute swaps on this chain in-app. |
| getChainName | (chainId: ChainId) => string | Display name for a chain. |
| getChainIcon | (chainId: ChainId) => string \| undefined | Icon URL for a chain. |
| getChainColor | (chainId: ChainId) => string | Brand color for a chain. |
| getBaseAsset | (chainId: ChainId) => Asset \| undefined | Native asset for a chain. |
| getExplorerTxLink | (chainId: ChainId) => string \| undefined | Block-explorer transaction link template. |
Amount and address formatting
| Function | Signature | Description |
| ----------------- | -------------------------------------------------------------------- | ---------------------------------------------------- |
| formatAmount | (amount: string, decimals: number, maxDecimals?: number) => string | Format a base-unit amount for display. |
| parseAmount | (amount: string, decimals: number) => string | Parse a human-readable amount into base units. |
| truncateAddress | (address: string, chars?: number) => string | Truncate an address (e.g. 0x1234...5678). |
Chain ID constants
const EVM_CHAIN_IDS = {
ethereum: 'eip155:1',
arbitrum: 'eip155:42161',
optimism: 'eip155:10',
polygon: 'eip155:137',
base: 'eip155:8453',
avalanche: 'eip155:43114',
bsc: 'eip155:56',
gnosis: 'eip155:100',
monad: 'eip155:143',
megaEth: 'eip155:4326',
hyperEvm: 'eip155:999',
plasma: 'eip155:9745',
katana: 'eip155:747474',
}
const UTXO_CHAIN_IDS = {
bitcoin: 'bip122:000000000019d6689c085ae165831e93',
bitcoinCash: 'bip122:000000000000000000651ef99cb9fcbe',
dogecoin: 'bip122:00000000001a91e3dace36e2be3bf030',
litecoin: 'bip122:12a765e31ffd4059bada1e25190f6e98',
}
const COSMOS_CHAIN_IDS = {
cosmos: 'cosmos:cosmoshub-4',
thorchain: 'cosmos:thorchain-1',
mayachain: 'cosmos:mayachain-mainnet-v1',
}
const OTHER_CHAIN_IDS = {
solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
}
// Listed in the asset selector, but swaps redirect to app.shapeshift.com (not executed in-widget)
const REDIRECT_ONLY_CHAIN_IDS = {
zcash: 'bip122:00040fe8ec8471911baa1db1266ea15d',
tron: 'tron:0x2b6653dc',
sui: 'sui:35834a8a',
ton: 'ton:mainnet',
near: 'near:mainnet',
starknet: 'starknet:SN_MAIN',
}Exported Hooks
import {
useAssetById,
useAssetSearch,
useAssets,
useAssetsByChainId,
useChains,
} from '@shapeshiftoss/swap-widget'| Hook | Description |
| --------------------------------- | -------------------------------------------------------------- |
| useAssets() | Fetch all available assets. |
| useAssetById(assetId) | Fetch a single asset by ID. |
| useChains() | Fetch all available chains with their native assets. |
| useAssetsByChainId(chainId) | Fetch all assets for a specific chain. |
| useAssetSearch(query, chainId?) | Search assets by symbol or name, optionally filtered by chain. |
These hooks must be used within a mounted <SwapWidget /> tree (they rely on the widget's internal
React Query client).
Supported Chains
Assets on the following chains appear in the selector. Swaps are executed in-widget only for EVM,
UTXO, and Solana assets (isWidgetExecutableChainId returns true). Cosmos-SDK and redirect-only
chains are selectable but route the user to app.shapeshift.com to
complete the swap (when allowShapeshiftRedirect is enabled).
| Chain | Chain ID | Type | Executable in-widget |
| ----------------- | ----------------------------------------- | ------ | -------------------- |
| Ethereum | eip155:1 | EVM | ✅ |
| Arbitrum One | eip155:42161 | EVM | ✅ |
| Optimism | eip155:10 | EVM | ✅ |
| Polygon | eip155:137 | EVM | ✅ |
| Base | eip155:8453 | EVM | ✅ |
| Avalanche C-Chain | eip155:43114 | EVM | ✅ |
| BNB Smart Chain | eip155:56 | EVM | ✅ |
| Gnosis | eip155:100 | EVM | ✅ |
| Monad | eip155:143 | EVM | ✅ |
| MegaETH | eip155:4326 | EVM | ✅ |
| HyperEVM | eip155:999 | EVM | ✅ |
| Plasma | eip155:9745 | EVM | ✅ |
| Katana | eip155:747474 | EVM | ✅ |
| Bitcoin | bip122:000000000019d6689c085ae165831e93 | UTXO | ✅ |
| Bitcoin Cash | bip122:000000000000000000651ef99cb9fcbe | UTXO | ✅ |
| Dogecoin | bip122:00000000001a91e3dace36e2be3bf030 | UTXO | ✅ |
| Litecoin | bip122:12a765e31ffd4059bada1e25190f6e98 | UTXO | ✅ |
| Solana | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | Solana | ✅ |
| Cosmos Hub | cosmos:cosmoshub-4 | Cosmos | ↗ redirect |
| THORChain | cosmos:thorchain-1 | Cosmos | ↗ redirect |
| MAYAChain | cosmos:mayachain-mainnet-v1 | Cosmos | ↗ redirect |
| Zcash, Tron, Sui, TON, NEAR, Starknet | see REDIRECT_ONLY_CHAIN_IDS | Other | ↗ redirect |
Supported Swappers
The widget aggregates quotes across the protocols below and surfaces the best rate. Use
allowedSwapperNames to restrict which are used.
- NEAR Intents (
SwapperName.NearIntents) - Relay (
SwapperName.Relay) - THORChain (
SwapperName.Thorchain) - MAYAChain (
SwapperName.Mayachain)
The set of enabled swappers changes over time. Treat this list as current-at-publish; the authoritative source is the
SwapperNameenum exported by this package.
Partner Codes & Affiliate Revenue
Pass your registered partnerCode to attribute swaps to your affiliate account and earn revenue
share. The widget forwards it to the ShapeShift Public API as the X-Partner-Code header, and the
API applies your configured fee automatically.
<SwapWidget walletConnectProjectId="..." partnerCode="your-partner-code" />See the Affiliate Program guide for how to obtain a partner code and how revenue attribution works.
Notes and Limitations
- Self-contained providers. The widget renders its own
WagmiProviderand React QueryQueryClient. Don't wrap it in your own — and remember it renders nothing until AppKit is initialized, whether bywalletConnectProjectIdor by your host app (see Wallet Connection). - Balances and USD prices. When a wallet is connected, the widget shows balances and USD prices for the selected assets.
- Redirects. Assets on non-executable chains (Cosmos, Zcash, Tron, Sui, TON, NEAR, Starknet)
send the user to app.shapeshift.com to finish the swap, unless
allowShapeshiftRedirect={false}. - Mobile responsive. The widget is designed to work on mobile as well as desktop.
