BigBlocks Docs
Components/Social

CompactMessageButton

A space-efficient messaging component that opens a popover for composing secure Bitcoin-based messages

A compact messaging component that provides a clean, space-efficient interface for composing and sending messages via Bitcoin transactions. Perfect for inline messaging in social feeds, user profiles, or chat interfaces where space is at a premium.

View Component Preview →

Installation

npx bigblocks add compact-message-button

Import

import { CompactMessageButton } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
onMessage(message: Message) => void | Promise<void>Yes-Callback when message is sent
recipientNamestringNo-Display name of the message recipient
recipientAvatarstringNo-Avatar URL of the recipient
recipientAddressstringYes-Bitcoin address of the recipient
defaultContentstringNo''Pre-filled message content
placeholderstringNo'Type a message...'Input placeholder text
maxLengthnumberNo500Maximum message length
align'start' | 'center' | 'end'No'end'Popover alignment
side'top' | 'right' | 'bottom' | 'left'No'bottom'Popover side
iconSizenumberNo16Size of the chat icon in pixels
allowRecipientChangebooleanNofalseAllow changing recipient in the popover
variantButtonVariantNo'ghost'Button style variant
size'1' | '2' | '3' | '4'No'2'Button size
disabledbooleanNofalseDisable the button
classNamestringNo-Additional CSS classes

Basic Usage

import { CompactMessageButton } from 'bigblocks';

function UserProfile({ user }) {
  return (
    <CompactMessageButton
      recipientName={user.name}
      recipientAvatar={user.avatar}
      recipientAddress={user.address}
      onMessage={(message) => {
        console.log('Message sent:', message);
      }}
    />
  );
}

Advanced Usage

import { CompactMessageButton } from 'bigblocks';
import { toast } from 'sonner';

function MessagingInterface() {
  const handleMessage = async (message: Message) => {
    try {
      // Message is already broadcast to Bitcoin network
      // You can store additional metadata in your backend
      await saveMessageToDatabase({
        txId: message.txId,
        recipient: message.recipient,
        timestamp: message.timestamp,
      });
      
      toast.success(`Message sent! TxID: ${message.txId.slice(0, 8)}...`);
    } catch (error) {
      toast.error('Failed to save message metadata');
    }
  };

  return (
    <CompactMessageButton
      recipientName="Alice"
      recipientAvatar="/avatars/alice.jpg"
      recipientAddress="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
      defaultContent="Hey Alice, "
      placeholder="Write your message..."
      maxLength={280}
      align="start"
      side="top"
      iconSize={20}
      variant="soft"
      size="3"
      onMessage={handleMessage}
    />
  );
}

Message Object

The onMessage callback receives a message object with the following structure:

interface Message {
  txId: string;        // Bitcoin transaction ID of the message
  content: string;     // The actual message text
  recipient: string;   // Recipient's Bitcoin address
  timestamp: number;   // Unix timestamp when message was sent
  contentType: string; // MIME type (default: 'text/plain')
}

Common Patterns

In User Lists

Display compact message buttons next to user names in lists:

import { CompactMessageButton } from 'bigblocks';

function UserList({ users }) {
  return (
    <div className="space-y-2">
      {users.map(user => (
        <div key={user.id} className="flex items-center justify-between p-3 hover:bg-gray-50">
          <div className="flex items-center gap-3">
            <img src={user.avatar} className="w-10 h-10 rounded-full" />
            <span className="font-medium">{user.name}</span>
          </div>
          <CompactMessageButton
            recipientName={user.name}
            recipientAvatar={user.avatar}
            recipientAddress={user.address}
            size="1"
            variant="ghost"
          />
        </div>
      ))}
    </div>
  );
}

In Social Feed Actions

Add messaging to post action bars:

import { CompactMessageButton, LikeButton, FollowButton } from 'bigblocks';

function PostActions({ post }) {
  return (
    <div className="flex items-center gap-2 mt-4">
      <LikeButton postId={post.id} />
      <CompactMessageButton
        recipientName={post.author.name}
        recipientAvatar={post.author.avatar}
        recipientAddress={post.author.address}
        size="2"
      />
      <FollowButton userAddress={post.author.address} />
    </div>
  );
}

Direct Message Composer

Allow users to compose messages without a predefined recipient:

import { CompactMessageButton } from 'bigblocks';
import { useContacts } from '@/hooks/useContacts';

function DirectMessageComposer() {
  const { contacts } = useContacts();
  
  return (
    <CompactMessageButton
      allowRecipientChange={true}
      placeholder="Start a conversation..."
      variant="surface"
      size="3"
      onMessage={async (message) => {
        // Handle the message
        await sendDirectMessage(message);
        toast.success('Message sent!');
      }}
    />
  );
}

Reply to Message

Pre-fill content for replies:

function MessageReply({ originalMessage }) {
  return (
    <CompactMessageButton
      recipientName={originalMessage.senderName}
      recipientAddress={originalMessage.senderAddress}
      defaultContent={`Re: ${originalMessage.subject}`}
      onMessage={handleReply}
      size="1"
      variant="ghost"
    />
  );
}

Authentication Requirements

The CompactMessageButton requires Bitcoin authentication to send messages. Wrap your app with the necessary providers:

import { 
  BitcoinAuthProvider, 
  BitcoinQueryProvider,
  BitcoinThemeProvider 
} from 'bigblocks';

function App() {
  return (
    <BitcoinQueryProvider>
      <BitcoinAuthProvider config={{ apiUrl: '/api' }}>
        <BitcoinThemeProvider>
          <YourApp />
        </BitcoinThemeProvider>
      </BitcoinAuthProvider>
    </BitcoinQueryProvider>
  );
}

When not authenticated, the button will be disabled automatically.

Popover Interface

The component opens a popover with the following layout:

┌─────────────────────────┐
│ Send Message           │
│ To: 👤 John Doe        │
├─────────────────────────┤
│                         │
│ [Message text area]     │
│                         │
│                         │
├─────────────────────────┤
│ 245 chars   Cmd+Enter   │
│            [✉️ Send]    │
└─────────────────────────┘

Character Counter

The character counter provides visual feedback:

  • Green: More than 50 characters remaining
  • Orange: Less than 50 characters remaining
  • Red: Over the character limit (send button disabled)

Keyboard Shortcuts

  • Cmd/Ctrl + Enter: Send the message
  • Escape: Close the popover

Bitcoin Integration

On-Chain Messaging

Messages are broadcast as Bitcoin transactions using the MAP (Magic Attribute Protocol) standard:

// Example MAP transaction structure
{
  "app": "messaging",
  "type": "message",
  "content": "Hello Alice!",
  "recipient": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
  "timestamp": 1704067200000,
  "contentType": "text/plain"
}

Cost Considerations

  • Each message costs approximately $0.001 in transaction fees
  • Messages are permanently stored on the blockchain
  • Consider implementing off-chain storage for longer conversations

Privacy Notes

  • Messages are public on the blockchain
  • Consider encryption for sensitive communications
  • Use the component's integration with BAP identity for verified senders

Styling

The component uses BigBlocks layout constants for consistent sizing:

// Internal styling uses:
CONTAINER_WIDTHS.POPOVER_SMALL // 320px popover width

Theme Adaptation

The component automatically adapts to your BitcoinThemeProvider settings:

  • Button variants respect theme colors
  • Text and borders follow theme contrast ratios
  • Dark mode is fully supported

Custom Styling

Apply custom styles with the className prop:

<CompactMessageButton
  className="hover:scale-105 transition-transform"
  recipientAddress={address}
  onMessage={handleMessage}
/>

Error Handling

function MessageWithErrorHandling() {
  const handleMessage = async (message: Message) => {
    try {
      // Message is already broadcast, handle additional logic
      await saveToDatabase(message);
    } catch (error) {
      if (error.code === 'INSUFFICIENT_FUNDS') {
        toast.error('Not enough BSV to send message');
      } else if (error.code === 'NETWORK_ERROR') {
        toast.error('Network error. Please try again.');
      } else {
        toast.error('Failed to send message');
      }
    }
  };

  return (
    <CompactMessageButton
      recipientAddress="..."
      onMessage={handleMessage}
    />
  );
}

Best Practices

  1. Always Provide Recipient Info: Include name and avatar when available for better UX
  2. Handle Async Operations: The onMessage callback can be async for additional processing
  3. Size Appropriately: Match button size to surrounding UI elements
  4. Provide Feedback: Show success/error states after message operations
  5. Consider Rate Limiting: Implement client-side rate limiting for high-traffic applications
  6. Test Authentication States: Ensure graceful handling when users aren't authenticated

Accessibility

  • ARIA Labels: Button includes proper labels for screen readers
  • Keyboard Navigation: Full keyboard support including focus management
  • Disabled States: Clear visual indicators when button is disabled
  • Loading States: Accessible loading indicators during message sending

Troubleshooting

Button Disabled

  • Solution: Ensure user is authenticated with BitcoinAuthProvider
  • Check: Verify Bitcoin keypair is available in session

Message Not Sending

  • Solution: Check Bitcoin balance for transaction fees
  • Verify: Network connectivity and API endpoint configuration

Popover Not Opening

  • Solution: Check z-index conflicts with other UI elements
  • Verify: No parent elements have overflow: hidden

API Reference

Message Type

interface Message {
  txId: string;        // Bitcoin transaction ID
  content: string;     // Message content
  recipient: string;   // Recipient Bitcoin address
  timestamp: number;   // Unix timestamp
  contentType: string; // MIME type
}

ButtonVariant Type

type ButtonVariant = 
  | 'solid' 
  | 'soft' 
  | 'surface' 
  | 'outline' 
  | 'ghost' 
  | 'classic';