⚠️ Not Currently Maintained — Do Not UseThis SDK is currently unmaintained while the Hypercerts core development team focuses on higher-priority matters, and should not be used. It is currently out of sync with the
@hypercerts-org/lexiconpackage and is likely to break when creating or updating Hypercerts records on ATProto. We'll reassess it over the coming months, informed by developer feedback and developments in AI.In the meantime, we recommend using the
@hypercerts-org/lexiconpackage directly, along with the official AT Protocol SDK and related packages.
React hooks and components for the Hypercerts ATProto SDK.
pnpm add @hypercerts-org/sdk-react @hypercerts-org/sdk-core @tanstack/react-query@hypercerts-org/sdk-react
├── / → Factory, Provider, hooks, types
└── /testing → TestProvider, mocks, createMockATProtoReact
Types are inherited from @hypercerts-org/sdk-core (which re-exports from @hypercerts-org/lexicon):
import type {
HypercertClaim, // Base claim type from lexicon
HypercertCollection,
CreateHypercertParams,
Session,
} from "@hypercerts-org/sdk-react";
// The Hypercert type extends HypercertClaim with ATProto metadata
import type { Hypercert } from "@hypercerts-org/sdk-react";
// Hypercert = HypercertClaim & { uri: string; cid: string }import { createATProtoReact } from "@hypercerts-org/sdk-react";
import { QueryClientProvider } from "@tanstack/react-query";
// 1. Create instance (typically in providers.ts)
const atproto = createATProtoReact({
config: {
oauth: {
clientId: "https://your-app.com/client-metadata.json",
redirectUri: "https://your-app.com/callback",
scope: "atproto",
jwksUri: "https://your-app.com/jwks.json",
jwkPrivate: process.env.ATPROTO_JWK_PRIVATE!,
},
},
});
// 2. Export hooks
export const { Provider, queryClient, useAuth, useProfile, useOrganizations, useHypercerts } = atproto;
// 3. Wrap app with QueryClientProvider
function App() {
return (
<QueryClientProvider client={queryClient}>
<Provider>
<MyApp />
</Provider>
</QueryClientProvider>
);
}
// 4. Use hooks in components
function AuthButton() {
const { status, login, logout } = useAuth();
if (status === "authenticated") {
return <button onClick={logout}>Logout</button>;
}
return <button onClick={() => login("user.bsky.social")}>Login</button>;
}Share a single QueryClient for unified caching:
const queryClient = new QueryClient();
const atproto = createATProtoReact({ config, queryClient });
<QueryClientProvider client={queryClient}>
<WagmiProvider config={wagmiConfig}>
<atproto.Provider>
<App />
</atproto.Provider>
</WagmiProvider>
</QueryClientProvider>;useAuth() → session, status, login, logout, refresh
useProfile(did?) → profile, save, isSaving, isLoading
useRepository(opts?) → repository, isSDS, serverUrl
useOrganizations() → organizations, create (SDS only)
useOrganization(did) → organization (SDS only)
useCollaborators(did) → collaborators, grant, revoke (SDS only)
useHypercerts(did?) → hypercerts, create, fetchNextPage
useHypercert(uri) → hypercert, update, remove
The useProfile hook manages Certified profile data using an upsert pattern:
function ProfileEditor() {
const { profile, save, isSaving, isLoading } = useProfile();
// Profile may be null if user hasn't created one yet
if (isLoading) return <div>Loading...</div>;
return (
<form onSubmit={(e) => {
e.preventDefault();
save({
displayName: "Alice",
description: "Climate researcher",
pronouns: "she/her",
website: "https://alice.com",
});
}}>
<input defaultValue={profile?.displayName ?? ""} />
<button type="submit" disabled={isSaving}>
{profile ? "Update" : "Create"} Profile
</button>
</form>
);
}Key features:
- Returns
nullwhen profile doesn't exist (not an error) save()uses upsert pattern (creates if missing, updates if exists)- Automatically invalidates profile cache on save
- Handles blob uploads for avatar/banner
For manual cache management:
import { atprotoKeys } from "@hypercerts-org/sdk-react";
// Invalidate all hypercerts
queryClient.invalidateQueries({ queryKey: atprotoKeys.allHypercerts() });
// Invalidate specific profile
queryClient.invalidateQueries({ queryKey: atprotoKeys.profile(did) });import { TestProvider, createMockSession } from "@hypercerts-org/sdk-react/testing";
test("renders profile", () => {
render(
<TestProvider mockSession={createMockSession({ handle: "test.bsky.social" })}>
<ProfileComponent />
</TestProvider>,
);
});pnpm build # Build
pnpm typecheck # Type check
pnpm lint # Lint