Quick Setup Walkthrough
End-to-end setup, from install to a protected Next.js route, with the most common config knobs in one place
Quick Setup Walkthrough
A pragmatic, top-to-bottom walkthrough that wires the plugin into a Payload + Next.js app, sets up the most common knobs (email flows, social providers, custom roles, a custom user collection), and ends with a protected route. Each section links to the deeper reference page.
Prerequisites
- Node.js 20.9+ (the floor required by Next.js 16)
- Next.js 16.2.2 or newer
- Payload v3 on any supported database
- React 19 (App Router)
1. Install
npm install @b3nab/payload-better-authbetter-auth is a direct dependency - no separate install.
2. Create the plugin config in its own file
Keeping the config separate from payload.config.ts keeps things readable and gives you good type inference. Use satisfies BetterAuthPluginOptions so TypeScript validates the shape without widening literal types.
import type { BetterAuthPluginOptions } from '@b3nab/payload-better-auth'
export const payloadBetterAuthConfig = {
logs: 'info',
betterAuth: {
appName: 'My App',
baseURL: process.env.NEXT_PUBLIC_SERVER_URL,
trustedOrigins: [process.env.NEXT_PUBLIC_SERVER_URL!],
},
} satisfies BetterAuthPluginOptionsEven an empty {} works - the plugin ships sensible defaults (admin, two-factor, openAPI in dev, next-cookies, default email flows). Add only what you want to change.
3. Wire it into Payload
import { buildConfig } from 'payload'
import { betterAuthPlugin } from '@b3nab/payload-better-auth'
import { payloadBetterAuthConfig } from '@/lib/payload-better-auth.config'
export default buildConfig({
// ...your existing config
plugins: [betterAuthPlugin(payloadBetterAuthConfig)],
})4. Environment variables
BETTER_AUTH_SECRET= # openssl rand -base64 32
NEXT_PUBLIC_SERVER_URL=http://localhost:30005. Customize the user collection
Treat the user collection like any Payload collection: define it in its own file, type it with CollectionConfigExtend<'user'>, then plug it in.
import type { CollectionConfigExtend } from '@b3nab/payload-better-auth'
export const User: CollectionConfigExtend<'user'> = {
admin: { useAsTitle: 'email', group: 'My App' },
fields: [
{ name: 'nickname', type: 'text' },
{
name: 'posts',
type: 'relationship',
relationTo: 'posts',
hasMany: true,
},
],
hooks: {
afterChange: [
({ doc }) => {
// anything Payload supports
},
],
},
}import { User } from '@/collections/User'
export const payloadBetterAuthConfig = {
extendsCollections: { user: User },
betterAuth: { /* ... */ },
} satisfies BetterAuthPluginOptionsNeed a field that should also be visible to Better Auth's typed APIs (e.g. on session.user)? Add it through betterAuth.user.additionalFields instead - both paths land in the same Payload collection. See Collections.
6. Add social providers
Configure providers under betterAuth.socialProviders. The plugin auto-injects social login buttons into the Payload admin login screen.
betterAuth: {
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
}See Social Login.
7. Add custom roles
Pass your own admin() instance with extended roles via betterAuth.plugins. Reuse the exported ac so your roles speak the same access-control vocabulary as the built-ins.
import { ac, roles } from '@b3nab/payload-better-auth'
const editor = ac.newRole({
payloadcms: ['access'], // can enter the admin
byRole: ['user'],
})
export const acBV = ac
export const rolesBV = { ...roles, editor }import { admin } from 'better-auth/plugins/admin'
import { acBV, rolesBV } from '@/lib/permissions'
export const payloadBetterAuthConfig = {
// ...
betterAuth: {
plugins: [
admin({ ac: acBV, roles: rolesBV }),
],
// ...
},
} satisfies BetterAuthPluginOptionsSee Permissions.
8. Customize email flows
Defaults are already wired to payload.sendEmail(...). Override any handler when you want full control:
betterAuth: {
emailVerification: {
sendOnSignUp: true,
autoSignInAfterVerification: true,
sendVerificationEmail: async ({ user, url }) => {
await myMailer.send({
to: user.email,
subject: 'Verify your email',
html: renderTemplate('verify', { url }),
})
},
},
emailAndPassword: {
enabled: true,
sendResetPassword: async ({ user, url }) => {
await myMailer.send({
to: user.email,
subject: 'Reset your password',
html: renderTemplate('reset', { url }),
})
},
},
}See Email Flows.
9. Create the auth layer for Next.js
import { createAuthLayer } from '@b3nab/payload-better-auth'
import config from '@/payload.config'
import { payloadBetterAuthConfig } from '@/lib/payload-better-auth.config'
export const {
auth,
// checkers
isAuth, isGuest, isUser, isAdmin, isRole,
// guards
guardAuth, guardGuest, guardUser, guardAdmin, guardRole,
} = createAuthLayer(config, payloadBetterAuthConfig)auth is the underlying Better Auth instance, typed through InferBetterAuthInstance<O>. Any plugin you add under betterAuth.plugins (custom roles, 2FA, social providers, etc.) is reflected in the auth.api.* typings out of the box.
10. Protect a route
import { guardAdmin } from '@/lib/auth'
export default async function AdminAreaPage() {
const { user } = await guardAdmin('/login') // bounces if not admin
return <div>Hello {user.email}</div>
}import { guardRole } from '@/lib/auth'
export default async function DashboardPage() {
const { user } = await guardRole({ role: 'editor' }, '/login')
return <div>Welcome, {user.nickname ?? user.email}</div>
}11. Run
pnpm devOpen the admin at http://localhost:3000/admin, register the first user (becomes admin), and verify:
- Sign-up/login work and the verification email lands.
- Social login buttons render on
/admin/loginif you configured providers. - Visiting
/admin/two-factor-setuplets you enrol TOTP. - Your protected Next.js routes redirect when unauthenticated and render when authorized.
Where to go next
- Plugin Options - full list of knobs and their defaults
- Collections - extending auto-generated collections
- Permissions - roles, statements, runtime checks
- Two-Factor - TOTP flow + admin UI
- Social Login - provider setup and auto-injection
- Email Flows - verification, password reset, change-email
- Exports - package entry points (
.,/client,/rsc)