RainbowKit
The best way to connect a wallet đ
Features
- đĽ Out-of-the-box wallet management
- đ Easily customizable
- đ Lightweight
- â
Accessible
- đŚ Built on top of wagmi and ethers
Installation
Install RainbowKit along with wagmi and its ethers peer dependency.
npm install @rainbow-me/rainbowkit wagmi ethers
Getting started
To start, import RainbowKitâs base styles, configure your wallets and desired chains, generate the required connectors, then wrap your application with RainbowKitProvider
and WagmiProvider
.
import '@rainbow-me/rainbowkit/styles.css';
import {
RainbowKitProvider,
Chain,
getDefaultWallets,
connectorsForWallets,
} from '@rainbow-me/rainbowkit';
import { WagmiProvider, chain } from 'wagmi';
import { providers } from 'ethers';
const infuraId = process.env.INFURA_ID;
const provider = ({ chainId }) =>
new providers.InfuraProvider(chainId, infuraId);
const chains: Chain[] = [
{ ...chain.mainnet, name: 'Ethereum' },
{ ...chain.polygonMainnet, name: 'Polygon' },
{ ...chain.optimism, name: 'Optimism' },
{ ...chain.arbitrumOne, name: 'Arbitrum' },
];
const wallets = getDefaultWallets({
chains,
infuraId,
appName: 'My RainbowKit App',
jsonRpcUrl: ({ chainId }) =>
chains.find(x => x.id === chainId)?.rpcUrls?.[0] ??
chain.mainnet.rpcUrls[0],
});
const connectors = connectorsForWallets(wallets);
const App = () => {
return (
<RainbowKitProvider chains={chains}>
<WagmiProvider autoConnect connectors={connectors} provider={provider}>
<YourApp />
</WagmiProvider>
</RainbowKitProvider>
);
};
Then, in your app, import RainbowKitâs ConnectButton
component.
import { ConnectButton } from '@rainbow-me/rainbowkit';
export const YourApp = () => {
return (
<>
<ConnectButton />
</>
);
};
Youâre done! RainbowKit will now handle your userâs wallet selection, display wallet/transaction information and handle network/wallet switching.
Customizing ConnectButton
The ConnectButton
component exposes several props to customize its appearance by toggling the visibility of different elements.
These props can also be defined in a responsive format, e.g. showBalance={{ smallScreen: false, largeScreen: true }}
, allowing you to customize its appearance across different screen sizes. Note that the built-in "largeScreen"
breakpoint is 768px
.
Prop | Type | Default | Description |
---|
accountStatus | "avatar" | "address" | "full" | { smallScreen: AccountStatus, largeScreen?: AccountStatus } | "full" | Whether the active accountâs avatar and/or address is displayed |
chainStatus | "icon" | "name" | "full" | "none" | { smallScreen: ChainStatus, largeScreen?: ChainStatus } | { smallScreen: "icon", largeScreen: "full" } | Whether the current chainâs icon and/or name is displayed, or hidden entirely |
showBalance | boolean | { smallScreen: boolean, largeScreen?: boolean } | { smallScreen: false, largeScreen: true } | Whether the balance is visible next to the account name |
Choosing a theme
RainbowKit ships with a static CSS file that can be themed via CSS variables, which RainbowKitProvider
provides as inline styles by default.
Built-in themes
There are 3 built-in themes:
lightTheme
(default)darkTheme
midnightTheme
These themes are implemented as functions where the resulting theme object can be passed to the theme
prop on RainbowKitProvider
.
import { RainbowKitProvider, darkTheme } from '@rainbow-me/rainbowkit';
const App = () => {
return (
<RainbowKitProvider theme={darkTheme()} {...etc}>
{/* ... */}
</RainbowKitProvider>
);
};
Customizing the built-in themes
The built-in theme functions also accept an options object, allowing you to select from several different visual styles.
Option | Type | Default | Description |
---|
accentColor | "blue" | "green" | "pink" | "purple" | "blue" | The background/text color of various interactive elements |
borderRadius | "none" | "small" | "medium" | "large" | "large" | The size of the entire border radius scale |
For example, to customize the dark theme with a purple
accent color and a medium
border radius scale:
import { RainbowKitProvider, darkTheme } from '@rainbow-me/rainbowkit';
const App = () => {
return (
<RainbowKitProvider
theme={darkTheme({
accentColor: 'purple',
borderRadius: 'medium',
})}
{...etc}
>
{/* ... */}
</RainbowKitProvider>
);
};
Dark mode support
If your app uses the standard prefers-color-mode: dark
media query to swap between light and dark modes, you can optionally provide a dynamic theme object containing lightMode
and darkMode
values.
import {
RainbowKitProvider,
lightTheme,
darkTheme,
} from '@rainbow-me/rainbowkit';
const App = () => {
return (
<RainbowKitProvider
theme={{
lightMode: lightTheme(),
darkMode: darkTheme(),
}}
{...etc}
>
{/* ... */}
</RainbowKitProvider>
);
};
Customizing chains
The chains
prop on RainbowKitProvider
defines which chains are available for the user to select.
Your chain config can be defined in a single array using RainbowKit's Chain
type, which is a combination of wagmiâs Chain
type and the chain metadata used by RainbowKit.
import { RainbowKitProvider, Chain } from '@rainbow-me/rainbowkit';
import { chain } from 'wagmi';
const chains: Chain[] = [
{ ...chain.mainnet, name: 'Ethereum' },
{ ...chain.polygonMainnet, name: 'Polygon' },
];
const App = () => {
return (
<RainbowKitProvider chains={chains} {...etc}>
{/* ... */}
</RainbowKitProvider>
);
};
Several chain icons are provided by default, but you can customize the icon for each chain using the iconUrl
property.
const chains: Chain[] = [
{
...chain.mainnet,
name: 'Ethereum',
iconUrl: 'https://example.com/icons/ethereum.png',
},
{
...chain.polygonMainnet,
name: 'Polygon',
iconUrl: 'https://example.com/icons/polygon.png',
},
];
Advanced usage
Creating a custom ConnectButton
If you want to create your own custom connection buttons, the low-level ConnectButton.Custom
component is also provided which accepts a render prop, i.e. a function as a child. This function is passed everything needed to re-implement the built-in buttons.
A minimal re-implementation of the built-in buttons would look something like this:
import { ConnectButton } from '@rainbow-me/rainbowkit';
export const YourApp = () => {
return (
<>
<ConnectButton.Custom>
{({
account,
chain,
openAccountModal,
openChainModal,
openConnectModal,
}) =>
!account ? (
<button onClick={openConnectModal} type="button">
Connect Wallet
</button>
) : (
<div style={{ display: 'flex', gap: 12 }}>
{chain && (
<button
onClick={openChainModal}
style={{ display: 'flex', alignItems: 'center' }}
type="button"
>
{chain.iconUrl && (
<img
alt={chain.name ?? 'Chain icon'}
src={chain.iconUrl}
style={{ width: 12, height: 12, marginRight: 4 }}
/>
)}
{chain.name ?? chain.id}
{chain.unsupported && ' (unsupported)'}
</button>
)}
<button onClick={openAccountModal} type="button">
{account.displayName}
{account.displayBalance ? ` (${account.displayBalance})` : ''}
</button>
</div>
)
}
</ConnectButton.Custom>
</>
);
};
The following props are passed to your render function.
Account properties
Property | Type | Description |
---|
account | object | undefined | Object containing details about the current account, described below |
account.address | string | The full account address, e.g. "0x7a3d05c70581bD345fe117c06e45f9669205384f" |
account.balanceDecimals | number | undefined | The account balance in decimals |
account.balanceFormatted | string | undefined | The account balance formatted as a string, e.g. "1.234567890123456789" |
account.balanceSymbol | string | undefined | The currency symbol for the balance, e.g. "ETH" |
account.displayBalance | string | undefined | The balance formatted to 3 significant digits, plus the symbol, e.g. "1.23 ETH" |
account.displayName | string | The ENS name, or a truncated version of the address, e.g.
"rainbowwallet.eth" or "0x7a3d...384f" |
account.ensAvatar | string | undefined | The ENS avatar URI |
account.ensName | string | undefined | The ENS name, e.g. "rainbowwallet.eth" |
Chain properties
Property | Type | Description |
---|
chain | object | undefined | Object containing details about the current chain, described below |
chain.iconUrl | string | undefined | The chain icon URL |
chain.id | number | The chain ID, e.g. 1 |
chain.name | string | undefined | The chain name, e.g. "Ethereum" |
chain.unsupported | boolean | undefined | Boolean indicating whether the current chain is unsupported |
Modal state properties
Property | Type | Description |
---|
openAccountModal | () => void | Function to open the account modal |
openChainModal | () => void | Function to open the chain modal |
openConnectModal | () => void | Function to open the connect modal |
accountModalOpen | boolean | Boolean indicating whether the account modal is open |
chainModalOpen | boolean | Boolean indicating whether the chain modal is open |
connectModalOpen | boolean | Boolean indicating whether the connect modal is open |
Customizing the wallet list
The following wallet options are presented by default via the getDefaultWallets
function:
- Rainbow
- WalletConnect
- Coinbase Wallet
- MetaMask
An "Injected Wallet" fallback is also provided if window.ethereum
exists and hasnât been provided by another wallet.
All built-in wallets are available via the wallet
object which allows you to rearrange/omit wallets as needed.
import { wallet, Wallet } from '@rainbow-me/rainbowkit';
const needsInjectedWalletFallback =
typeof window !== 'undefined' &&
window.ethereum &&
!window.ethereum.isMetaMask &&
!window.ethereum.isCoinbaseWallet;
const wallets: Wallet[] = [
wallet.rainbow({ chains, infuraId }),
wallet.walletConnect({ chains, infuraId }),
wallet.coinbase({
chains,
appName: 'My RainbowKit App',
jsonRpcUrl: ({ chainId }) =>
chains.find(x => x.id === chainId)?.rpcUrls?.[0] ??
chain.mainnet.rpcUrls[0],
}),
wallet.metaMask({ chains, infuraId }),
...(needsInjectedWalletFallback
? [wallet.injected({ chains, infuraId })]
: []),
];
Creating custom wallets
â ď¸ Note: This API is unstable and likely to change in the near future. We will be adding more built-in wallets over time, so let us know if there are any particular wallets youâre interested in.
The Wallet
type is provided to help you define your own custom wallets.
import { Wallet, getDefaultWallets } from '@rainbow-me/rainbowkit';
const myCustomWallet: Wallet = () => ({
id: 'myCustomWallet',
name: 'My Custom Wallet',
iconUrl: 'https://example.com/icon.png',
connector: new WalletConnectConnector({
chains,
options: {
infuraId,
qrcode: true,
},
}),
});
const defaultWallets = getDefaultWallets({
chains,
infuraId,
appName: 'My RainbowKit App',
jsonRpcUrl: ({ chainId }) =>
chains.find(x => x.id === chainId)?.rpcUrls?.[0] ??
chain.mainnet.rpcUrls[0],
});
const wallets: Wallet[] = [...defaultWallets, myCustomWallet];
Creating custom themes
â ď¸ Note: This API is unstable and likely to change in the near future. We recommend sticking with the built-in themes for now.
While the built-in themes provide some level of customization, the Theme
type is provided to help you define your own custom themes with lower-level access to the underlying theme variables.
import { RainbowKitProvider, Theme } from '@rainbow-me/rainbowkit';
const myCustomTheme: Theme = {
colors: {
accentColor: '...',
buttonBorder: '...',
buttonSecondaryBackground: '...',
buttonText: '...',
connectButtonBackground: '...',
connectButtonBackgroundError: '...',
connectButtonInnerBackground: '...',
connectButtonText: '...',
connectButtonTextError: '...',
connectionIndicator: '...',
error: '...',
menuBackground: '...',
menuItemActiveBackground: '...',
menuItemBackground: '...',
menuText: '...',
menuTextAction: '...',
menuTextDisconnect: '...',
menuTextSecondary: '...',
modalBackdrop: '...',
modalBackground: '...',
modalBorder: '...',
modalClose: '...',
modalCloseBackground: '...',
modalText: '...',
modalTextSecondary: '...',
profileAction: '...',
profileActionHover: '...',
profileForeground: '...',
selectedOptionBorder: '...',
standby: '',
},
fonts: {
body: '...',
},
radii: {
connectButton: '...',
menuButton: '...',
modal: '...',
},
shadows: {
connectButton: '...',
dialog: '...',
menu: '...',
selectedOption: '...',
},
};
const App = () => {
return (
<RainbowKitProvider theme={myCustomTheme} {...etc}>
{/* ... */}
</RainbowKitProvider>
);
};
Creating custom theme selectors
If your app is server/statically rendered and allows users to manually toggle between themes, RainbowKitâs theming system can be hooked up to custom CSS selectors with the following functions that can be used with any CSS-in-JS system:
cssStringFromTheme
cssObjectFromTheme
These functions return CSS that sets all required theme variables. Since both strings and objects are supported, this can be integrated with any CSS-in-JS system.
As a basic example, you can render your own style
element with custom selectors for each theme. Since weâre taking control of rendering the themeâs CSS, weâre passing null
to the theme
prop so that RainbowKitProvider
doesnât render any styles for us. Also note the use of the extends
option on the cssStringFromTheme
function which omits any theme variables that are the same as the base theme.
import {
RainbowKitProvider,
cssStringFromTheme,
lightTheme,
darkTheme,
} from '@rainbow-me/rainbowkit';
const App = () => {
return (
<RainbowKitProvider theme={null} {...etc}>
<style>
{`
:root {
${cssStringFromTheme(lightTheme)}
}
html[data-dark] {
${cssStringFromTheme(darkTheme, {
extends: lightTheme,
})}
}
`}
</style>
{/* ... */}
</RainbowKitProvider>
);
};
License
MIT.