Core
This is the core package that contains all of the UI and logic to be able to seamlessly connect user's wallets to your app and track the state of those wallets.

@web3-onboard/core

The core package. Onboard no longer contains any wallet specific code, so wallets need to be passed in upon initialization.

Installation

Install the core module:
npm i @web3-onboard/core
If you would like to support all wallets, then you can install all of the wallet modules:
npm i @web3-onboard/injected-wallets @web3-onboard/ledger @web3-onboard/trezor @web3-onboard/keepkey @web3-onboard/walletconnect @web3-onboard/walletlink @web3-onboard/torus @web3-onboard/portis @web3-onboard/mew @web3-onboard/gnosis @web3-onboard/fortmatic @web3-onboard/magic
Note:
  • MEW wallet currently fails to install on M1 macs
  • All wallet modules (except for injected-wallets) require extra dependencies and may require polyfilling the node built in modules for the browser. See the Build Environments section for more info

Initialization

Onboard needs to be initialized with an options object before the API can be used:
1
type InitOptions {
2
wallets: WalletInit[]
3
chains: Chain[]
4
appMetadata?: AppMetadata
5
i18n?: i18nOptions
6
accountCenter?: AccountCenterOptions
7
}
Copied!

Options

wallets An array of wallet modules that you would like to be presented to the user to select from when connecting a wallet. A wallet module is an abstraction that allows for easy interaction without needing to know the specifics of how that wallet works and are separate packages that can be included. A list of wallet module packages that can be installed can be found here.
chains An array of Chains that your app supports:
1
type Chain = {
2
id: ChainId // hex encoded string, eg '0x1' for Ethereum Mainnet
3
namespace?: 'evm' // string indicating chain namespace. Defaults to 'evm' but will allow other chain namespaces in the future
4
rpcUrl: string // used for network requests
5
label: string // used for display, eg Ethereum Mainnet
6
token: TokenSymbol // the native token symbol, eg ETH, BNB, MATIC
7
}
Copied!
Not sure about the chainIDs of the chains you want to support? We recommend looking them up on ChainList and converting the decimal to hex before passing to web3-onboard
appMetadata An object that defines your app:
1
type AppMetadata = {
2
// app name
3
name: string
4
// SVG icon string, with height or width (whichever is larger) set to 100% or a valid image URL
5
icon: string
6
// Optional wide format logo (ie icon and text) to be displayed in the sidebar of connect modal. Defaults to icon if not provided
7
logo?: string
8
// description of app
9
description?: string
10
// url to a getting started guide for app
11
gettingStartedGuide?: string
12
// url that points to more information about app
13
explore?: string
14
// if your app only supports injected wallets and when no injected wallets detected, recommend the user to install some
15
recommendedInjectedWallets?: RecommendedInjectedWallets[]
16
}
17
18
type RecommendedInjectedWallets = {
19
name: string // display name
20
url: string // link to download wallet
21
}
Copied!
i18n An object that defines the display text for different locales. Can also be used to override the default text. To override the default text, pass in a object for the en locale.
1
type Locale = string // eg 'en', 'es'
2
type i18nOptions = Record<Locale, i18n>
Copied!
To see a list of all of the text values that can be internationalized or replaced, check out the default en file. Onboard is using the ICU syntax for formatting under the hood.
accountCenter An object that defines whether the account center UI is enabled and its position on the screen. Currently the account center is disabled for mobile devices, so only desktop options are available.
1
type AccountCenterOptions = {
2
desktop: {
3
position?: AccountCenterPosition // default: 'topRight'
4
enabled?: AccountCenter['enabled'] // default: true
5
}
6
}
7
type AccountCenter = {
8
enabled: boolean
9
position: AccountCenterPosition
10
expanded: boolean
11
}
12
type AccountCenterPosition =
13
| 'topRight'
14
| 'bottomRight'
15
| 'bottomLeft'
16
| 'topLeft'
Copied!

Initialization Example

Putting it all together, here is an example initialization with the injected wallet modules:
1
import Onboard from '@web3-onboard/core'
2
import injectedModule from '@web3-onboard/injected-wallets'
3
4
const ETH_MAINNET_RPC = `https://mainnet.infura.io/v3/${INFURA_KEY}`
5
const ETH_RINKEBY_RPC = `https://rinkeby.infura.io/v3/${INFURA_KEY}`
6
const MATIC_MAINNET_RPC = 'https://matic-mainnet.chainstacklabs.com'
7
8
const injected = injectedModule()
9
10
const onboard = Onboard({
11
wallets: [injected],
12
chains: [
13
{
14
id: '0x1',
15
token: 'ETH',
16
label: 'Ethereum Mainnet',
17
rpcUrl: ETH_MAINNET_RPC
18
},
19
{
20
id: '0x3',
21
token: 'tROP',
22
label: 'Ethereum Ropsten Testnet',
23
rpcUrl: ETH_ROPSTEN_RPC
24
},
25
{
26
id: '0x4',
27
token: 'rETH',
28
label: 'Ethereum Rinkeby Testnet',
29
rpcUrl: ETH_RINKEBY_RPC
30
},
31
{
32
id: '0x38',
33
token: 'BNB',
34
label: 'BNB Chain',
35
rpcUrl: 'https://bsc-dataseed.binance.org/'
36
},
37
{
38
id: '0x89',
39
token: 'MATIC',
40
label: 'Matic Mainnet',
41
rpcUrl: 'https://matic-mainnet.chainstacklabs.com'
42
},
43
{
44
id: '0xfa',
45
token: 'FTM',
46
label: 'Fantom Mainnet',
47
rpcUrl: 'https://rpc.ftm.tools/'
48
}
49
],
50
appMetadata: {
51
name: 'Token Swap',
52
icon: myIcon, // svg string icon
53
description: 'Swap tokens for other tokens',
54
recommendedInjectedWallets: [
55
{ name: 'MetaMask', url: 'https://metamask.io' },
56
{ name: 'Coinbase', url: 'https://wallet.coinbase.com/' }
57
]
58
}
59
i18n: {
60
en: {
61
connect: {
62
selectingWallet: {
63
header: 'custom text header'
64
}
65
}
66
}
67
}
68
})
Copied!

Connecting a Wallet

To initiate a user to select and connect a wallet you can call the connectWallet function on an initialized Onboard instance. It will return a Promise that will resolve when the user either successfully connects a wallet, or when they dismiss the UI. The resolved value from the promise will be the latest state of the wallets array. The order of the wallets array is last to first, so the most recently selected wallet will be the first item in the array and can be thought of as the "primary wallet". If no wallet was selected, then the wallets array will have the same state as it had before calling connectWallet.

Example

1
async function connectWallet() {
2
const wallets = await onboard.connectWallet()
3
console.log(wallets)
4
}
5
6
connectWallet()
Copied!

Auto Selecting a Wallet

A common UX pattern is to remember the wallet(s) that a user has previously connected by storing them in localStorage and then automatically selecting them for the user next time they visit your app. You could enable this in your app by first syncing the wallets array to localStorage:
1
const walletsSub = onboard.state.select('wallets')
2
const { unsubscribe } = walletsSub.subscribe(wallets => {
3
const connectedWallets = wallets.map(({ label }) => label)
4
window.localStorage.setItem(
5
'connectedWallets',
6
JSON.stringify(connectedWallets)
7
)
8
})
9
10
// Don't forget to unsubscribe when your app or component un mounts to prevent memory leaks
11
// unsubscribe()
Copied!
Now that you have the most recent wallets connected saved in local storage, you can auto select those wallet(s) when your app loads:
1
const previouslyConnectedWallets = JSON.parse(
2
window.localStorage.getItem('connectedWallets')
3
)
4
5
if (previouslyConnectedWallets) {
6
// Connect the most recently connected wallet (first in the array)
7
await onboard.connectWallet({ autoSelect: previouslyConnectedWallets[0] })
8
9
// You can also auto connect "silently" and disable all onboard modals to avoid them flashing on page load
10
await onboard.connectWallet({
11
autoSelect: { label: previouslyConnectedWallets[0], disableModals: true }
12
})
13
14
// OR - loop through and initiate connection for all previously connected wallets
15
// note: This UX might not be great as the user may need to login to each wallet one after the other
16
// for (walletLabel in previouslyConnectedWallets) {
17
// await onboard.connectWallet({ autoSelect: walletLabel })
18
// }
19
}
Copied!

Disconnecting a Wallet

A wallet can be disconnected, which will cleanup any background operations the wallet may be doing and will also remove it from the Onboard wallets array:
1
// disconnect the first wallet in the wallets array
2
const [primaryWallet] = onboard.state.get().wallets
3
await onboard.disconnectWallet({ label: primaryWallet.label })
Copied!
The disconnectWallet method takes the wallet.label value and returns a Promise that resolves to the current state of the wallets array.

State

Onboard currently keeps track of the following state:
  • wallets: The wallets connected to Onboard
  • chains: The chains that Onboard has been initialized with
  • accountCenter: The current state of the account center UI
  • walletModules: The wallet modules that are currently set and will be rendered in the wallet selection modal
1
type AppState = {
2
wallets: WalletState[]
3
chains: Chain[]
4
accountCenter: AccountCenter
5
walletModules: WalletModule[]
6
}
7
type Chain {
8
namespace?: 'evm'
9
id: ChainId
10
rpcUrl: string
11
label: string
12
token: TokenSymbol
13
color?: string
14
icon?: string
15
}
16
17
type WalletState = {
18
label: string
19
icon: string
20
provider: EIP1193Provider
21
accounts: Account[]
22
chains: ConnectedChain[]
23
instance?: unknown
24
}
25
26
type Account = {
27
address: string
28
ens: {
29
name?: string
30
avatar?: string
31
contentHash?: string
32
getText?: (key: string) => Promise<string | undefined>
33
}
34
balance: Record<TokenSymbol, string>
35
}
36
37
type ConnectedChain = {
38
namespace: 'evm'
39
id: ChainId
40
}
41
42
type ChainId = string
43
type TokenSymbol = string
44
45
type AccountCenter = {
46
enabled: boolean
47
position: AccountCenterPosition
48
expanded: boolean
49
}
50
type AccountCenterPosition =
51
| 'topRight'
52
| 'bottomRight'
53
| 'bottomLeft'
54
| 'topLeft'
Copied!
The current state of Onboard can be accessed at any time using the state.get() method:
1
const currentState = onboard.state.get()
Copied!
State can also be subscribed to using the state.select() method. The select method will return an RXJS Observable. Understanding of RXJS observables is not necessary to subscribe to state updates, but allows for composable functionality if wanted. The key point to understand is that if you subscribe for updates, remember to unsubscribe when you are finished to prevent memory leaks.
To subscribe to all state updates, call the select method with no arguments:
1
const state = onboard.state.select()
2
const { unsubscribe } = state.subscribe(update =>
3
console.log('state update: ', update)
4
)
5
6
// remember to unsubscribe when updates are no longer needed
7
// unsubscribe()
Copied!
Specific top level slices of state can be subcribed to. For example you may want to just subscribe to receive updates to the wallets array only:
1
const wallets = onboard.state.select('wallets')
2
const { unsubscribe } = wallets.subscribe(update =>
3
console.log('wallets update: ', update)
4
)
5
6
// unsubscribe when updates are no longer needed
7
unsubscribe()
Copied!

Actions to Modify State

setWalletModules For updating the wallets that are displayed in the wallet selection modal. This can be used if the wallets you want to support is conditional on another user action within your app. The setWalletModules action is called with an updated array of wallets (the same wallets that are passed in on initialization)
1
const onboard = Onboard({
2
wallets: [injected, trezor, ledger],
3
chains: [
4
{
5
id: '0x1',
6
token: 'ETH',
7
label: 'Ethereum Mainnet',
8
rpcUrl: `https://mainnet.infura.io/v3/${INFURA_ID}`
9
}
10
]
11
})
12
13
// then after a user action, you may decide to only display hardware wallets on the next call to onboard.connectWallet
14
onboard.state.actions.setWalletModules([ledger, trezor])
Copied!

Setting the User's Chain/Network

When initializing Onboard you define a list of chains/networks that your app supports. If you would like to prompt the user to switch to one of those chains, you can use the setChain method on an initialized instance of Onboard:
1
type SetChain = (options: SetChainOptions) => Promise<boolean>
2
type SetChainOptions = {
3
chainId: string // hex encoded string
4
chainNamespace?: 'evm' // defaults to 'evm' (currently the only valid value, but will add more in future updates)
5
wallet?: string // the wallet.label of the wallet to set chain
6
}
7
8
const success = await onboard.setChain({ chainId: '0x89' })
Copied!
The setChain methods takes an options object with a chainId property hex encoded string for the chain id to switch to. The chain id must be one of the chains that Onboard was initialized with. If the wallet supports programatically adding and switching the chain, then the user will be prompted to do so, if not, then a modal will be displayed indicating to the user that they need to switch chains to continue. The setChain method returns a promise that resolves when either the user has confirmed the chain switch, or has dismissed the modal and resolves with a boolean indicating if the switch network was successful or not. The setChain method will by default switch the first wallet (the most recently connected) in the wallets array. A specific wallet can be targeted by passing in the wallet.label in the options object as the wallet parameter.

Custom Styling

The Onboard styles can customized via CSS variables. The following properties and their default properties can be customized by adding these variables to the :root in your CSS file:
1
:root {
2
/* CUSTOMIZE THE COLOR PALLETTE */
3
--onboard-white: white;
4
--onboard-black: black;
5
--onboard-primary-1: #2f80ed;
6
--onboard-primary-100: #eff1fc;
7
--onboard-primary-200: #d0d4f7;
8
--onboard-primary-300: #b1b8f2;
9
--onboard-primary-400: #929bed;
10
--onboard-primary-500: #6370e5;
11
--onboard-primary-600: #454ea0;
12
--onboard-primary-700: #323873;
13
--onboard-gray-100: #ebebed;
14
--onboard-gray-200: #c2c4c9;
15
--onboard-gray-300: #999ca5;
16
--onboard-gray-400: #707481;
17
--onboard-gray-500: #33394b;
18
--onboard-gray-600: #242835;
19
--onboard-gray-700: #1a1d26;
20
--onboard-success-100: #d1fae3;
21
--onboard-success-200: #baf7d5;
22
--onboard-success-300: #a4f4c6;
23
--onboard-success-400: #8df2b8;
24
--onboard-success-500: #5aec99;
25
--onboard-success-600: #18ce66;
26
--onboard-success-700: #129b4d;
27
--onboard-danger-100: #ffe5e6;
28
--onboard-danger-200: #ffcccc;
29
--onboard-danger-300: #ffb3b3;
30
--onboard-danger-400: #ff8080;
31
--onboard-danger-500: #ff4f4f;
32
--onboard-danger-600: #cc0000;
33
--onboard-danger-700: #660000;
34
--onboard-warning-100: #ffefcc;
35
--onboard-warning-200: #ffe7b3;
36
--onboard-warning-300: #ffd780;
37
--onboard-warning-400: #ffc74c;
38
--onboard-warning-500: #ffaf00;
39
--onboard-warning-600: #cc8c00;
40
--onboard-warning-700: #664600;
41
42
/* CUSTOMIZE SECTIONS OF THE CONNECT MODAL */
43
--onboard-connect-content-width
44
--onboard-connect-content-height
45
--onboard-wallet-columns
46
--onboard-connect-sidebar-background
47
--onboard-connect-sidebar-color
48
--onboard-connect-sidebar-progress-background
49
--onboard-connect-sidebar-progress-color
50
--onboard-connect-header-background
51
--onboard-connect-header-color
52
--onboard-link-color
53
--onboard-close-button-background
54
--onboard-close-button-color
55
--onboard-checkbox-background
56
--onboard-checkbox-color
57
--onboard-wallet-button-background
58
--onboard-wallet-button-background-hover
59
--onboard-wallet-button-color
60
--onboard-wallet-button-border-color
61
--onboard-wallet-app-icon-border-color
62
63
/* FONTS */
64
--onboard-font-family-normal: Sofia Pro;
65
--onboard-font-family-semibold: Sofia Pro Semibold;
66
--onboard-font-family-light: Sofia Pro Light;
67
68
--onboard-font-size-1: 3rem;
69
--onboard-font-size-2: 2.25rem;
70
--onboard-font-size-3: 1.5rem;
71
--onboard-font-size-4: 1.25rem;
72
--onboard-font-size-5: 1rem;
73
--onboard-font-size-6: 0.875rem;
74
--onboard-font-size-7: 0.75rem;
75
76
/* SPACING */
77
--onboard-spacing-1: 3rem;
78
--onboard-spacing-2: 2rem;
79
--onboard-spacing-3: 1.5rem;
80
--onboard-spacing-4: 1rem;
81
--onboard-spacing-5: 0.5rem;
82
83
/* SHADOWS */
84
--onboard-shadow-1: 0px 4px 12px rgba(0, 0, 0, 0.1);
85
--onboard-shadow-2: inset 0px -1px 0px rgba(0, 0, 0, 0.1);
86
87
--onboard-modal-z-index: 10
88
}
Copied!

Build Environments

Many of the wallet modules require dependencies that are not normally included in browser builds (namely the node builtin modules such as crypto, buffer, util etc). If you are having build issues you can try the following bundler configs to resolve these dependency issues:

Webpack 4

Everything should just work since the node builtins are automatically bundled in v4

Webpack 5

You'll need to add some dev dependencies with the following command:
npm i --save-dev assert buffer crypto-browserify stream-http https-browserify os-browserify process stream-browserify util
Then add the following to your webpack.config.js file:
1
const webpack = require('webpack')
2
3
module.exports = {
4
resolve: {
5
alias: {
6
assert: 'assert',
7
buffer: 'buffer',
8
crypto: 'crypto-browserify',
9
http: 'stream-http',
10
https: 'https-browserify',
11
os: 'os-browserify/browser',
12
process: 'process/browser',
13
stream: 'stream-browserify',
14
util: 'util'
15
}
16
},
17
experiments: {
18
asyncWebAssembly: true
19
},
20
plugins: [
21
new webpack.ProvidePlugin({
22
process: 'process/browser',
23
Buffer: ['buffer', 'Buffer']
24
})
25
]
26
}
Copied!
If using create-react-app
CRACO provides an easy way to override webpack config which is obfuscated in Create React App built applications.
The above webpack 5 example can be used in the craco.config.js file at the root level in this case.

SvelteKit

Add the following dev dependencies:
npm i --save-dev rollup-plugin-polyfill-node
Then add the following to your svelte.config.js file:
1
import adapter from '@sveltejs/adapter-auto'
2
import preprocess from 'svelte-preprocess'
3
import nodePolyfills from 'rollup-plugin-polyfill-node'
4
5
const MODE = process.env.NODE_ENV
6
const development = MODE === 'development'
7
8
/** @type {import('@sveltejs/kit').Config} */
9
const config = {
10
preprocess: preprocess(),
11
kit: {
12
adapter: adapter(),
13
vite: {
14
plugins: [
15
development &&
16
nodePolyfills({
17
include: [
18
'node_modules/**/*.js',
19
new RegExp('node_modules/.vite/.*js')
20
],
21
http: true,
22
crypto: true
23
})
24
],
25
resolve: {
26
alias: {
27
crypto: 'crypto-browserify',
28
stream: 'stream-browserify',
29
assert: 'assert'
30
}
31
},
32
build: {
33
rollupOptions: {
34
plugins: [nodePolyfills({ crypto: true, http: true })]
35
},
36
commonjsOptions: {
37
transformMixedEsModules: true
38
}
39
}
40
}
41
}
42
}
43
44
export default config
Copied!

Vite

Add the following dev dependencies:
npm i --save-dev rollup-plugin-polyfill-node
Then add the following to your vite.config.js file:
1
import nodePolyfills from 'rollup-plugin-polyfill-node'
2
3
const MODE = process.env.NODE_ENV
4
const development = MODE === 'development'
5
6
export default {
7
// other config options
8
plugins: [
9
development &&
10
nodePolyfills({
11
include: [
12
'node_modules/**/*.js',
13
new RegExp('node_modules/.vite/.*js')
14
],
15
http: true,
16
crypto: true
17
})
18
],
19
resolve: {
20
alias: {
21
crypto: 'crypto-browserify',
22
stream: 'stream-browserify',
23
assert: 'assert'
24
}
25
},
26
build: {
27
rollupOptions: {
28
plugins: [nodePolyfills({ crypto: true, http: true })]
29
},
30
commonjsOptions: {
31
transformMixedEsModules: true
32
}
33
}
34
}javas
Copied!

Nuxt.js

Add the following to your nuxt.config.js:
1
build: {
2
standalone: true,
3
}
Copied!