Installation
Install payload-better-auth and Better Auth, check peer versions, and set the environment variables Better Auth needs.
Start with an existing Payload app, including its Next.js routes, admin layout, TypeScript aliases and core dependencies. Install the plugin together with Better Auth. The optional Better Auth packages are only needed for the features they provide.
Requirements
| Dependency | Version | Notes |
|---|---|---|
payload | >=3.90.0 <4 | |
@payloadcms/next | >=3.90.0 <4 | |
@payloadcms/ui | >=3.90.0 <4 | |
better-auth | >=1.7.7 <2 | 1.7.7 fixes a critical Magic Link account takeover. 1.6 is not supported. |
next | >=16.4.1 <17 | |
react | >=19.2.1 <20 | |
@better-auth/api-key | >=1.7.7 <2 | Optional. Only for API keys. |
@better-auth/passkey | >=1.7.7 <2 | Optional. Only for passkeys. |
| Node | >=22.23.4 | Node 20 is end-of-life and unpatched. |
These floors exist for security fixes in each peer. The reasons are listed under 0.14. For Payload 4 beta, see Payload 4.
Install
npm i @delmaredigital/payload-better-auth better-auth @payloadcms/db-postgresOptional plugins. Install only what you use:
npm i @better-auth/passkeynpm i @better-auth/api-keydrizzle-orm (pinned to 0.45.2, matching Payload 3.92.1) and qrcode.react are regular dependencies and install automatically.
Environment variables
# Required: use separate, randomly generated secrets
PAYLOAD_SECRET=replace-with-a-random-secret-at-least-32-characters
BETTER_AUTH_SECRET=replace-with-another-random-secret-at-least-32-characters
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/my-app
# App origin: set this to your custom domain in production
BETTER_AUTH_URL=http://localhost:3000
# OAuth providers (if using social login)
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...Base URL on Vercel
Use the configured app origin first, then fall back to the Vercel deployment URL or localhost:
export function getBaseUrl() {
if (process.env.BETTER_AUTH_URL) return process.env.BETTER_AUTH_URL
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`
return 'http://localhost:3000'
}On a custom domain, BETTER_AUTH_URL must win over VERCEL_URL, which identifies an individual deployment. Set it to your public origin in production; use a preview-specific value or leave it unset in previews.
The quick start passes its result to Better Auth as baseURL and trustedOrigins.

