@wheelx-widget/widget
An open-source, framework-agnostic React widget for cross-chain token swaps and bridges. Built-in wallet connection support (EVM + Solana), configurable theme system, and ready-to-use demo application.
Features
- Cross-chain Swap & Bridge — Full trading flow with quote polling, gas estimation, approval handling
- Built-in Wallet Connection — Dynamic SDK + Wagmi v2 supporting EVM (MetaMask, OKX, etc.) and Solana (Phantom)
- Configurable Theme — 14 color tokens for custom skinning
- Framework Agnostic — Works with any React framework (Next.js, Vite, Create React App, etc.)
- ESM + CJS — Dual-format builds with full TypeScript declarations
- Configurable Default Tokens — Set default from/to chains and tokens
- Deep Link Support — Wallet app launch support in WebView (React Native) — see Deep Link Guide
Installation
npm install @wheelx-widget/widget @chakra-ui/react @emotion/react
Note: @chakra-ui/react and @emotion/react are required peer dependencies.
Quick Start
If you don't have a project yet, scaffold one first:
npm create vite@latest my-swap-page -- --template react-ts
cd my-swap-page
Then install the widget and required peer dependencies:
npm install @wheelx-widget/widget @chakra-ui/react @emotion/react
// main.tsx — wrap with ChakraProvider
import { ChakraProvider, defaultSystem } from '@chakra-ui/react'
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(
<ChakraProvider value={defaultSystem}>
<App />
</ChakraProvider>
)
// App.tsx
// Import the CSS for animations (required!)
import '@wheelx-widget/widget/styles.css'
import { WheelXProvider, WheelXWidget } from '@wheelx-widget/widget'
function App() {
return (
<WheelXProvider>
<WheelXWidget />
</WheelXProvider>
)
}
Configuration
Theme Customization
Customize 14 visual tokens:
<WheelXProvider config={{
theme: {
defaultTextColor: '#1A1D26',
highlightTextColor: '#007B9D',
weakTextColor: '#5D6270',
defaultBgColor: '#FFFFFF',
highlightBgColor: '#E6F7FB',
assistBgColor: '#F5F6F8',
defaultBorderColor: '#E2E4E8',
highlightBorderColor: '#007B9D',
highlightButtonBg: '#007B9D',
highlightButtonText: '#FFFFFF',
normalButtonBg: '#F5F6F8',
normalButtonText: '#1A1D26',
disabledButtonBg: '#E2E4E8',
disabledButtonText: '#A0A4AE'
}
}}>
<WheelXWidget />
</WheelXProvider>
Default Chain/Token Configuration
<WheelXProvider config={{
from: {
chains: [8453, 1], // Only show Base and Ethereum as from chains
defaultChainId: 8453 // Default to Base
},
to: {
chains: [1868], // Only show Soneium as to chain
defaultChainId: 1868 // Default to Soneium
}
}}>
<WheelXWidget />
</WheelXProvider>
Custom Dynamic Environment ID
<WheelXProvider config={{
dynamicEnvironmentId: 'your-dynamic-environment-id'
}}>
<WheelXWidget />
</WheelXProvider>
Fixed Recipient Address (Different Address)
Configure fixed recipient addresses for EVM or Solana chains. When set, the widget will:
- Display the configured address in the To field instead of showing "Connect Wallet"
- Query the token balance of the configured address
- Skip wallet connection for the target chain (only the source chain wallet is required for signing)
- Show GetGas when the configured address has insufficient native token balance
This is useful for exchange or merchant scenarios where funds should always be sent to a specific address regardless of which wallet the user connects.
Priority order for recipient address:
- Configured fixed address (
evmDifferentAddress/solanaDifferentAddress) - User manually entered different address (via Different Address dialog)
- Connected wallet address
<WheelXProvider config={{
evmDifferentAddress: '0xAbCdEf1234567890...', // Fixed recipient for EVM chains (0x format)
solanaDifferentAddress: '9zQX...' // Fixed recipient for Solana chains (base58 format)
}}>
<WheelXWidget />
</WheelXProvider>
Note: You can configure one, both, or neither. The widget will only use the relevant one based on the selected To chain.
API Reference
<WheelXProvider>
The root provider component that sets up wallet connections, React Query, and theme.
| Prop | Type | Default | Description |
|---|---|---|---|
config |
WidgetConfig |
{} |
Widget configuration |
children |
React.ReactNode |
— | Child components |
<WheelXWidget>
The main trading widget component.
| Prop | Type | Default | Description |
|---|---|---|---|
config |
WidgetConfig |
{} |
Widget configuration |
WidgetConfig
interface WidgetConfig {
theme?: Partial<WidgetTheme>
from?: {
chains?: number[]
defaultChainId?: number
chainTokens?: Record<number, TokenConfig[]>
defaultToken?: { chainId: number; address: string }
}
to?: {
chains?: number[]
defaultChainId?: number
chainTokens?: Record<number, TokenConfig[]>
defaultToken?: { chainId: number; address: string }
}
routerCallback?: {
onPathnameChange?: (pathname: string) => void
onRouteLeave?: () => void
getCurrentPathname?: () => string
}
dynamicEnvironmentId?: string
onDeepLink?: (url: string) => void // See Deep Link Guide for WebView environments
/** Fixed recipient address for EVM chains (0x format). Takes priority over wallet address and manual different address */
evmDifferentAddress?: string
/** Fixed recipient address for Solana chains (base58 format). Takes priority over wallet address and manual different address */
solanaDifferentAddress?: string
}
WidgetTheme
interface WidgetTheme {
defaultTextColor: string
highlightTextColor: string
weakTextColor: string
defaultBgColor: string
highlightBgColor: string
assistBgColor: string
defaultBorderColor: string
highlightBorderColor: string
highlightButtonBg: string
highlightButtonText: string
normalButtonBg: string
normalButtonText: string
disabledButtonBg: string
disabledButtonText: string
/** Color palette for the GetGas switch component. e.g. 'purple', 'blue', 'green'. Default: 'purple' */
getGasSwitchColorPalette?: string
}
Demo
A demo https://widget.wheelx.fi
API Endpoint
This widget connects to the WheelX API at https://api.wheelx.fi. The API domain is not configurable.
React Native / WebView Integration
If you embed the Widget into a React Native WebView, additional configuration is required to properly launch wallet apps.
Please refer to: Mobile Wallet Launch Guide (DEEP_LINK_GUIDE.md)
In short, three things are required:
- Add
onMessagelistener in the WebView - Configure
<queries>declaration in Android'sAndroidManifest.xml - Configure
LSApplicationQueriesSchemesin iOS'sInfo.plist
Troubleshooting
| Problem | Symptom | Solution |
|---|---|---|
| Missing ChakraProvider | White screen or unstyled component | Wrap your app in <ChakraProvider value={defaultSystem}> — the widget depends on Chakra's context |
| Forgot to import CSS | Component works but has no styling/animations | Add import '@wheelx-widget/widget/styles.css' in your entry file |
| Vite dev server starts slow | vite dev takes 30+ seconds |
Normal — Vite pre-bundles large wallet dependencies (wagmi, viem, ethers, etc.). Subsequent starts with cache are faster |
| Build output is large | dist/ folder tens of MB, main bundle ~7.5MB (1.9MB gzipped) | Expected — the widget bundles all wallet SDKs. Use code-splitting or lazy-loading in your host app if needed |
| React hooks errors | "Invalid hook call" or context mismatch | You may have duplicate React instances. Ensure react and react-dom are peer dependencies in your project, and use a single version (18 or 19) |
License
MIT