Create managed test users from the templates you define. Keep real accounts out of your everyday auth checks.
Switch into a managed test user in one click and reload the app against the new Better Auth session.
Inspect the current session view your app exposes, including user fields and any approved metadata.
Patch only the fields you explicitly allow, then refresh the app with the updated auth state.
Define stable personas like Admin, Editor, and Viewer so auth-gated UI is easy to verify and easy to repeat.
Runs only when DEV_AUTH_ENABLED=true outside production. Keep it explicit, local, and easy to disable.
Define templates once, but keep DB-backed devtools config on the server. The panel stays client-safe by receiving serializable props from your layout.
I've been working on Aceternity for the past 2 years. Here's a timeline of my journey.
Install the package from npm.
$ pnpm add better-auth-devtools
Define your templates and host-app callbacks, then connect them to your real user model. In Next.js App Router, keep database-backed devtools code on the server and pass panel props into a client wrapper.
import {
createDevtoolsIntegration,
defineDevtoolsConfig,
} from "better-auth-devtools/plugin";
export const devtools = createDevtoolsIntegration(defineDevtoolsConfig({
templates: {
admin: { label: "Admin", meta: { role: "admin" } },
editor: { label: "Editor", meta: { role: "editor" } },
viewer: { label: "Viewer", meta: { role: "viewer" } },
},
editableFields: [
{
key: "role",
label: "Role",
type: "select",
options: ["admin", "editor", "viewer"],
},
],
async createManagedUser(args) {
// Create a real user in your app database and return the real ID.
const user = await db.user.create({
data: {
email: args.email,
name: args.template.label,
role: String(args.template.meta?.role ?? "viewer"),
},
});
return {
userId: user.id,
email: user.email,
label: args.template.label,
};
},
async getSessionView(args) {
const user = await db.user.findUnique({ where: { id: args.userId } });
return {
userId: args.userId,
email: user?.email,
label: user?.name,
fields: {
sessionId: args.sessionId,
role: user?.role ?? "viewer",
},
editableFields: ["role"],
};
},
async patchSession(args) {
await db.user.update({
where: { id: args.userId },
data: { role: String(args.patch.role ?? "viewer") },
});
return {
userId: args.userId,
fields: {
sessionId: args.sessionId,
role: String(args.patch.role ?? "viewer"),
},
editableFields: ["role"],
};
},
}), {
position: "bottom-right",
triggerLabel: "Auth DevTools",
});Run your app with DEV_AUTH_ENABLED=true in development. Create managed test users, switch sessions, inspect the current session, and patch approved fields from the panel. If you use Prisma or another ORM, make sure the plugin storage model exists before testing the panel.
Use the server plugin from your Better Auth config, use devtoolsClientPlugin() in the auth client, and pass panel props into a client wrapper in Next.js App Router.
import { betterAuth } from "better-auth";
import { devtools } from "./devtools";
export const auth = betterAuth({
database,
plugins: [devtools.serverPlugin],
});"use client";
import { createAuthClient } from "better-auth/react";
import { BetterAuthDevtools } from "better-auth-devtools/react";
import type { BetterAuthDevtoolsProps } from "better-auth-devtools/react";
import { devtoolsClientPlugin } from "better-auth-devtools/plugin";
export const authClient = createAuthClient({
plugins: [devtoolsClientPlugin()],
});
export function DevtoolsWrapper({
panelProps,
}: {
panelProps: BetterAuthDevtoolsProps;
}) {
return <BetterAuthDevtools {...panelProps} />;
}createManagedUser must create a real user in your app database.
The plugin provides its own Better Auth schema. After adding it, rerun npx auth@latest migrate for built-in adapters, or npx auth@latest generate before your Prisma or Drizzle migration flow.
In Next.js App Router, pass panelProps from a server layout into a client wrapper instead of importing a DB-backed devtools module directly into the client.