Sessions
Handle sessions with @onmax/nuxt-better-auth using useUserSession().
- `useUserSession()` is auto-imported and returns: user, session, loggedIn, ready, signIn, signUp, signOut, fetchSession
- SSR: session is fetched server-side via cookies and hydrated — `ready` is `true` after hydration
- Client: `.client` is `null` during SSR, available after hydration
- Split model: use `useUserSession()` in pages/components, use `requireUserSession(event)` in server handlers
- Configure session lifetime in `server/auth.config.ts` via `session.expiresIn` and `session.cookieCache`
- Use `<BetterAuthState>` or `ready` ref to avoid loading flashes
- Force refresh: `fetchSession({ force: true })`
Read more: https://better-auth.nuxt.dev/raw/core-concepts/sessions.md
Source: https://github.com/nuxt-modules/better-auth
const {
user,
session,
loggedIn,
ready,
signIn,
signUp,
signOut,
fetchSession,
} = useUserSession()
SSR behavior
During server-side rendering (SSR), the module fetches the session using incoming request cookies and populates state before rendering. A client plugin then keeps the state in sync after hydration.
- Server render:
userandsessionare set when a valid session cookie exists, andreadyistrue. - Server runtime client access:
useUserSession().clientisnullduring SSR. - After hydration: State stays in sync with client-side updates.
Use a split model:
- In pages and components, use
useUserSession()state and SSR-safe fetch helpers (useAuthRequestFetch,useAuthAsyncData). - In server handlers and routes, use server utilities (
serverAuth,getUserSession,requireUserSession).
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event)
return { id: user.id, email: user.email }
})
Optional: Skip Hydrated SSR Session Fetch
By default, SSR pages still bootstrap the client session with an initial /api/auth/get-session request. This enables Better Auth's session refresh behavior.
If you want to skip that request when user and session are already hydrated from SSR, enable the option below:
export default defineNuxtConfig({
auth: {
session: {
skipHydratedSsrGetSession: true,
},
},
})
For prerendered or cached pages, the client plugin fetches the session after mount. Use ready or <BetterAuthState> to avoid flashes in those cases.
Auth Readiness vs Domain Data Readiness
ready and <BetterAuthState> guarantee only that auth hydration has finished. They do not guarantee that additional auth-bound domain data (billing state, role membership, entitlements) has loaded.
When rendering UI that depends on both auth and domain data, fetch that domain data SSR-first:
const { data: customerState } = await useFetch('/api/auth/customer/state')
const { data: customerById } = await useFetch('/api/auth/customer/123/state')
Endpoint payloads are inferred from your Better Auth config (core + plugins), including dynamic auth paths.
const { data: customerState, pending, error } = await useAuthAsyncData(
'customer-state',
requestFetch => requestFetch('/api/auth/customer/state'),
)
This prevents first-paint UI flips where unauthenticated defaults briefly appear before domain data resolves.
Cookie vs Database Sessions
Database sessions (default): stored in DB, revocable, visible in admin JWE sessions (database-less): encrypted cookie, no server storage
Handling Loading State
<script setup lang="ts">
const { user, loggedIn, ready } = useUserSession()
</script>
<template>
<div v-if="!ready">Loading...</div>
<div v-else-if="loggedIn">Welcome, {{ user?.name }}</div>
<div v-else>Please log in</div>
</template>
Server-Side Session Access
For server handlers and API routes, use the server utilities:
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event)
return { id: user.id, email: user.email }
})
Session Refresh
Force refresh the session data:
<script setup lang="ts">
const { fetchSession, ready } = useUserSession()
</script>
<template>
<button :disabled="!ready" @click="fetchSession({ force: true })">
Refresh session
</button>
</template>
Session Lifetime
Configure session duration in your auth config:
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
export default defineServerAuth({
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
cookieCache: {
enabled: true,
maxAge: 5 * 60, // 5 minutes
},
},
})