BigBlocks

Follow Button

A follow/unfollow toggle button for BSocial identities with three distinct visual states

Installation

bunx shadcn@latest add https://registry.bigblocks.dev/r/follow-button.json

Usage

Pass the BAP identity key of the user to follow and callbacks to broadcast follow and unfollow transactions. Set isFollowing to reflect the current state.

import { FollowButton } from "@/components/blocks/follow-button"
 
export default function UserProfile({ user }) {
  return (
    <FollowButton
      bapId={user.bapId}
      isFollowing={user.followedByMe}
      onFollow={async (bapId) => {
        const txid = await broadcastFollow(bapId)
        return { txid }
      }}
      onUnfollow={async (bapId) => {
        const txid = await broadcastUnfollow(bapId)
        return { txid }
      }}
      onToggled={(following, result) => {
        console.log(following ? "Now following" : "Unfollowed", result.txid)
      }}
    />
  )
}

Visual States

The button renders three distinct states depending on the follow status and hover interaction:

StateConditionAppearance
FollowNot followingPrimary button with UserPlus icon
FollowingFollowing, not hoveringSecondary/muted button with checkmark
UnfollowFollowing and hovering (requires onUnfollow)Destructive button with UserMinus icon

Variants

VariantDescription
defaultStandard rounded button (px-4 py-2)
compactSmaller button (px-3 py-1.5 text-xs)
pillFully rounded pill shape (rounded-full px-5 py-2)

Compact

Pill

Following State

Custom Labels

Hook: useFollow

Use useFollow when you need the follow state logic without the default button rendering — for example, to drive a custom toggle or an icon-only control.

import { useFollow } from "@/components/blocks/follow-button"
 
export function FollowIcon({ bapId, isFollowing }) {
  const { following, isLoading, handleClick, setIsHovering } = useFollow({
    bapId,
    isFollowing,
    onFollow: async (id) => ({ txid: await broadcastFollow(id) }),
    onUnfollow: async (id) => ({ txid: await broadcastUnfollow(id) }),
  })
 
  return (
    <button
      onClick={handleClick}
      onMouseEnter={() => setIsHovering(true)}
      onMouseLeave={() => setIsHovering(false)}
      disabled={isLoading}
    >
      {following ? <UserCheck /> : <UserPlus />}
    </button>
  )
}

API Reference

FollowButton

PropTypeDefaultDescription
bapIdstring—Required. BAP identity key of the user to follow
onFollow(bapId: string) => Promise<FollowResult>—Required. Called to broadcast a follow action
onUnfollow(bapId: string) => Promise<FollowResult>—Called to broadcast an unfollow action; also enables the unfollow hover state
isFollowingbooleanfalseWhether the current user is already following this user
onToggled(following: boolean, result: FollowResult) => void—Called after a successful follow/unfollow
onError(error: Error) => void—Called when the action fails
labels{ follow?: string; following?: string; unfollow?: string }—Override default button labels
variant"default" | "compact" | "pill""default"Visual variant
disabledbooleanfalseDisable the button
classNamestring—Additional CSS classes

FollowResult

interface FollowResult {
  /** Transaction ID of the follow/unfollow action */
  txid?: string
  /** Raw transaction hex */
  rawtx?: string
  /** Error message if the action failed */
  error?: string
}

useFollow Options

OptionTypeDefaultDescription
bapIdstring—Required. BAP identity key to follow/unfollow
onFollow(bapId: string) => Promise<FollowResult>—Required. Follow broadcast callback
onUnfollow(bapId: string) => Promise<FollowResult>—Unfollow broadcast callback
isFollowingbooleanfalseInitial follow state
onToggled(following: boolean, result: FollowResult) => void—Success callback
onError(error: Error) => void—Error callback
labels{ follow?: string; following?: string; unfollow?: string }—Override default labels

useFollow Return

PropertyTypeDescription
followingbooleanWhether the current user is following the target
isHoveringbooleanWhether the user is hovering over the button
setIsHovering(hovering: boolean) => voidSet the hovering state
actionState"idle" | "following" | "unfollowing"Current action state
isLoadingbooleanWhether a follow/unfollow action is in progress
showUnfollowbooleanWhether the unfollow UI should show
currentLabelstringThe label to display given current state
handleClick() => Promise<void>Handle the follow/unfollow click