Skip to content

Latest commit

 

History

History
576 lines (426 loc) · 13.7 KB

File metadata and controls

576 lines (426 loc) · 13.7 KB

@txnlab/use-wallet-ui-react

Ready-to-use React UI components for Algorand wallet integration, built as a companion to @txnlab/use-wallet-react.

npm version CI License: MIT TypeScript

Table of Contents

Installation

npm install @txnlab/use-wallet-ui-react
# or
yarn add @txnlab/use-wallet-ui-react
# or
pnpm add @txnlab/use-wallet-ui-react
# or
bun add @txnlab/use-wallet-ui-react

Requirements

  • @txnlab/use-wallet-react v4+
  • algosdk v3+
  • React 18 or 19

Quick Start

import { NetworkId, WalletId, WalletManager, WalletProvider } from '@txnlab/use-wallet-react'
import { WalletUIProvider, WalletButton } from '@txnlab/use-wallet-ui-react'

// Import styles if NOT using Tailwind CSS
import '@txnlab/use-wallet-ui-react/dist/style.css'

const walletManager = new WalletManager({
  wallets: [
    WalletId.PERA,
    WalletId.DEFLY,
    WalletId.LUTE,
    WalletId.EXODUS,
    {
      id: WalletId.WALLETCONNECT,
      options: { projectId: 'your-project-id' },
    },
  ],
  defaultNetwork: NetworkId.TESTNET,
})

function App() {
  return (
    <WalletProvider manager={walletManager}>
      <WalletUIProvider>
        <WalletButton />
      </WalletUIProvider>
    </WalletProvider>
  )
}

The WalletButton handles everything:

  • Shows "Connect Wallet" when disconnected
  • Opens wallet selection dialog
  • Displays wallet address/NFD name and avatar when connected
  • Provides account switching and disconnect options

Styling

The library supports two styling approaches:

With Tailwind CSS

Components use Tailwind utility classes. Configure Tailwind to scan our package:

Tailwind v4:

@import 'tailwindcss';
@source "../node_modules/@txnlab/use-wallet-ui-react";

Tailwind v3:

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './node_modules/@txnlab/use-wallet-ui-react/dist/**/*.{js,ts,jsx,tsx}',
  ],
}

Without Tailwind CSS

Import our pre-built CSS:

import '@txnlab/use-wallet-ui-react/dist/style.css'

Customization

v1.0 introduces a flexible customization system with multiple approaches.

Size Variants

<WalletButton size="sm" />  {/* Small */}
<WalletButton size="md" />  {/* Medium (default) */}
<WalletButton size="lg" />  {/* Large */}

CSS Variable Overrides

Theme colors are CSS custom properties that can be overridden globally or per-instance.

Global override (in your CSS):

[data-wallet-theme] {
  --wui-color-primary: #8b5cf6;
  --wui-color-primary-hover: #7c3aed;
  --wui-color-primary-text: #ffffff;
}

Scoped override (on a wrapper):

.my-purple-button {
  --wui-color-primary: #8b5cf6;
  --wui-color-primary-hover: #7c3aed;
}
<div className="my-purple-button">
  <WalletButton />
</div>

Inline style override:

<WalletButton
  style={{
    '--wui-color-primary': '#10b981',
    '--wui-color-primary-hover': '#059669',
  } as React.CSSProperties}
/>

Available CSS Variables

Variable Description
--wui-color-primary Primary button background
--wui-color-primary-hover Primary button hover state
--wui-color-primary-text Primary button text
--wui-color-bg Panel/dialog background
--wui-color-bg-secondary Secondary background
--wui-color-bg-tertiary Tertiary background
--wui-color-bg-hover Hover background
--wui-color-text Primary text color
--wui-color-text-secondary Secondary text color
--wui-color-text-tertiary Tertiary text color
--wui-color-border Border color
--wui-color-link Link color
--wui-color-link-hover Link hover color
--wui-color-overlay Modal overlay color
--wui-color-danger-bg Danger button background
--wui-color-danger-bg-hover Danger button hover
--wui-color-danger-text Danger button text
--wui-color-avatar-bg Avatar placeholder background
--wui-color-avatar-icon Avatar placeholder icon

className Prop

Add custom classes (useful with Tailwind):

<WalletButton className="rounded-full shadow-lg" />

Direct CSS Selectors

Target the button element directly:

.pill-button [data-wallet-button] {
  border-radius: 9999px;
}
<div className="pill-button">
  <WalletButton />
</div>

Custom Trigger Button

For complete control, use the Menu components with your own button:

import { useWallet } from '@txnlab/use-wallet-react'
import { ConnectWalletMenu, ConnectedWalletMenu } from '@txnlab/use-wallet-ui-react'

function CustomWalletButton() {
  const { activeAddress } = useWallet()

  if (activeAddress) {
    return (
      <ConnectedWalletMenu>
        <button className="my-connected-button">
          {activeAddress.slice(0, 8)}...
        </button>
      </ConnectedWalletMenu>
    )
  }

  return (
    <ConnectWalletMenu>
      <button className="my-connect-button">Connect Wallet</button>
    </ConnectWalletMenu>
  )
}

The child element becomes the trigger - the library handles all the menu logic.

Theme-Aware Customization

Create different styles for light and dark modes:

.amber-theme {
  --wui-color-primary: #f59e0b;
  --wui-color-primary-hover: #d97706;
}

.dark .amber-theme {
  --wui-color-primary: rgba(245, 179, 71, 0.15);
  --wui-color-primary-hover: rgba(245, 179, 71, 0.25);
}

.dark .amber-theme [data-wallet-button] {
  border: 1px solid rgba(245, 179, 71, 0.3);
}

See the react-custom example for comprehensive demos.

Theming

The library supports automatic light/dark mode.

Theme Options

<WalletUIProvider theme="system">  {/* Default: follows OS preference */}
<WalletUIProvider theme="light">   {/* Always light */}
<WalletUIProvider theme="dark">    {/* Always dark */}

How Theme Detection Works

The library checks these in order (first match wins):

  1. data-theme attribute: Set by the theme prop on WalletUIProvider
  2. .dark class: On any ancestor element (Tailwind convention)
  3. prefers-color-scheme: System/browser preference (when theme="system")

Accessing Theme State

import { useWalletUI } from '@txnlab/use-wallet-ui-react'

function MyComponent() {
  const { theme, resolvedTheme } = useWalletUI()
  // theme: 'light' | 'dark' | 'system' (the prop value)
  // resolvedTheme: 'light' | 'dark' (the actual theme in use)
}

Components

WalletUIProvider

Required wrapper that enables theming, NFD lookups, and data prefetching.

<WalletUIProvider
  theme="system"              // 'light' | 'dark' | 'system'
  enablePrefetching={true}    // Prefetch data for all wallet accounts
  prefetchNfdView="thumbnail" // NFD data view: 'tiny' | 'thumbnail' | 'brief' | 'full'
  queryClient={queryClient}   // Optional: your existing Tanstack Query client
>
  {children}
</WalletUIProvider>

WalletButton

All-in-one wallet connection component.

<WalletButton
  size="md"              // 'sm' | 'md' | 'lg'
  className="..."        // Additional CSS classes
  style={{...}}          // Inline styles (supports CSS variable overrides)
/>

ConnectWalletMenu

Dropdown menu for wallet selection (disconnected state).

// With default button
<ConnectWalletMenu />

// With customized button
<ConnectWalletMenu>
  <ConnectWalletButton size="lg">Connect</ConnectWalletButton>
</ConnectWalletMenu>

// With custom trigger
<ConnectWalletMenu>
  <button>My Custom Button</button>
</ConnectWalletMenu>

ConnectWalletButton

Styled button for the connect state.

<ConnectWalletButton
  size="md"       // 'sm' | 'md' | 'lg'
  className="..." // Additional classes
  style={{...}}   // Inline styles
>
  Connect Wallet  {/* Optional: custom text */}
</ConnectWalletButton>

ConnectedWalletMenu

Dropdown menu for account management (connected state).

// With default button
<ConnectedWalletMenu />

// With customized button
<ConnectedWalletMenu>
  <ConnectedWalletButton size="sm" />
</ConnectedWalletMenu>

// With custom trigger
<ConnectedWalletMenu>
  <div className="my-wallet-display">Connected</div>
</ConnectedWalletMenu>

ConnectedWalletButton

Styled button showing wallet address/NFD and avatar.

<ConnectedWalletButton
  size="md"       // 'sm' | 'md' | 'lg'
  className="..." // Additional classes
  style={{...}}   // Inline styles
/>

NfdAvatar

Renders NFD avatar images with IPFS gateway handling.

import { useNfd, NfdAvatar } from '@txnlab/use-wallet-ui-react'

function Profile() {
  const nfdQuery = useNfd()

  return (
    <NfdAvatar
      nfd={nfdQuery.data}
      size={48}
      className="rounded-full"
      alt="Profile"
    />
  )
}

WalletList

Renders a list of available wallets (used internally by ConnectWalletMenu).

<WalletList onWalletSelected={() => closeMenu()} />

Hooks

useNfd

Fetch NFD data for the active address.

import { useNfd } from '@txnlab/use-wallet-ui-react'

function Profile() {
  const nfdQuery = useNfd({
    enabled: true,    // Enable/disable the query
    view: 'thumbnail' // 'tiny' | 'thumbnail' | 'brief' | 'full'
  })

  if (nfdQuery.isLoading) return <div>Loading...</div>

  return <div>{nfdQuery.data?.name || 'No NFD'}</div>
}

useAccountInfo

Fetch account information (balance, assets) for the active address.

import { useAccountInfo } from '@txnlab/use-wallet-ui-react'

function Balance() {
  const accountQuery = useAccountInfo()

  if (accountQuery.isLoading) return <div>Loading...</div>
  if (!accountQuery.data) return null

  const algoBalance = Number(accountQuery.data.amount) / 1_000_000

  return <div>{algoBalance.toFixed(2)} ALGO</div>
}

useWalletUI

Access the WalletUI context.

import { useWalletUI } from '@txnlab/use-wallet-ui-react'

function MyComponent() {
  const { theme, resolvedTheme, queryClient } = useWalletUI()
}

useResolvedTheme

Get the resolved theme value (handles 'system' preference detection).

import { useResolvedTheme } from '@txnlab/use-wallet-ui-react'

function MyComponent() {
  const resolvedTheme = useResolvedTheme('system') // Returns 'light' or 'dark'
}

Tanstack Query Integration

The library uses Tanstack Query internally. If your app already uses it, share the QueryClient to avoid duplicate caches:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WalletProvider } from '@txnlab/use-wallet-react'
import { WalletUIProvider } from '@txnlab/use-wallet-ui-react'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <WalletProvider manager={walletManager}>
        <WalletUIProvider queryClient={queryClient}>
          {/* Your app */}
        </WalletUIProvider>
      </WalletProvider>
    </QueryClientProvider>
  )
}

Migration from v0.x

v1.0 introduces a redesigned CSS architecture. Here's what changed:

Breaking Changes

  1. CSS Variable Prefix: Variables now use --wui- prefix (previously undocumented)
  2. Theme Variables Location: Defined on [data-wallet-theme] element (for CSS variable inheritance)
  3. Button Data Attribute: Buttons now have data-wallet-button attribute for targeting

New Features

  1. Size Variants: WalletButton, ConnectWalletButton, and ConnectedWalletButton now accept a size prop ('sm' | 'md' | 'lg')

  2. className/style Props: All button components accept className and style props for customization

  3. CSS Variable Overrides: Override any theme color via CSS:

    [data-wallet-theme] {
      --wui-color-primary: #your-color;
    }
  4. Custom Triggers: Pass any element as a child to ConnectWalletMenu or ConnectedWalletMenu for full control:

    <ConnectWalletMenu>
      <MyCustomButton />
    </ConnectWalletMenu>
  5. Theme Detection: Now respects .dark class on ancestors (Tailwind convention) in addition to data-theme attribute and prefers-color-scheme media query

Migration Steps

  1. Update any custom CSS targeting the library:

    • Use [data-wallet-theme] for CSS variable overrides
    • Use [data-wallet-button] for targeting button elements
  2. If you were using workarounds for customization, you can likely simplify using the new APIs

  3. Test dark mode behavior - it should work more reliably with Tailwind's .dark class

Plugins

The library includes a plugin system for extending the wallet UI without modifying core components. Plugins can inject menu items, register full-panel views, open dialogs, respond to wallet lifecycle events, and provide React context.

import { WalletUIProvider } from '@txnlab/use-wallet-ui-react'
import { exportKeyPlugin } from '@txnlab/use-wallet-ui-react/plugins/export-key'

function App() {
  return (
    <WalletUIProvider plugins={[exportKeyPlugin()]}>
      {/* ... */}
    </WalletUIProvider>
  )
}

See the Plugin Architecture Guide for full documentation.

License

MIT