BigBlocks Docs
Components/Social

CompactPostButton

Space-efficient button for creating on-chain social posts with minimal UI footprint, perfect for toolbars and mobile interfaces

A space-efficient button for creating on-chain posts on the Bitcoin blockchain using the bSocial protocol. Designed for toolbars, sidebars, and mobile interfaces where screen space is limited.

View Component Preview →

Installation

npx bigblocks add compact-post-button

Import

import { CompactPostButton } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
onSuccess(result: PostResult) => voidNo-Callback after successful post with transaction details
onError(error: Error) => voidNo-Error callback for handling failures
placeholderstringNo'Write something...'Input placeholder text
maxLengthnumberNo217Maximum post length in characters
classNamestringNo-Additional CSS classes for customization

PostResult Interface

interface PostResult {
  txid: string;           // Transaction ID on blockchain
  content: string;        // Post content
  author: {
    idKey: string;        // BAP identity key
    address: string;      // Bitcoin address
  };
  timestamp: number;      // Creation timestamp
  fee: number;           // Transaction fee in satoshis
}

Basic Usage

import { CompactPostButton } from 'bigblocks';

export default function Toolbar() {
  return (
    <div className="toolbar">
      <CompactPostButton />
    </div>
  );
}

Advanced Usage

With Success Handler

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

export default function SocialToolbar() {
  const handlePostSuccess = (result) => {
    console.log('Posted! TxID:', result.txid);
    toast.success('Post created on blockchain!');
    
    // Update feed or trigger refresh
    refreshFeed();
  };

  return (
    <CompactPostButton 
      onSuccess={handlePostSuccess}
      placeholder="Share your thoughts..."
    />
  );
}

Error Handling

import { CompactPostButton } from 'bigblocks';
import { useState } from 'react';

export default function PostWithErrorHandling() {
  const [lastError, setLastError] = useState(null);

  const handleError = (error) => {
    setLastError(error);
    
    if (error.code === 'INSUFFICIENT_FUNDS') {
      toast.error('Insufficient BSV balance for posting');
    } else if (error.code === 'AUTH_REQUIRED') {
      toast.error('Please sign in to post');
    } else {
      toast.error('Failed to create post');
    }
  };

  return (
    <div>
      <CompactPostButton 
        onSuccess={(result) => console.log('Success:', result)}
        onError={handleError}
      />
      
      {lastError && (
        <div className="text-red-500 text-sm mt-2">
          Last error: {lastError.message}
        </div>
      )}
    </div>
  );
}

Custom Character Limit

import { CompactPostButton } from 'bigblocks';

export default function TwitterStylePost() {
  return (
    <CompactPostButton 
      maxLength={280}
      placeholder="What's happening?"
      onSuccess={(result) => {
        console.log('Tweet-style post created:', result);
      }}
    />
  );
}

Custom Styling

import { CompactPostButton } from 'bigblocks';

export default function StyledCompactPost() {
  return (
    <CompactPostButton 
      className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600"
      placeholder="Share something amazing..."
      onSuccess={(result) => console.log('Styled post:', result)}
    />
  );
}

Common Patterns

Mobile Navigation Bar

import { CompactPostButton } from 'bigblocks';

export default function MobileNav() {
  return (
    <nav className="fixed bottom-0 left-0 right-0 bg-white border-t">
      <div className="flex items-center justify-between p-2">
        <button className="p-2">Home</button>
        <button className="p-2">Search</button>
        
        <CompactPostButton 
          className="flex-1 mx-2"
          placeholder="Quick post..."
          onSuccess={(result) => {
            // Navigate to post
            router.push(`/posts/${result.txid}`);
          }}
        />
        
        <button className="p-2">Messages</button>
        <button className="p-2">Profile</button>
      </div>
    </nav>
  );
}

Floating Action Button

import { CompactPostButton } from 'bigblocks';

export default function FloatingPost() {
  return (
    <div className="fixed bottom-4 right-4 z-50">
      <CompactPostButton 
        className="shadow-lg rounded-full"
        placeholder="Share update..."
        onSuccess={(result) => {
          toast.success('Posted!');
          refreshFeed();
        }}
      />
    </div>
  );
}
import { CompactPostButton } from 'bigblocks';

export default function SidebarWithPost() {
  return (
    <aside className="w-64 bg-gray-50 p-4">
      <h3 className="font-semibold mb-4">Quick Actions</h3>
      
      <div className="space-y-3">
        <CompactPostButton 
          className="w-full"
          placeholder="Quick update..."
          onSuccess={(result) => console.log('Posted:', result)}
        />
        
        <button className="w-full bg-gray-200 p-2 rounded">
          View Feed
        </button>
        
        <button className="w-full bg-gray-200 p-2 rounded">
          My Posts
        </button>
      </div>
    </aside>
  );
}

Toolbar Integration

import { CompactPostButton } from 'bigblocks';

export default function AppToolbar() {
  return (
    <div className="bg-white border-b px-4 py-2">
      <div className="flex items-center space-x-4">
        <h1 className="text-xl font-bold">Social App</h1>
        
        <div className="flex-1 max-w-md">
          <CompactPostButton 
            placeholder="Share something..."
            onSuccess={(result) => {
              // Add to feed immediately
              addToFeed(result);
            }}
          />
        </div>
        
        <div className="flex space-x-2">
          <button>Notifications</button>
          <button>Settings</button>
        </div>
      </div>
    </div>
  );
}

Reply Interface

import { CompactPostButton } from 'bigblocks';

export default function ReplyBox({ parentPost }) {
  return (
    <div className="border-l-2 border-gray-200 pl-4 ml-4">
      <CompactPostButton 
        placeholder={`Reply to ${parentPost.author.name}...`}
        maxLength={200}
        className="text-sm"
        onSuccess={(result) => {
          // Link reply to parent post
          linkReply(parentPost.txid, result.txid);
        }}
      />
    </div>
  );
}

Authentication Requirements

The CompactPostButton requires Bitcoin authentication context to create posts:

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

function App() {
  return (
    <BitcoinQueryProvider>
      <BitcoinAuthProvider config={{ apiUrl: '/api' }}>
        <CompactPostButton 
          onSuccess={(result) => console.log('Posted:', result)}
        />
      </BitcoinAuthProvider>
    </BitcoinQueryProvider>
  );
}

API Integration

Post Creation Endpoint

The component integrates with the bSocial protocol API:

POST /api/social/posts
{
  content: string;
  contentType: 'text/plain';
}

// Response
{
  success: boolean;
  result: {
    txid: string;
    content: string;
    author: {
      idKey: string;
      address: string;
    };
    timestamp: number;
    fee: number;
  };
}

Error Response

{
  success: false;
  error: {
    code: string;
    message: string;
  };
}

Styling and Theming

Default Styles

The component uses minimal styling to fit various design systems:

.compact-post-button {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  transition: all 0.2s;
}

.compact-post-input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
}

.compact-post-submit {
  opacity: 0.6;
  transition: opacity 0.2s;
}

.compact-post-submit:enabled {
  opacity: 1;
  cursor: pointer;
}

Theme Integration

import { CompactPostButton, BitcoinThemeProvider } from 'bigblocks';

function ThemedApp() {
  return (
    <BitcoinThemeProvider theme="orange">
      <CompactPostButton 
        className="themed-post-button"
        onSuccess={(result) => console.log('Themed post:', result)}
      />
    </BitcoinThemeProvider>
  );
}

Mobile Considerations

Touch-Friendly Design

import { CompactPostButton } from 'bigblocks';

export default function MobileOptimizedPost() {
  return (
    <CompactPostButton 
      className="min-h-[44px] text-base" // iOS touch target size
      placeholder="Tap to post..."
      onSuccess={(result) => {
        // Haptic feedback on mobile
        if ('vibrate' in navigator) {
          navigator.vibrate(50);
        }
        handleSuccess(result);
      }}
    />
  );
}

Responsive Layout

import { CompactPostButton } from 'bigblocks';

export default function ResponsivePost() {
  return (
    <>
      {/* Mobile: Fixed bottom */}
      <div className="md:hidden fixed bottom-0 left-0 right-0 p-2 bg-white border-t">
        <CompactPostButton 
          className="w-full"
          placeholder="Post..."
          onSuccess={handlePost}
        />
      </div>
      
      {/* Desktop: In header */}
      <div className="hidden md:block">
        <CompactPostButton 
          className="max-w-md"
          placeholder="Share your thoughts..."
          onSuccess={handlePost}
        />
      </div>
    </>
  );
}

Performance Optimization

Debounced Input

The component automatically debounces input to prevent excessive re-renders:

import { CompactPostButton } from 'bigblocks';
import { memo } from 'react';

// Memoize for performance in lists
const MemoizedCompactPost = memo(CompactPostButton);

export default function PostList({ items }) {
  return (
    <div>
      {items.map(item => (
        <div key={item.id} className="mb-4">
          <p>{item.content}</p>
          <MemoizedCompactPost 
            placeholder="Reply..."
            onSuccess={(result) => handleReply(item.id, result)}
          />
        </div>
      ))}
    </div>
  );
}

Troubleshooting

Common Issues

Post not creating

  • Ensure user is authenticated with Bitcoin wallet
  • Check wallet has sufficient BSV balance for fees (~$0.001)
  • Verify API endpoint is accessible

Character limit not working

  • The maxLength prop enforces limit on input
  • Default is 217 characters for optimal blockchain storage

Component not responsive

  • Add responsive classes to className prop
  • Use container queries for better adaptability

Debug Mode

import { CompactPostButton } from 'bigblocks';

export default function DebugPost() {
  return (
    <CompactPostButton 
      onSuccess={(result) => {
        console.log('Debug - Post Result:', {
          txid: result.txid,
          fee: `${result.fee} satoshis`,
          timestamp: new Date(result.timestamp).toISOString(),
          content: result.content,
          author: result.author
        });
      }}
      onError={(error) => {
        console.error('Debug - Post Error:', {
          code: error.code,
          message: error.message,
          stack: error.stack
        });
      }}
    />
  );
}

Best Practices

  1. Feedback: Always provide success/error feedback to users
  2. Character Limits: Set appropriate limits for your use case
  3. Loading States: Component shows loading state during posting
  4. Error Recovery: Handle common errors gracefully
  5. Mobile UX: Ensure touch targets meet accessibility standards

Notes

  • Posts are permanently stored on the Bitcoin blockchain
  • Each post costs a small transaction fee (~$0.001)
  • Content is limited to text for optimal blockchain efficiency
  • Posts are visible globally once confirmed on-chain