Skip to content

Custom ConnectButton ​

Learn how to create completely custom wallet connection buttons while leveraging LunoKit's connection logic.

Overview ​

While LunoKit's default ConnectButton covers most use cases, you might want to create a completely custom UI. LunoKit provides the underlying hooks and logic to build your own connect button from scratch.

Basic Custom Button ​

tsx
import { useConnect, useAccount, useDisconnect, type ConnectionStatus } from '@luno-kit/react'

function CustomConnectButton() {
  const { connect, connectors, status } = useConnect()
  const { account } = useAccount()
  const { disconnect } = useDisconnect()
  
  // If connected, show account info
  if (status === ConnectionStatus.Connected && account) {
    return (
      <div className="custom-wallet-info">
        <div className="account-details">
          <span className="account-name">{account.name}</span>
          <span className="account-address">{account.formattedAddress}</span>
        </div>
        <button onClick={() => disconnect()} className="disconnect-btn">
          Disconnect
        </button>
      </div>
    )
  }
  
  // If not connected, show wallet options
  return (
    <div className="custom-connect-modal">
      <h3>Connect Wallet</h3>
      <div className="wallet">
        {connectors.map((connector) => (
          <button
            key={connector.id}
            onClick={() => connect({ connectorId: connector.id })}
            className="wallet-option"
            disabled={!connector.installed}
          >
            <img src={connector.icon} alt={connector.name} />
            <span>{connector.name}</span>
            {!connector.installed && <span className="not-installed">Not Installed</span>}
          </button>
        ))}
      </div>
    </div>
  )
}

Using LunoKit UI Modals with Custom Buttons ​

If you want to create a custom connect button but still use LunoKit's pre-built modal components, you can leverage the modal hooks to control the UI modals while maintaining your custom button design.

Example with Modal Hooks ​

tsx
import { useConnectModal, useAccountModal, useChainModal } from '@luno-kit/ui'
import { useAccount, useStatus, type ConnectionStatus } from '@luno-kit/react'

function CustomButtonWithModals() {
  const { open: openConnectModal, close: closeConnectModal, isOpen: isConnectModalOpen } = useConnectModal()
  const { open: openAccountModal, close: closeAccountModal, isOpen: isAccountModalOpen } = useAccountModal()
  const { open: openChainModal, close: closeChainModal, isOpen: isChainModalOpen } = useChainModal()
  
  const { account } = useAccount()
  const { status } = useStatus()

  if (status === ConnectionStatus.Connected && account) {
    return (
      <div className="custom-connected-state">
        <button onClick={() => openAccountModal?.()} className="account-button">
          {account.name || 'Account'}
        </button>
        <button onClick={() => openChainModal?.()} className="chain-button">
          Switch Chain
        </button>
      </div>
    )
  }

  return (
    <button onClick={() => openConnectModal?.()} className="custom-connect-btn">
      Connect Wallet
    </button>
  )
}

The modal hooks provide different functionality based on the connection status:

  • useConnectModal - open is available when not connected, undefined when connected
  • useAccountModal - open is available when connected, undefined when not connected
  • useChainModal - open is available when connected, undefined when not connected

This design ensures that:

  • Users can only open the connect modal when they're not connected
  • Users can only open account/chain modals when they have an active connection
  • The open method will be undefined when the modal shouldn't be accessible

Using LunoProvider Instead of LunoKitProvider ​

If you prefer to use only the core functionality from @luno-kit/react without the UI components from @luno-kit/ui, you can use LunoProvider directly. However, you must manually set up the QueryClientProvider since it's not included in the LunoProvider.

Example with LunoProvider ​

tsx
import { LunoProvider, createConfig } from '@luno-kit/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

// Create a config
const config = createConfig({
  // Your configuration here
})

// Create a query client
const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <LunoProvider config={config}>
        {/* Your app content */}
        <CustomConnectButton />
      </LunoProvider>
    </QueryClientProvider>
  )
}

WARNING

When using LunoProvider directly from @luno-kit/react instead of LunoKitProvider from @luno-kit/ui, you must manually wrap your application with QueryClientProvider from @tanstack/react-query. Without this, hooks that depend on React Query will not function properly.

Released under the MIT License.