Skip to content

@scribe-atp/social

function LikeButton(props: LikeButtonProps): JSX.Element

A React button that creates an AT Protocol site.standard.graph.recommend record (“recommend”) for an article when clicked. Opens a popup OAuth flow at social.scribe-atp.app, then updates to a confirmed state on success.

LikeButtonProps

Prop Type Required Description
documentUri string AT URI of the site.standard.document record
publicationUri string AT URI of the site.standard.publication record
title string Article title — shown in the like popup
serviceUrl string Override the social service base URL. Defaults to "https://social.scribe-atp.app"
className string Additional CSS class appended to the base scribe-atp-like-button class
children ReactNode | ((isLiked: boolean) => ReactNode) Custom button label. Omit to use the defaults "Like" / "Liked ✓"
onSuccess () => void Called after a successful like
defaultLiked boolean Initial liked state. Pass true from an SSR loader to avoid a flash of unconfirmed state on first render
import { LikeButton } from '@scribe-atp/social';
<LikeButton
documentUri="at://did:plc:abc123/site.standard.document/3jxtctq7kqm2y"
publicationUri="at://did:plc:abc123/site.standard.publication/3mp4nd46xwr2h"
title="My Article Title"
/>

Getting the URIs: Use fetchArticleBySlug from @scribe-atp/core — it returns { article, uri } where uri is the document AT URI. fetchSite returns a Site object whose uri is the publication AT URI. If you’re using @scribe-atp/react-router-framework, createArticleRouteLoader includes documentUri in the loader return.

Liked state persistence: Once liked, the result is stored in localStorage under the key scribe:recommended:{documentUri}. The button renders in its confirmed state on future visits without another network call.

Accessibility: Uses aria-pressed to communicate liked state. The button remains in the tab order after being activated — it is never disabled.


function ShareButton(props: ShareButtonProps): JSX.Element

A React button that opens a popup for the reader to share the article via their Bluesky account. After a successful share, the button briefly enters a confirmed state then resets, allowing the reader to share again.

ShareButtonProps

Prop Type Required Description
documentUri string AT URI of the site.standard.document record
publicationUri string AT URI of the site.standard.publication record
title string Article title — shown in the share popup
canonicalUrl string Canonical URL of the article. Defaults to window.location.href
serviceUrl string Override the social service base URL. Defaults to "https://social.scribe-atp.app"
className string Additional CSS class appended to the base scribe-atp-share-button class
children ReactNode | ((isShared: boolean) => ReactNode) Custom button label. Omit to use the defaults "Share" / "Shared ✓"
onSuccess () => void Called after a successful share
import { ShareButton } from '@scribe-atp/social';
<ShareButton
documentUri="at://did:plc:abc123/site.standard.document/3jxtctq7kqm2y"
publicationUri="at://did:plc:abc123/site.standard.publication/3mp4nd46xwr2h"
title="My Article Title"
/>

Note: Unlike LikeButton and SubscribeButton, the shared state is not persisted to localStorage. The confirmed state resets after 3 seconds so the reader can share again.


function SubscribeButton(props: SubscribeButtonProps): JSX.Element

A React button that follows an author’s publication on the AT Protocol when clicked. Opens a popup OAuth flow, then updates to a confirmed state on success. When the reader is already subscribed, clicking the button opens an unsubscribe confirmation popup — the button acts as a toggle.

SubscribeButtonProps

Prop Type Required Description
publicationUri string AT URI of the site.standard.publication record
title string Publication name — shown in the subscribe/unsubscribe popup
serviceUrl string Override the social service base URL. Defaults to "https://social.scribe-atp.app"
className string Additional CSS class appended to the base scribe-atp-subscribe-button class
children ReactNode | ((isSubscribed: boolean) => ReactNode) Custom button label. Omit to use the defaults "Subscribe" / "Subscribed ✓". The render prop receives isSubscribed so you can show different copy in each state
onSuccess () => void Called after a successful subscription
onUnsubscribe () => void Called after a confirmed unsubscription
defaultSubscribed boolean Initial subscribed state. Pass true from an SSR loader to avoid a flash of unconfirmed state on first render
import { SubscribeButton } from '@scribe-atp/social';
<SubscribeButton
publicationUri="at://did:plc:abc123/site.standard.publication/3mp4nd46xwr2h"
title="My Site"
/>

Getting the publicationUri: fetchSite from @scribe-atp/core returns a Site object whose uri field is the publication AT URI. createArticleRouteLoader also returns publicationUri in the loader data.

Subscribed state persistence: Stored in localStorage under scribe:subscribed:{publicationUri}. Cleared automatically after a confirmed unsubscription.

Accessibility: Uses aria-pressed to communicate subscribed state. The button remains in the tab order after being activated — it is never disabled.


Exported for reading or setting liked/subscribed state outside the components — e.g. for pre-checking state server-side or in tests.

function isRecommended(documentUri: string): boolean

Returns true if the current user has already liked the given document.


function markRecommended(documentUri: string): void

Persists a liked state to localStorage. Useful if you’ve handled the OAuth flow yourself and want the button to reflect it.


function isSubscribed(publicationUri: string): boolean

Returns true if the current user has already subscribed to the given publication.


function markSubscribed(publicationUri: string): void

Persists a subscribed state to localStorage.


All four storage functions above are SSR-safe — localStorage access is wrapped in a try/catch. They return false / do nothing when called in environments where localStorage is unavailable.


  1. The user clicks a button. A 480×640 popup window opens at social.scribe-atp.app with the relevant AT URI, a title, and a one-time token as query params.
  2. The user authenticates with their Bluesky account via AT Protocol OAuth.
  3. On success, the service writes the like, share, or follow record and notifies the opener page. Notification uses postMessage if window.opener is available, or the button polls /status/:token as a fallback (handles browsers that block cross-origin postMessage after popup close).
  4. The button transitions to its confirmed state. For LikeButton and SubscribeButton the state is persisted to localStorage; for ShareButton the confirmed state resets after 3 seconds.

The polling fallback runs for up to 30 seconds (20 attempts at 1.5 s intervals) before stopping.

See the Social interactions guide for full integration examples.