@dzapio/widget
v0.0.1
Published
Embeddable React SDK for multi-chain swaps, bridges, and intent execution with wallets, routing, and theming
Readme
@dzapio/widget
Embeddable React SDK for multi-chain swaps, bridges, and DeFi zaps. Drop a production-ready widget into any dApp with wallet connection, route aggregation, transaction execution, and theming built in.
Built on the DZap API and @dzapio/sdk. Wallet connection is powered by @dzapio/wallet.
Try it in the live playground → — configure theme, chains, and providers, then copy the embed code.
Features
- Swap & bridge — Cross-chain token swaps with route comparison and slippage controls
- Zap — One-click liquidity and yield actions across supported DeFi protocols
- Multi-chain wallets — EVM (via Wagmi/WalletConnect), Solana, Bitcoin, and Sui out of the box
- Flexible embed — Inline widget or drawer/modal trigger buttons
- Customizable — Theme tokens, provider allowlists, hidden/disabled UI, and lifecycle callbacks
- Type-safe — Full TypeScript definitions for config, props, and events
Requirements
- Node.js 18+
- React 18+
Peer dependencies react, react-dom, viem (≥2), and wagmi (≥3) are required for EVM wallet support.
Installation
Published under the beta dist-tag — there is no latest tag yet, so the version is required:
npm install @dzapio/widget@beta
# or
pnpm add @dzapio/widget@betaImport the bundled stylesheet once in your app entry:
import '@dzapio/widget/styles.css';Vite hosts that pull in Bitcoin/wallet deps may also need a Buffer polyfill (for example vite-plugin-node-polyfills). Next.js usually does not.
Quick start
Wrap your app (or the subtree that renders the widget) with DZapWidgetProvider, then mount a widget component.
import { ConnectWalletButton, DZapWidgetProvider, SwapWidget } from '@dzapio/widget';
import '@dzapio/widget/styles.css';
// Next.js env vars shown here; on Vite use import.meta.env.VITE_WC_PROJECT_ID
// and import.meta.env.VITE_DZAP_API_KEY.
const config = {
walletConnectProjectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
dzapApiKey: process.env.NEXT_PUBLIC_DZAP_API_KEY,
appName: 'My dApp',
theme: 'dark' as const,
};
export function App() {
return (
<DZapWidgetProvider config={config}>
<ConnectWalletButton />
<SwapWidget />
</DZapWidgetProvider>
);
}Drawer trigger
Use SwapButton or ZapButton to open the widget in a drawer instead of rendering it inline:
import { DZapWidgetProvider, SwapButton } from '@dzapio/widget';
import '@dzapio/widget/styles.css';
<DZapWidgetProvider config={config}>
<SwapButton label="Open swap" />
</DZapWidgetProvider>;Configuration
DZapWidgetProvider accepts a config object. Common options:
| Option | Type | Description |
| ------------------------ | ----------------------------- | ------------------------------------------------------ |
| walletConnectProjectId | string | Required. WalletConnect Cloud project ID |
| dzapApiKey | string | DZap API key for authenticated requests |
| appName | string | Display name in wallet connection prompts |
| appUrl | string | App URL for wallet metadata |
| appIcon | string | App icon URL for wallet metadata |
| theme | 'light' \| 'dark' \| 'auto' | Base appearance mode (default: 'dark') |
| customTheme | WidgetTheme | Custom color, radius, and font tokens |
| defaultSrcChainId | number | Default source chain for swap |
| defaultDstChainId | number | Default destination chain for swap |
| defaultZapChainId | number | Default chain for zap flows |
| allowedSrcChainIds | number[] | Restrict selectable source chains (omit = all) |
| allowedDstChainIds | number[] | Restrict selectable destination chains (omit = all) |
| allowedZapChainIds | number[] | Restrict selectable zap chains (omit = all) |
| enableTestnetMode | boolean | Show the "Testnet mode" toggle in swap settings |
| solanaRpcUri | string | Override the Solana RPC endpoint |
| providers | { swap?, bridge?, zap? } | Allowlist of provider IDs |
| defaultSettings | object | Initial slippage, route priority, etc. |
| hiddenUI | HiddenUIConfig | Hide history, settings, route selector, etc. |
| disabledUI | DisabledUIConfig | Lock form fields (amount, tokens, recipient) |
| callbacks | WidgetCallbacks | Lifecycle hooks (see Events) |
| variant | 'inline' \| 'drawer' | Default layout variant |
| buildUrl | boolean | Sync widget state with URL search params |
| keyPrefix | string | Prefix for URL/localStorage keys on multi-widget pages |
| formUpdateKey | string | Change to re-apply default widget props |
| walletConfig | WalletConfig | Delegate wallet connection to the host app |
See WidgetConfig in @dzapio/widget for the full type definition, including the testnet
(defaultTestnetSrcChainId, allowedTestnetSrcChainIds, …) options.
walletConfigis{ onConnect?, useExternalWallet? }— it is not theWalletConfigtype exported by@dzapio/wallet, which carrieswalletConnectProjectIdand app metadata. Those live at the top level ofWidgetConfighere.
Exports
| Export | Description |
| -------------------------- | ---------------------------------------------------------------------- |
| DZapWidgetProvider | Root provider — config, wallet, store, and theme context |
| SwapWidget | Inline swap/bridge form |
| ZapWidget | Inline zap (liquidity/yield) form |
| SwapButton | Drawer trigger wrapping SwapWidget |
| ZapButton | Drawer trigger wrapping ZapWidget |
| ConnectWalletButton | Multi-chain wallet connect/disconnect control |
| useAccount | Connected account state across ecosystems |
| useWidgetEvents | Subscribe to widget lifecycle events |
| fetchSupportedChains | Fetch supported chain options (no provider required) |
| fetchSwapBridgeProviders | Fetch swap/bridge provider ids |
| fetchZapProviders | Fetch available zap protocol providers |
| fetchTokenList | Fetch token identity list for a chain (no balances; throws on failure) |
| fetchZapPools | Fetch zap pools for a provider/chain (throws on failure) |
| fetchZapPositions | Fetch zap positions for an account/provider (throws on failure) |
| tokenToSwapToken | Map a list token to SwapWidget defaultSrcToken / defaultDstToken |
| poolDataToZapAsset | Map a pool to ZapWidget defaultSrc / defaultDst |
| positionDataToZapAsset | Map a position to a zap asset |
| swapTokenToZapToken | Map a SwapToken to a zap token asset |
| useTokenList | Load/cache token lists inside DZapWidgetProvider |
| useZapPools | Load/cache zap pools inside DZapWidgetProvider |
| useZapPositions | Load/cache zap positions inside DZapWidgetProvider |
Imperative control
SwapButton / ZapButton forward a WidgetDrawerRef (open, close, toggle, isOpen), so the
drawer can be driven from anywhere in the host app. SwapWidget / ZapWidget forward
SwapWidgetRef / ZapWidgetRef.
import { useRef } from 'react';
import { SwapButton, type WidgetDrawerRef } from '@dzapio/widget';
function TradeLauncher() {
const drawer = useRef<WidgetDrawerRef>(null);
return (
<>
<SwapButton ref={drawer} />
<button onClick={() => drawer.current?.open()}>Trade</button>
</>
);
}Lock a token or pool
Pass identity-only defaults and lock that side. The widget loads the connected wallet’s balance; do not stamp balances onto defaultSrcToken.
Standalone fetchers (fetchTokenList, fetchZapPools, fetchZapPositions) throw on network or API failure. An empty result means the request succeeded and there is no matching data.
import {
SwapWidget,
ZapWidget,
fetchTokenList,
fetchZapPools,
poolDataToZapAsset,
tokenToSwapToken,
type SwapToken,
type ZapAsset,
} from '@dzapio/widget';
const apiKey = process.env.NEXT_PUBLIC_DZAP_API_KEY;
export async function loadLockedSrcToken(): Promise<SwapToken> {
// Keys come back from the API as-is, so match case-insensitively rather than
// indexing with a checksummed address.
const tokens = await fetchTokenList(42161, apiKey);
const target = '0xaf88d065e77c8cc2239327c5edb3a432268e5831';
const usdc = Object.values(tokens).find((t) => t.contract.toLowerCase() === target);
if (!usdc) throw new Error('USDC not in token list');
return tokenToSwapToken(usdc, 42161);
}
export async function loadLockedDstPool(): Promise<ZapAsset> {
const { pools } = await fetchZapPools({
chainId: 8453,
provider: 'uniswap-v3',
dzapApiKey: apiKey,
});
const pool = pools[0];
if (!pool) throw new Error('No pools returned');
return poolDataToZapAsset(pool);
}Feed the results in as props (both components render inside DZapWidgetProvider):
<SwapWidget lockSrc defaultSrcToken={srcToken} />
<ZapWidget lockDst defaultDst={dstPool} />useTokenList, useZapPools, and useZapPositions require a mounted DZapWidgetProvider. Standalone fetchers do not.
Events
Subscribe via useWidgetEvents or pass handlers in config.callbacks:
import { useEffect } from 'react';
import { WidgetEvent, useWidgetEvents } from '@dzapio/widget';
function RouteLogger() {
const { on } = useWidgetEvents();
useEffect(() => {
const off = on(WidgetEvent.RouteSelected, (data) => console.log('Route:', data));
return off;
}, [on]);
return null;
}useWidgetEvents() returns { on, off }; on(event, listener) returns an unsubscribe function.
The component must render inside DZapWidgetProvider.
The same payloads are delivered to config.callbacks, which uses on-prefixed keys:
const config = {
// …
callbacks: {
onRouteSelected: (data) => console.log('Route:', data),
onTxnCompleted: ({ hash }) => console.log('Done:', hash),
onSettingUpdated: ({ key, value }) => console.log(key, value),
},
};Available events: routeSelected, txnStarted, txnCompleted, txnFailed, formFieldChanged, settingUpdated.
Theming
Set a base mode with theme, then override individual tokens with customTheme:
<DZapWidgetProvider
config={{
...config,
theme: 'auto',
customTheme: {
colors: {
primary: '#6366f1',
background: '#0a0a0a',
},
radius: '12px',
},
}}
>
{/* ... */}
</DZapWidgetProvider>Related packages
| Package | Description |
| ---------------------------------------------------------------- | ------------------------------------ |
| @dzapio/wallet | Multi-chain wallet integration layer |
License
MIT
