This library is in early development. Expect breaking changes.
API Reference

Composables

API reference for client-side composables.

useUserSession

The primary composable for accessing authentication state. Returns reactive user, session, and auth client.

pages/login.vue
const { loggedIn, user, session, client, signIn, signOut } = useUserSession()
loggedIn
ComputedRef<boolean>
true if the user is currently authenticated.
user
Ref<AuthUser | null>
The current user object, inferred from your config.
session
Ref<AuthSession | null>
The current session object.
ready
ComputedRef<boolean>
true when initial session resolution is complete (from SSR hydration or client fetch).
client
AuthClient | null
Direct access to the Better Auth client instance. Available on client runtime; null during SSR/server runtime.
useUserSession().client is browser-only. During SSR/server runtime it is null.For SSR-safe auth access:
  • Use user, session, loggedIn, ready, and fetchSession from useUserSession().
  • Use useAuthRequestFetch() or useAuthAsyncData() for auth-bound data in pages.
  • Use server helpers like serverAuth(event), getUserSession(event), or requireUserSession(event) in server/ handlers.

Methods

signIn

Proxies Better Auth signIn.

await signIn.email({
  email: 'user@example.com',
  password: 'password'
})

Promise Behavior

Methods like signIn return a promise that resolves when the server responds, not when local state updates.

// This awaits the server response
await client.signIn.email({ email, password })

// Local state updates asynchronously after
// Use onSuccess callback for actions that depend on updated state
await client.signIn.email(
  { email, password },
  { onSuccess: () => navigateTo('/dashboard') }
)

If no onSuccess callback is provided in method options, signIn will:

  • navigate to route.query.redirect (or custom auth.redirectQueryKey) when it is a local path
  • otherwise fallback to auth.redirects.authenticated when configured and an authenticated session is established
  • otherwise no automatic navigation
await signIn.email({ email, password }) // redirects to safe `?redirect=...` or auth.redirects.authenticated

signUp

Proxies Better Auth signUp with the same onSuccess behavior as signIn.

await signUp.email({
  email: 'user@example.com',
  password: 'password'
})

Like signIn, if no callback is provided, signUp follows the same redirect precedence: query redirect > auth.redirects.authenticated (only when authenticated) > no auto-redirect.

signOut

Signs the user out and clears the local session state.

await signOut()

If auth.redirects.logout is configured, signOut() will navigate there automatically (client-side), unless you provide onSuccess.

Options

await signOut({
  onSuccess: () => navigateTo('/'),
})

waitForSession()

Waits for session state to be ready. Resolves when user is logged in or after 5 second timeout.

await waitForSession()
// Session is now ready (or timed out)
Use this when you need to ensure session state before proceeding. The function always resolves - it doesn't throw or return a value.

fetchSession

Manually triggers a session refresh. Useful if you've updated user data on the server via a side channel.

await fetchSession()

Options

await fetchSession({
  headers, // optional HeadersInit
  force: true, // disables Better Auth cookie cache for this fetch
})

updateUser

Updates the user on the server and optimistically patches local state. Local state reverts if the server call fails.

await updateUser({ name: 'New Name' })
During SSR, updateUser only patches local state since no client is available.
Reactivity: user and session are global states using useState. Changes in one component are instantly reflected everywhere.

useAuthRequestFetch

Returns Nuxt's request-scoped fetch function. On SSR it preserves request context (including cookies); on client it behaves like regular fetch.

useAuthRequestFetch() defaults to GET. For endpoints that only allow POST (or another method), pass method explicitly to preserve response type inference.

When endpoint typing is enabled, useFetch('/api/auth/...') and useLazyFetch('/api/auth/...') infer payloads directly from your Better Auth config:

pages/app.vue
const { data: customerState } = await useFetch('/api/auth/customer/state')
customerState.value?.activeSubscriptions[0]?.toUpperCase()

const { data: customerById } = await useLazyFetch('/api/auth/customer/123/state')
customerById.value?.customerId.toUpperCase()

Global $fetch keeps Nitro InternalApi response inference, but path autocomplete is best on useFetch/useLazyFetch and useAuthRequestFetch.

pages/app.vue
const requestFetch = useAuthRequestFetch()
const state = await requestFetch('/api/auth/customer/state')
const postOnly = await requestFetch('/api/auth/customer/post-only', { method: 'POST' })

Use this when you need low-level control and want to build your own data loader pattern.

useAuthAsyncData

SSR-safe helper for auth-bound data loading.

pages/app.vue
const { data: customerState, pending, error } = await useAuthAsyncData(
  'customer-state',
  requestFetch => requestFetch('/api/auth/customer/state'),
)

When route typing is enabled, payload types for /api/auth/* endpoints are inferred automatically.

By default:

  • requireAuth is true (unauthenticated users resolve to null without calling the endpoint).
  • data defaults to null.
  • errors are exposed through error.
pages/app.vue
const { data } = await useAuthAsyncData(
  'public-profile',
  requestFetch => requestFetch('/api/profile/public'),
  { requireAuth: false },
)

useAction

Creates a reusable async action handle with normalized error state.

components/ProfileForm.vue
const saveProfile = useAction(async (payload: { name: string }) => {
  return await $fetch('/api/profile', { method: 'PATCH', body: payload })
})

await saveProfile.execute({ name: 'Max' })

if (saveProfile.status.value === 'error') {
  console.error(saveProfile.error.value?.message)
}

useAction returns the same handle shape as useSignIn/useSignUp:

execute
(...args) => Promise<void>
Executes the action and never throws.
status
'idle' | 'pending' | 'success' | 'error'
Current status of the latest execute() call.
data
any | null
Last successful result.
error
AuthActionError | null
Normalized error from thrown errors or { error } responses.

useAuthClientAction

Wraps plugin/client methods from useUserSession().client in the same action handle pattern.

pages/pricing.vue
const checkout = useAuthClientAction((client) => client.checkout)

await checkout.execute({ slug: 'pro' })

if (checkout.status.value === 'error') {
  console.error(checkout.error.value?.message)
}

Nested methods are supported via selector functions:

pages/app/index.vue
const openPortal = useAuthClientAction((client) => client.customer.portal)
await openPortal.execute()

useSignIn

Returns a keyed action handle for useUserSession().signIn.*. Each method handle exposes template-friendly async state, similar to composables like useFetch.

pages/login.vue
const { execute, data, status, error } = useSignIn('email')

await execute(
  { email, password, rememberMe },
  {
    onSuccess: () => navigateTo('/app'),
    onError: (ctx) => console.error(ctx.error),
  },
)

if (status.value === 'error') {
  console.error(error.value?.message)
}

For OAuth sign-in, use the social key and pass the provider in the payload:

pages/login.vue
await useSignIn('social').execute({ provider: 'github' })

Provider keys are inferred from server/auth.config.ts socialProviders keys. When callbackURL is omitted, the module auto-fills it using the same safe redirect order as other auth flows:

  • safe query redirect (auth.redirectQueryKey)
  • auth.redirects.authenticated
  • otherwise no fallback callback URL

Use renaming to avoid collisions when you use multiple methods in the same scope:

pages/login.vue
const {
  execute: loginWithEmail,
  status: statusEmail,
  error: errorEmail,
} = useSignIn('email')

const {
  execute: loginWithPasskey,
  status: statusPasskey,
  error: errorPasskey,
} = useSignIn('passkey')

Each method returns an action handle:

execute
(...args) => Promise<void>
Calls the underlying Better Auth method and never throws.
status
'idle' | 'pending' | 'success' | 'error'
Current status of the latest execute() call.
data
any | null
Last successful result. Cleared on each new execute() and on errors.
error
AuthActionError | null
Normalized error for the latest call (cleared on new execute()).
Each sign-in method has independent state. When you call execute() multiple times, only the latest call updates status and error.

Error state and promise behavior

Use status, data, and error as your source of truth. The action handle always sets status='error' and populates normalized error when a sign-in attempt fails.

Better Auth methods can signal failure by throwing or by resolving to a { error } result. In both cases, the action handle updates status and error, and await execute() always resolves.
pages/login.vue
const { execute, data, status, error } = useSignIn('email')

await execute({ email, password })

if (status.value === 'error') {
  console.error(error.value?.message)
}

if (status.value === 'success') {
  console.log(data.value)
}
useUserSignIn and useUserSignUp were renamed to useSignIn and useSignUp in alpha. The API switched from map-style access (useUserSignIn().email) to keyed access (useSignIn('email')) in alpha. OAuth provider aliases (for example useSignIn('github')) were removed. Use useSignIn('social').execute({ provider: 'github' }). error changed from unknown | null to AuthActionError | null in alpha. The message alias field was removed in alpha. Use error.value?.message. execute() changed twice in alpha:
  • old: await execute() could reject
  • previous alpha: await execute() resolved { ok: true, data } | { ok: false, error }
  • new: await execute() resolves void, and you read status / data / error
If you relied on raw payloads, use error.raw.

useSignUp

Same API as useSignIn, but wraps useUserSession().signUp.*.

pages/signup.vue
const { execute, data, status, error } = useSignUp('email')

await execute(
  { email, password, name },
  {
    onSuccess: () => navigateTo('/welcome'),
    onError: (ctx) => console.error(ctx.error),
  },
)

useSignUp returns the same action handle shape (execute, status, data, error) and follows the same error normalization semantics as useSignIn.