@multisender.app/multisender-react-widget
v0.2.3
Published
## What is MultisenderWidget? MultisenderWidget is a plug-and-play React component designed for seamless integration of bulk token transfers (“multisend”) into any third-party application, dashboard, or custom frontend. The widget provides almost all the
Downloads
1,073
Readme
Multisender React Widget Documentation
What is MultisenderWidget?
MultisenderWidget is a plug-and-play React component designed for seamless integration of bulk token transfers (“multisend”) into any third-party application, dashboard, or custom frontend. The widget provides almost all the core features of the main Multisender.app platform:
Sending tokens to multiple recipients in a single transaction
- Support for dozens of EVM networks
- Intuitive UI for entering, uploading, and validating addresses and amounts
- Integration with the Multisender smart contract, including real-time transaction status and error tracking
- Flexible style customization via props and CSS
You don’t need to build your own multisend logic or UI—just embed the component and configure it with your parameters.
Supported Networks
Currently, MultisenderWidget supports bulk transfers on the following networks (list is regularly updated):
- Ethereum Mainnet, Sepolia, Base, Base Sepolia
- BNB Smart Chain (BSC), opBNB
- Polygon, Polygon zkEVM
- Arbitrum, Arbitrum Nova
- Optimism, Mode, Mantle, Blast, Linea, Taiko, Scroll, Zircuit, Fraxtal
- Moonbeam, Moonriver, Aurora, Gnosis, Celo, Metis, Fantom, Avalanche, Berachain, Cronos, Telos, IOTEX, ThunderCore, HarmonyOne, Soneium, Ink, Sonic, Kaia, ApeChain, Zetachain, UniChain, Worldchain
- zkSync, zkSync Sepolia Testnet, Sei, Rootstock
If you need to add support for a custom network, please contact the Multisender.app team. Custom ChainConfig is not currently supported via widget props.
PostHog Analytics
PostHog is an analytics platform that can be integrated with MultisenderWidget to track user events (such as transaction start/completion, errors, clicks, and more). This helps you analyze user behavior and improve your interface.
How to enable:
- To enable analytics, provide the posthogKey and posthogHost props.
- These values are issued by the Multisender.app team upon request.
- If these props are not provided, analytics will be disabled, and the widget will function normally without tracking.
Installation
Install the Multisender React Widget package using yarn:
yarn add @multisender/react-widgetUsage
AppKit Initialization
Before using MultisenderWidget, you must initialize appKit with the same wagmiConfig that will be passed to the widget:
import { createConfig, http, fallback } from 'wagmi';
import { sepolia, mainnet, rootstock } from 'wagmi/chains';
import { createAppKit, WagmiAdapter, AppKitNetwork } from '@reown/appkit';
const networks: AppKitNetwork[] = [sepolia, rootstock, mainnet];
export const widgetConfig: Record<number, ChainConfig> = {
[sepolia.id]: {
id: sepolia.id,
name: sepolia.name,
blockExplorerUrl: {
tx: 'https://eth-sepolia.blockscout.com/tx/',
address: 'https://eth-sepolia.blockscout.com/address/',
},
rpcUrls: ['https://eth-sepolia.g.alchemy.com/v2/*****'],
blockScoutApiUrl: 'https://eth-sepolia.blockscout.com',
},
[mainnet.id]: {
id: mainnet.id,
name: mainnet.name,
blockExplorerUrl: {
tx: 'https://eth.blockscout.com/tx/',
address: 'https://eth.blockscout.com/address/',
},
rpcUrls: ['https://eth-mainnet.g.alchemy.com/v2/*****'],
blockScoutApiUrl: 'https://eth.blockscout.com',
},
[rootstock.id]: {
id: rootstock.id,
name: rootstock.name,
blockExplorerUrl: {
tx: 'https://rootstock.blockscout.com/tx/',
address: 'https://rootstock.blockscout.com/address/',
},
rpcUrls: ['https://rootstock-mainnet.g.alchemy.com/v2/*****'],
blockScoutApiUrl: 'https://rootstock.blockscout.com',
},
}
const projectId = 'your-project-id'; // walletConnect project id
export function initAdapters() {
const wagmiAdapter = new WagmiAdapter({
networks,
projectId,
transports: Object.values(widgetConfig).reduce<Record<number, ReturnType<typeof fallback>>>(
(acc, curr) => {
const httpProviders = (curr.rpcUrls as string[]).map((url) => http(url))
acc[curr.id] = fallback(httpProviders)
return acc
},
{},
),
})
const modalParams = {
networks,
projectId,
adapters: [wagmiAdapter],
}
const modal = createAppKit(modalParams)
return { modal, wagmiAdapter, modalParams }
}Widget Implementation
import { QueryClient } from '@tanstack/react-query';
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { MultisenderWidget } from '@multisender/react-widget';
import { widgetConfig, initAdapters } from './config'
const queryClient = new QueryClient();
const multisenderMantineTheme = createTheme({
components: {
Button: Button.extend({
defaultProps: {
opacity: 0.9,
},
}),
},
})
function App() {
const { wagmiAdapter } = initAdapters()
return (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>
<MultisenderWidget
config={widgetConfig}
posthogKey='your-posthog-key'
posthogHost="https://your-posthog-url.com"
mantineTheme={multisenderMantineTheme} // Optional Mantine theme overrides
classNames={{
theme: 'custom-theme-class',
mantineProvider: 'custom-mantine-provider-class',
}} // Optional custom CSS classes
/>
</QueryClientProvider>
</WagmiProvider>
);
}Props
| Prop | Type | Required | Description |
|----------------------|------------------------------------------------|----------|-------------------------------------------------------|
| config | Record<number, ChainConfig> | Yes | multisender widget config |
| wagmiConfig | Config | Yes | wagmi configuration (must match AppKit) |
| queryClient | QueryClient | Yes | React Query client |
| posthogKey | string | No | PostHog analytics key |
| posthogHost | string | No | PostHog host URL |
| theme | MultisenderThemeProp | No | Custom widget theme |
| mantineTheme | MantineTheme | No | Mantine theme overrides |
| mantineProviderProps | MantineProviderProps | No | Additional MantineProvider props |
| classNames | { theme?: string; mantineProvider?: string } | No | Custom CSS classes |
| logoType | LogoType | No | Variations of the Multisender.app Logo Implementation |
LogoType
type LogoType = 'default' | 'minified'The default logo refers to the full-size version displayed in the top-left corner of the widget
The minified logo is a compact text element ("Powered by Multisender.app") accompanied by a small, minimized icon, positioned in the bottom-right corner of the widget
ChainConfig
type ChainConfig = {
id: number // Chain Id
name: string // Chain Name
rpcUrls: string[] // List of RPC urls for viem Fallback Provider
blockScoutApiUrl: string // BlockScout API url for fetching users data
// This parameter is used to explicitly enforce a specific gas fee model.
// For example, although BSC (chain ID 56) supports EIP-1559, we may want to use the
// legacy gas model instead, so we set `isSupportEstimateFees: false`.
isSupportEstimateFees?: boolean
optimisticVersion?: OptimisticVersions // Optimistic version 'ecotone' / 'fjord'
blockExplorerUrl: {
tx: string // 'https://eth.blockscout.com/tx/'
address: string // 'https://eth.blockscout.com/address/'
}
}Posthog (posthogKey, posthogHost)
This parameters needs to be requested from the Multisender.app team.
Multisender theme prop
Minimal set of brand-specific properties
import { MultisenderThemeProp } from 'containers/Theme'
const multisenderTheme: MultisenderThemeProp = {
color: {
brand: 'red',
},
}
export const YourApp: FC = () => (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>
<MultisenderWidget theme={multisenderTheme} config={config} />
</QueryClientProvider>
</WagmiProvider>
)This will change Multisender brand color to 'red', painting buttons, radio buttons, some icons, and so on.
Mantine theme prop
You can add some of Mantine theme overrides
import { Button } from '@mantine/core'
const multisenderMantineTheme = createTheme({
components: {
Button: Button.extend({
defaultProps: {
opacity: 0.9,
},
}),
},
})
export const YourApp: FC = () => (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>
<MultisenderWidget
mantineTheme={multisenderMantineTheme}
config={config}
/>
</QueryClientProvider>
</WagmiProvider>
)This will add opacity={0.9} prop to every <Button> used by Multisender or Mantine other components
NOTE: By default, withCssVariables if turned off for <MantineProvider>, so any CSS-generating props of Mantine theme will not work.
For example, you want to apply a theme with font setting:
const mantineTheme = createTheme({
fontFamily: 'monospace',
})But this prop will try to generate CSS and add it to new <style> element inside Mantine Provider component. This styles is not wrapped into @layer, so they cancel some Multisender theme overrides.
You can still turn it on to apply your own theme overrides using mantineProviderProps:
export const YourApp: FC = () => (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>
<MultisenderWidget
mantineTheme={multisenderMantineTheme}
mantineProviderProps={{ withCssVariables: true }}
config={config}
/>
</QueryClientProvider>
</WagmiProvider>
)You can override any MantineProvider props.
classNames prop
You can add your own CSS classes for Multisender widget wrapper elements to override CSS Custom Properties:
import styles from 'app.module.css'
export const YourApp: FC = () => (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>
<MultisenderWidget
classNames={{
theme: styles.multisenderTheme,
mantineProvider: styles.multisenderMantineProvider,
}}
config={config}
/>
</QueryClientProvider>
</WagmiProvider>
)app.module.css:
.multisenderTheme {
/* Here you can override Multisender CSS Custom Properties, same as using 'theme' prop in <MultisenderWidget> */
--mw-color-brand: red;
}
.multisenderMantineProvider {
/* Here you can override Mantine CSS Custom Properties */
--mantine-color-green-outline: darkgreen;
}CSS Layers
If your app applies any global styles, place them into @layer global {} to avoid unexpected CSS specifity overloads.
For example, you have this CSS:
* { margin: 0; padding: 0 }All CSS used by Multisender widget is wrapped into layers, so this style will cancel any margin or padding in widget components.
You can tune your global styles to resolve any problems you see, but Multisender widget has a lot of components and states, so you can miss some regressions.
Instead, wrap these global styles into a CSS layer:
@layer global {
* { margin: 0; padding: 0 }
}This will force browser to set this style specifity to lower value than Multisender widget CSS specifity.
Multisender widget CSS is placed into layers in this order:
global– Empty layer you can use for global stylesreset– The same, but named to match some other existing appsmultisender-mantine-provider– Mantine Theme Provider stylesmultisender– Multisender widget stylesmantine– Mantine styles
Lower the layer, less specifity.
You cannot change the order of this layers in your app.
Global styles
Probably, you want to apply some CSS reset in your app.
In Mantine widget, there is no global styles are applied. If want any, import them on host app.
We recommend normalize.css from CSS Tools:
import '@csstools/normalize.css'
import '@csstools/normalize.css/opinionated.css'Dependencies
Ensure your project includes these dependencies:
{
"dependencies": {
"viem": "^2.30.0",
"wagmi": "^2.15.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@reown/appkit": "^1.6.7",
"@tanstack/react-query": "^5.67.1",
"@reown/appkit-adapter-wagmi": "^1.6.7"
}
}