Skip to main content

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.
FieldTypeDescription
isLoadedbooleantrue once the Better Auth session has resolved.
isSignedInbooleanThe canonical signed-in check.
userIdstring | nullJWT subject; null when signed out.
sessionIdstring | nullBetter 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.
PropTypeDescription
providersArray<'github' | 'google'>Which OAuth buttons to show. Defaults to ['github', 'google']. Email/password sign-in is always available below the OAuth options.
onClose() => voidIf 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.
PropTypeDescription
fallbackReactNodeUI shown to signed-out users. Defaults to non-dismissible <AuthOverlay />.
redirectOnSignOutstringWhere 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 />

Sign-out button

import { signOut } from 'deepspace'

<button onClick={() => signOut()}>Sign out</button>

See also