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.
Installation
npx bigblocks add compact-post-button
Import
import { CompactPostButton } from 'bigblocks';
Props
Prop | Type | Required | Default | Description |
---|---|---|---|---|
onSuccess | (result: PostResult) => void | No | - | Callback after successful post with transaction details |
onError | (error: Error) => void | No | - | Error callback for handling failures |
placeholder | string | No | 'Write something...' | Input placeholder text |
maxLength | number | No | 217 | Maximum post length in characters |
className | string | No | - | 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>
);
}
Sidebar Integration
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
- Feedback: Always provide success/error feedback to users
- Character Limits: Set appropriate limits for your use case
- Loading States: Component shows loading state during posting
- Error Recovery: Handle common errors gracefully
- Mobile UX: Ensure touch targets meet accessibility standards
Related Components
- PostButton - Full-featured post creation component
- SocialFeed - Display social posts
- PostCard - Individual post display
- CompactMessageButton - Compact messaging interface
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