BigBlocks Docs
Components/Profiles

ProfileViewer

Display a complete view of a BAP profile with avatar, information, actions, and status

A component for displaying a complete view of a BAP (Bitcoin Attestation Protocol) profile, including avatar, profile information, Bitcoin address, publication status, and action buttons.

Installation

npx bigblocks add profile-viewer

Import

import { ProfileViewer } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
profileProfileInfoYes-Profile data to display
showAddressbooleanNotrueShow Bitcoin address with copy function
showPublishStatusbooleanNotrueShow published/unpublished status
showActionsbooleanNotrueShow edit and publish buttons
onEdit() => voidNo-Edit button click handler
onPublish() => voidNo-Publish button click handler
classNamestringNo-Additional CSS classes
variant'surface' | 'classic' | 'ghost'No'surface'Visual style variant
size'1' | '2' | '3' | '4'No'3'Component size

Basic Usage

import { ProfileViewer } from 'bigblocks';

export default function UserProfile() {
  const profile = {
    id: 'bap-123',
    address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
    isPublished: true,
    name: 'Bitcoin Builder',
    alternateName: '@btc_builder',
    description: 'Building the future of money on Bitcoin',
    image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=builder',
    url: 'https://bitcoin.org',
    email: 'builder@bitcoin.org',
    paymail: 'builder@moneybutton.com'
  };

  return (
    <ProfileViewer
      profile={profile}
      onEdit={() => {
        console.log('Edit profile');
      }}
      onPublish={() => {
        console.log('Publish profile');
      }}
    />
  );
}

Profile Structure

interface ProfileInfo {
  id: string;                    // Profile identifier
  address: string;               // Bitcoin address
  isPublished: boolean;          // Publication status
  
  // Basic Information
  '@context'?: string;           // JSON-LD context
  '@type'?: 'Person' | 'Organization';
  name?: string;                 // Display name
  alternateName?: string;        // Username/handle
  description?: string;          // Bio/description
  
  // Images
  image?: string;                // Avatar/profile image
  logo?: string;                 // Organization logo
  banner?: string;               // Banner/cover image
  
  // Contact Information
  url?: string;                  // Website URL
  email?: string;                // Email address
  paymail?: string;             // Bitcoin paymail
  bitcoinAddress?: string;       // Additional Bitcoin address
  
  // Personal Details
  givenName?: string;           // First name
  familyName?: string;          // Last name
  legalName?: string;           // Legal/full name
  
  // Location Information
  location?: string;             // General location
  homeLocation?: string;         // Home location
  streetAddress?: string;        // Street address
  addressLocality?: string;      // City
  addressRegion?: string;        // State/province
  postalCode?: string;           // ZIP/postal code
  addressCountry?: string;       // Country
}

Advanced Usage

Complete Profile with All Fields

import { ProfileViewer } from 'bigblocks';

export default function CompleteProfile() {
  const profile = {
    id: 'bap-456',
    address: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2',
    isPublished: true,
    '@context': 'https://schema.org',
    '@type': 'Person',
    name: 'Satoshi Nakamoto',
    alternateName: '@satoshi',
    description: 'Creator of Bitcoin. Pseudonymous developer who designed and implemented the world\'s first cryptocurrency.',
    image: 'https://api.dicebear.com/7.x/avataaars/svg?seed=satoshi',
    banner: 'https://images.unsplash.com/photo-1640340434855-6084b1f4901c',
    url: 'https://bitcoin.org',
    email: 'satoshi@gmx.com',
    paymail: 'satoshi@relayx.io',
    givenName: 'Satoshi',
    familyName: 'Nakamoto',
    location: 'Worldwide',
    bitcoinAddress: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
  };

  return (
    <ProfileViewer
      profile={profile}
      variant="surface"
      size="4"
      onEdit={() => {
        // Open profile editor
        setEditMode(true);
      }}
      onPublish={() => {
        // Publish to blockchain
        publishProfile(profile.id);
      }}
    />
  );
}

Read-only Profile View

import { ProfileViewer } from 'bigblocks';

export default function PublicProfile({ profile }) {
  return (
    <ProfileViewer
      profile={profile}
      showActions={false}
      showPublishStatus={false}
      variant="classic"
    />
  );
}

Organization Profile

import { ProfileViewer } from 'bigblocks';

export default function CompanyProfile() {
  const orgProfile = {
    id: 'org-789',
    address: '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy',
    isPublished: true,
    '@type': 'Organization',
    name: 'Bitcoin Foundation',
    legalName: 'Bitcoin Foundation, Inc.',
    description: 'Promoting the responsible adoption of Bitcoin through education, advocacy, and ecosystem development.',
    logo: 'https://bitcoinfoundation.org/logo.png',
    banner: 'https://bitcoinfoundation.org/banner.jpg',
    url: 'https://bitcoinfoundation.org',
    email: 'info@bitcoinfoundation.org',
    streetAddress: '1230 NE 3rd Avenue',
    addressLocality: 'Miami',
    addressRegion: 'FL',
    postalCode: '33132',
    addressCountry: 'US'
  };

  return (
    <ProfileViewer
      profile={orgProfile}
      size="4"
      onEdit={() => editOrganization(orgProfile.id)}
      onPublish={() => publishOrganization(orgProfile.id)}
    />
  );
}

With Custom Styling

import { ProfileViewer } from 'bigblocks';

export default function StyledProfile({ profile }) {
  return (
    <ProfileViewer
      profile={profile}
      className="shadow-lg border-2 border-orange-200"
      variant="ghost"
      size="3"
      onEdit={() => {
        // Custom edit handler
      }}
    />
  );
}

Different Variants

import { ProfileViewer } from 'bigblocks';

export default function ProfileVariants({ profile }) {
  return (
    <div className="space-y-6">
      {/* Surface variant - default */}
      <ProfileViewer
        profile={profile}
        variant="surface"
        onEdit={() => console.log('Edit surface')}
      />
      
      {/* Classic variant */}
      <ProfileViewer
        profile={profile}
        variant="classic"
        onEdit={() => console.log('Edit classic')}
      />
      
      {/* Ghost variant */}
      <ProfileViewer
        profile={profile}
        variant="ghost"
        onEdit={() => console.log('Edit ghost')}
      />
    </div>
  );
}

Different Sizes

import { ProfileViewer } from 'bigblocks';

export default function ProfileSizes({ profile }) {
  return (
    <div className="grid grid-cols-2 gap-4">
      {/* Small */}
      <ProfileViewer
        profile={profile}
        size="1"
        onEdit={() => console.log('Edit small')}
      />
      
      {/* Medium */}
      <ProfileViewer
        profile={profile}
        size="2"
        onEdit={() => console.log('Edit medium')}
      />
      
      {/* Large */}
      <ProfileViewer
        profile={profile}
        size="3"
        onEdit={() => console.log('Edit large')}
      />
      
      {/* Extra Large */}
      <ProfileViewer
        profile={profile}
        size="4"
        onEdit={() => console.log('Edit extra large')}
      />
    </div>
  );
}

Common Patterns

Profile Page Layout

import { ProfileViewer, SocialFeed } from 'bigblocks';

export default function ProfilePage({ userId }) {
  const [profile, setProfile] = useState(null);
  const [isEditing, setIsEditing] = useState(false);

  useEffect(() => {
    const loadProfile = async () => {
      const profileData = await fetchProfile(userId);
      setProfile(profileData);
    };
    
    loadProfile();
  }, [userId]);

  const handleEdit = () => {
    setIsEditing(true);
  };

  const handlePublish = async () => {
    try {
      await publishProfileToBlockchain(profile.id);
      setProfile(prev => ({ ...prev, isPublished: true }));
      toast.success('Profile published to blockchain!');
    } catch (error) {
      toast.error('Failed to publish profile');
    }
  };

  if (!profile) return <div>Loading...</div>;

  return (
    <div className="max-w-4xl mx-auto space-y-6">
      <ProfileViewer
        profile={profile}
        onEdit={handleEdit}
        onPublish={handlePublish}
      />
      
      <div className="mt-8">
        <SocialFeed
          feedType="user"
          userId={userId}
        />
      </div>
    </div>
  );
}

Profile Comparison

import { ProfileViewer } from 'bigblocks';

export default function ProfileComparison({ profiles }) {
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
      {profiles.map(profile => (
        <ProfileViewer
          key={profile.id}
          profile={profile}
          showActions={false}
          variant="classic"
          size="3"
        />
      ))}
    </div>
  );
}

Profile with Conditional Actions

import { ProfileViewer } from 'bigblocks';
import { useAuth } from 'bigblocks';

export default function ConditionalProfile({ profile }) {
  const { user, isAuthenticated } = useAuth();
  
  const isOwnProfile = isAuthenticated && user?.id === profile.id;
  const canEdit = isOwnProfile || user?.isAdmin;

  return (
    <ProfileViewer
      profile={profile}
      showActions={canEdit}
      showPublishStatus={isOwnProfile}
      onEdit={canEdit ? () => editProfile(profile.id) : undefined}
      onPublish={isOwnProfile ? () => publishProfile(profile.id) : undefined}
    />
  );
}

Profile with Status Updates

import { ProfileViewer } from 'bigblocks';
import { useState, useEffect } from 'react';

export default function LiveProfile({ profileId }) {
  const [profile, setProfile] = useState(null);
  const [publishing, setPublishing] = useState(false);

  useEffect(() => {
    // Subscribe to profile updates
    const unsubscribe = subscribeToProfile(profileId, (updatedProfile) => {
      setProfile(updatedProfile);
    });

    return unsubscribe;
  }, [profileId]);

  const handlePublish = async () => {
    setPublishing(true);
    try {
      await publishProfile(profileId);
      // Profile update will come through subscription
    } catch (error) {
      console.error('Publish failed:', error);
    } finally {
      setPublishing(false);
    }
  };

  return (
    <div>
      {publishing && (
        <div className="mb-4 p-3 bg-blue-100 text-blue-700 rounded">
          Publishing profile to blockchain...
        </div>
      )}
      
      <ProfileViewer
        profile={profile}
        onPublish={handlePublish}
        onEdit={() => editProfile(profileId)}
      />
    </div>
  );
}

Mobile Responsive Profile

import { ProfileViewer } from 'bigblocks';

export default function MobileProfile({ profile }) {
  return (
    <div className="px-4">
      <ProfileViewer
        profile={profile}
        size="2" // Smaller on mobile
        className="w-full"
        onEdit={() => {
          // Open mobile-friendly editor
          openMobileEditor(profile.id);
        }}
      />
    </div>
  );
}

Publishing Status

The component displays different states based on isPublished:

Published Profile

  • ✅ Green checkmark with "Published" text
  • Profile data is on the blockchain
  • Publicly discoverable

Unpublished Profile

  • ⏳ Orange indicator with "Draft" text
  • Profile exists locally only
  • Not yet on blockchain

Features

  • Complete Profile Display: Avatar, name, description, and all profile fields
  • Bitcoin Address: Displayed with copy-to-clipboard functionality
  • Publication Status: Visual indicator of blockchain publication state
  • Action Buttons: Edit and publish controls when enabled
  • Responsive Design: Adapts to different screen sizes
  • Multiple Variants: Surface, classic, and ghost styling options
  • Size Options: Four different size presets
  • Type Support: Both Person and Organization profiles
  • Schema.org Compliance: Structured data format support

Address Display

When showAddress is true, the Bitcoin address is displayed with:

  • Truncated format for readability
  • Copy-to-clipboard button
  • Tooltip showing full address
  • Visual feedback on copy

Action Buttons

When showActions is true, displays:

  • Edit Button: Opens profile editor (requires onEdit handler)
  • Publish Button: Publishes to blockchain (requires onPublish handler)

Accessibility

  • Screen reader support for all content
  • Keyboard navigation for interactive elements
  • High contrast support
  • Focus indicators on actionable items
  • Alternative text for images

Best Practices

  1. Complete Profiles: Encourage users to fill all relevant fields
  2. Image Optimization: Use optimized images for avatars and banners
  3. Privacy Awareness: Remind users that published data is permanent
  4. Error Handling: Handle missing or invalid profile data gracefully
  5. Performance: Lazy load large images and banners

Troubleshooting

Profile Not Displaying

  • Check that profile object has required id and address fields
  • Verify profile data structure matches ProfileInfo interface
  • Ensure component is wrapped in proper providers

Images Not Loading

  • Verify image URLs are accessible
  • Check for CORS issues with external images
  • Provide fallback images for missing avatars

Actions Not Working

  • Ensure onEdit and onPublish handlers are provided
  • Check that showActions is true (default)
  • Verify user permissions for profile actions

API Integration

The component works with BAP profile APIs:

// Get profile data
GET /api/bap/profiles/{id}

// Publish profile
POST /api/bap/profiles/{id}/publish

// Update profile
PUT /api/bap/profiles/{id}

Notes for Improvement

Enhanced Implementation: The actual component provides more sophisticated features than the basic prompt described:

  • Complete BAP profile schema support with all schema.org fields
  • Multiple visual variants and size options for different use cases
  • Advanced status indicators for publication state
  • Better address handling with copy functionality
  • Support for both Person and Organization profile types
  • Responsive design optimizations for mobile devices