Documentation Index
Fetch the complete documentation index at: https://docs.deep.space/llms.txt
Use this file to discover all available pages before exploring further.
The auth surface is exported from deepspace. The Better Auth client is wrapped in a React provider, with hooks and components for everything you’d build by hand.
import {
DeepSpaceAuthProvider,
AuthGate, AuthOverlay,
SignedIn, SignedOut, GuestBanner,
useAuth, useAuthUser, useUser, useDisplayName,
signIn, signOut, getAuthToken, clearAuthToken,
authClient, useSession,
} from 'deepspace'
Providers
DeepSpaceAuthProvider
Wraps the tree and initializes the Better Auth client. Required as an ancestor of every auth hook and component.
<DeepSpaceAuthProvider>
<App />
</DeepSpaceAuthProvider>
The scaffolded _app.tsx already mounts this provider at the root. You don’t normally render it yourself.
Hooks
useAuth(): AuthState
Primary auth-state hook. Session-based; updates immediately on sign-in / sign-out.
| Field | Type | Description |
|---|
isLoaded | boolean | true once the Better Auth session has resolved. |
isSignedIn | boolean | The canonical signed-in check. |
userId | string | null | JWT subject; null when signed out. |
sessionId | string | null | Better Auth session ID. |
const { isLoaded, isSignedIn, userId } = useAuth()
if (!isLoaded) return <Skeleton />
if (!isSignedIn) return <SignInPrompt />
useAuthUser(): { isLoaded, isSignedIn, user }
Returns the auth-layer user from Better Auth’s session - { id, fullName, firstName, primaryEmailAddress, ... } | null. Use when you need fields from the OAuth provider’s profile.
Different from useUser(), which merges in storage-layer fields like role and karma.
useUser(): { user, isLoading, refetch }
Returns the storage-layer user, merged with the room-specific role from the app’s users collection. Karma and credits are loaded from the API as nested objects when available.
type UserKarma = {
total: number
breakdown: { publishing: number; content: number; comment: number; curation: number }
rank: number
monthlyKarma: number
monthlyRank: number
}
type UserCredits = {
total: number
subscription: number
bonus: number
purchased: number
}
type StorageUser = {
id: string
name: string
email: string
/** Free-form role string from the user-roles collection; defaults to `'viewer'`. */
role: string
imageUrl?: string
isAdmin?: boolean
publicUsername?: string | null
subscriptionTier?: string | null
subscriptionStatus?: string | null
karma?: UserKarma | null
credits?: UserCredits | null
}
const { user, isLoading } = useUser()
if (isLoading) return null
if (user?.role !== 'admin') return <p>Admins only</p>
// Karma and credits are objects, not numbers:
const totalKarma = user?.karma?.total ?? 0
const availableCredits = user?.credits?.total ?? 0
useUser reads the merged profile + room-role from the record store, so it must be rendered inside a RecordProvider ancestor (not just DeepSpaceAuthProvider). Outside a RecordProvider, user will be null.
Destructure as const { user } = useUser() and read fields off user. Don’t write const { id } = useUser() - id is not at the top level of the return.
useDisplayName(): string | null
Resolves the best available display name (full name → first name → email username). Returns null while loading or signed out.
useSession() and authClient
Re-exports from Better Auth for advanced flows (custom OAuth providers, magic links, etc.).
Components
<AuthOverlay providers={...} />
Modal sign-in UI. Render without an onClose prop and gate on !isSignedIn; auto-hides when signed in.
| Prop | Type | Description |
|---|
providers | Array<'github' | 'google'> | Which OAuth buttons to show. Defaults to ['github', 'google']. Email/password sign-in is always available below the OAuth options. |
onClose | () => void | If provided, renders a close button. Omit for non-dismissible. |
<AuthOverlay providers={['google', 'github']} />
<AuthGate fallback={...} redirectOnSignOut={...} />
Renders children when signed in; renders fallback (default: <AuthOverlay />) otherwise.
| Prop | Type | Description |
|---|
fallback | ReactNode | UI shown to signed-out users. Defaults to non-dismissible <AuthOverlay />. |
redirectOnSignOut | string | Where the user lands on sign-out. Default '/'. Triggers a full reload. |
<AuthGate fallback={<TeaserPage />}>
<ProtectedContent />
</AuthGate>
<SignedIn> and <SignedOut>
Conditional rendering helpers:
<SignedIn><UserMenu /></SignedIn>
<SignedOut><SignInButton /></SignedOut>
<GuestBanner />
A small inline banner prompting sign-in for anonymous visitors.
Functions
signIn / signOut
Re-exports from Better Auth.
import { signOut } from 'deepspace'
await signOut()
getAuthToken(): Promise<string | null>
Returns the current JWT, refreshing it from the auth worker if necessary. Attach to outbound fetch calls:
const r = await fetch('/api/premium', {
headers: { Authorization: `Bearer ${await getAuthToken()}` },
})
clearAuthToken(): void
Clears the cached JWT. Forces the next getAuthToken() call to fetch a fresh one. Useful in tests or after explicit session changes.
Patterns
Auth-state checks
const { isLoaded, isSignedIn } = useAuth()
if (!isLoaded) return <Skeleton />
return isSignedIn ? <App /> : <Landing />
Profile access
const { user, isLoading } = useUser()
if (isLoading) return null
return user ? <Hi name={user.name} /> : <SignInPrompt />
import { signOut } from 'deepspace'
<button onClick={() => signOut()}>Sign out</button>
See also