Ready-made Lit web component for Magi multi-chain login (Hive, Ethereum, Bitcoin), styled with Tailwind CSS. Built as Lit web components so it can be consumed from React, Vue, Svelte, or vanilla JS via the standard custom-element API.
pnpm i @aioha/magi-ui @aioha/aioha @aioha/magi @aioha/lit-ui lit @lit/contextFor Ethereum support also install:
pnpm i @reown/appkit @reown/appkit-adapter-wagmi viem wagmi @wagmi/coreFor Bitcoin support also install:
pnpm i @reown/appkit-adapter-bitcoinimport { LitElement, html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import { Aioha, initAioha, KeyTypes } from '@aioha/aioha'
import { Magi, type BtcClient } from '@aioha/magi'
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { BitcoinAdapter, type BitcoinConnector } from '@reown/appkit-adapter-bitcoin'
import { mainnet, bitcoin } from '@reown/appkit/networks'
import { createAppKit } from '@reown/appkit'
import '@aioha/magi-ui'
@customElement('app-root')
export class AppRoot extends LitElement {
@state() private _modalDisplayed = false
@state() private _btcClient?: BtcClient
private _aioha = initAioha()
private _magi = new Magi()
private _wagmiAdapter = new WagmiAdapter({ networks: [mainnet], projectId: 'YOUR_PROJECT_ID' })
private _btcAdapter = new BitcoinAdapter({ projectId: 'YOUR_PROJECT_ID' })
private _appKit = createAppKit({
adapters: [this._wagmiAdapter, this._btcAdapter],
networks: [mainnet, bitcoin],
projectId: 'YOUR_PROJECT_ID'
})
connectedCallback() {
super.connectedCallback()
this._magi.setAioha(this._aioha)
this._appKit.subscribeAccount((account) => {
if (account.caipAddress?.startsWith('bip122:') && account.isConnected && account.address) {
const provider = this._appKit.getProvider<BitcoinConnector>('bip122')
if (provider) {
const address = account.address
this._btcClient = {
address,
signMessage: (msg: string) => provider.signMessage({ address, message: msg })
}
}
} else if (account.caipAddress?.startsWith('bip122:')) {
this._btcClient = undefined
}
})
}
render() {
return html`
<button @click=${() => (this._modalDisplayed = true)}>Connect</button>
<aioha-provider .aioha=${this._aioha}>
<magi-provider .magi=${this._magi} .wagmiConfig=${this._wagmiAdapter.wagmiConfig} .btcClient=${this._btcClient}>
<magi-modal
?displayed=${this._modalDisplayed}
.loginOptions=${{ msg: 'Login', keyType: KeyTypes.Posting }}
.openEthModal=${() => this._appKit.open({ view: 'Connect', namespace: 'eip155' })}
.openBtcModal=${() => this._appKit.open({ view: 'Connect', namespace: 'bip122' })}
.onClose=${() => (this._modalDisplayed = false)}
></magi-modal>
</magi-provider>
</aioha-provider>
`
}
}| Property | Description | Default |
|---|---|---|
displayed |
Whether the modal is open. | false |
lightMode |
Display the modal in light mode. | false |
loginTitle |
Hive login title. | "Connect Wallet" |
loginHelpUrl |
Help URL under provider selection. | undefined |
loginOptions |
Aioha login options. | required |
discOptions |
Account discovery options. | undefined |
forceShowProviders |
Force-show Hive providers. | [] |
arrangement |
Provider selection layout: list or grid. |
list |
imageServer |
Avatar image server URL. | https://images.hive.blog |
explorerUrl |
Hive block explorer URL. | https://hivehub.dev |
language |
UI language code. | en |
direction |
ltr, rtl, or auto. |
auto |
messages |
Custom Messages instance. |
shared default |
openEthModal |
Callback to open AppKit Ethereum view. | required |
openBtcModal |
Callback to open AppKit Bitcoin view. | required |
onLogin |
Hive login callback. | undefined |
onClose |
Modal close callback. | undefined |
The modal auto-closes once Magi reports a connected Ethereum or Bitcoin wallet, mirroring the behavior of Hive logins handled by the embedded <login-modal>.
<magi-modal> can be instantiated imperatively from vanilla HTML/JS or any non-Lit framework using initModal(). It builds the <aioha-provider> → <magi-provider> → <magi-modal> tree for you and returns the modal element.
<html>
<body>
<button id="connectButton" type="button">Connect Wallet</button>
<div id="loginModal"></div>
<script type="module">
import { initAioha, KeyTypes } from '@aioha/aioha'
import { Magi } from '@aioha/magi'
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin'
import { mainnet, bitcoin } from '@reown/appkit/networks'
import { createAppKit } from '@reown/appkit'
import { initModal } from '@aioha/magi-ui'
const aioha = initAioha()
const magi = new Magi()
magi.setAioha(aioha)
const wagmiAdapter = new WagmiAdapter({ networks: [mainnet], projectId: 'YOUR_PROJECT_ID' })
const btcAdapter = new BitcoinAdapter({ projectId: 'YOUR_PROJECT_ID' })
const appKit = createAppKit({
adapters: [wagmiAdapter, btcAdapter],
networks: [mainnet, bitcoin],
projectId: 'YOUR_PROJECT_ID'
})
const modal = initModal(aioha, magi, document.getElementById('loginModal'), {
wagmiConfig: wagmiAdapter.wagmiConfig,
loginOptions: { msg: 'Login', keyType: KeyTypes.Posting },
openEthModal: () => appKit.open({ view: 'Connect', namespace: 'eip155' }),
openBtcModal: () => appKit.open({ view: 'Connect', namespace: 'bip122' }),
onClose: () => (modal.displayed = false)
})
// The btcClient is reactive — push updates onto the magi-provider
appKit.subscribeAccount((account) => {
if (account.caipAddress?.startsWith('bip122:') && account.isConnected && account.address) {
const provider = appKit.getProvider('bip122')
const address = account.address
modal.parentElement.btcClient = {
address,
signMessage: (msg) => provider.signMessage({ address, message: msg })
}
} else if (account.caipAddress?.startsWith('bip122:')) {
modal.parentElement.btcClient = undefined
}
})
document.getElementById('connectButton').addEventListener('click', () => (modal.displayed = true))
</script>
</body>
</html>initModal() accepts the same options as the <magi-modal> properties listed above, plus:
| Option | Description | Default |
|---|---|---|
wagmiConfig |
Wagmi Config instance from your AppKit Ethereum adapter. Enables EVM auto-connect. |
undefined |
btcClient |
Initial Bitcoin signer. Update reactively via modal.parentElement.btcClient = newClient when the AppKit Bitcoin connection changes. |
undefined |
The DOM hierarchy created by initModal() is <aioha-provider> → <magi-provider> → <magi-modal>. Use modal.parentElement to reach the <magi-provider> for reactive btcClient updates.