Learn how to self-host Better Auth Studio in your Next.js application using the App Router.
Prerequisites
Before you begin, ensure you have:
A Next.js application with App Router (Next.js 13+)
Better Auth configured in your project
better-auth-studio installed as a dependency
Installation
Install Dependencies
Install as a regular dependency (not devDependency) for production deployments.
npm install better-auth-studio
Initialize Configuration
Run the init command to create your configuration file: pnpx better-auth-studio init
This creates a studio.config.ts file in your project root: import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
metadata: {
title: "Admin Dashboard" ,
theme: "dark" ,
},
access: {
roles: [ "admin" ],
allowEmails: [ "[email protected] " ],
},
};
export default config ;
Create API Route
The init command automatically creates a catch-all route handler. Create the file manually if needed: app/api/studio/[[...path]]/route.ts
import { betterAuthStudio } from "better-auth-studio/nextjs" ;
import studioConfig from "@/studio.config" ;
const handler = betterAuthStudio ( studioConfig );
export { handler as GET , handler as POST , handler as PUT , handler as DELETE , handler as PATCH };
The [[...path]] catch-all route ensures all studio requests are handled correctly.
Access the Studio
Start your Next.js development server: Access the studio at: http://localhost:3000/api/studio
How It Works
The Next.js adapter (better-auth-studio/nextjs) provides a betterAuthStudio() function that:
Accepts your StudioConfig - Takes your configuration including auth instance and settings
Returns a Request handler - Returns a function that handles Next.js Request objects
Converts requests - Transforms Next.js requests to universal format
Handles all HTTP methods - Supports GET, POST, PUT, DELETE, and PATCH
Injects hooks - Automatically injects last-seen tracking and event hooks if configured
Adapter Implementation
Here’s how the Next.js adapter works internally:
import { handleStudioRequest } from "../core/handler.js" ;
import type { StudioConfig , UniversalRequest , UniversalResponse } from "../types/handler.js" ;
import { injectEventHooks , injectLastSeenAtHooks } from "../utils/hook-injector.js" ;
export function betterAuthStudio ( config : StudioConfig ) {
if ( config . auth ) {
injectLastSeenAtHooks ( config . auth , config );
if ( config . events ?. enabled ) injectEventHooks ( config . auth , config . events );
}
return async ( request : Request ) : Promise < Response > => {
try {
const universalRequest = await requestToUniversal ( request );
const universalResponse = await handleStudioRequest ( universalRequest , config );
return universalToResponse ( universalResponse );
} catch ( error ) {
console . error ( "Studio handler error:" , error );
return new Response ( JSON . stringify ({ error: "Internal server error" }), {
status: 500 ,
headers: { "Content-Type" : "application/json" },
});
}
};
}
Configuration Examples
Basic Setup
Minimal configuration for Next.js:
import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
};
export default config ;
With Access Control
Restrict access to specific roles or emails:
import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
access: {
roles: [ "admin" , "super-admin" ],
allowEmails: [
"[email protected] " ,
"[email protected] "
],
},
};
export default config ;
With Custom Branding
Customize the studio appearance:
import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
metadata: {
title: "Acme Admin Dashboard" ,
theme: "dark" ,
company: {
name: "Acme Inc." ,
website: "https://acme.com" ,
supportEmail: "[email protected] " ,
},
colors: {
primary: "#6366f1" ,
secondary: "#8b5cf6" ,
accent: "#ec4899" ,
},
},
};
export default config ;
With Last-Seen Tracking
Enable user last-seen tracking:
import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
lastSeenAt: {
enabled: true ,
columnName: "lastSeenAt" , // Default value
},
};
export default config ;
When enabling lastSeenAt, make sure to add the column to your user table and run migrations (e.g., prisma migrate dev).
With Event Tracking
Enable authentication event tracking:
import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
tableName: "auth_events" ,
include: [ "sign-in" , "sign-up" , "sign-out" ],
liveMarquee: {
enabled: true ,
pollInterval: 2000 ,
speed: 0.5 ,
},
},
};
export default config ;
Custom Base Path
You can mount the studio at any path by changing the basePath and route location:
const config : StudioConfig = {
auth ,
basePath: "/admin" ,
// ... other options
};
app/admin/[[...path]]/route.ts
import { betterAuthStudio } from "better-auth-studio/nextjs" ;
import studioConfig from "@/studio.config" ;
const handler = betterAuthStudio ( studioConfig );
export { handler as GET , handler as POST , handler as PUT , handler as DELETE , handler as PATCH };
Access at: http://localhost:3000/admin const config : StudioConfig = {
auth ,
basePath: "/dashboard/admin" ,
// ... other options
};
app/dashboard/admin/[[...path]]/route.ts
import { betterAuthStudio } from "better-auth-studio/nextjs" ;
import studioConfig from "@/studio.config" ;
const handler = betterAuthStudio ( studioConfig );
export { handler as GET , handler as POST , handler as PUT , handler as DELETE , handler as PATCH };
Access at: http://localhost:3000/dashboard/admin
Deployment
Vercel
Better Auth Studio works seamlessly on Vercel:
Ensure better-auth-studio is in dependencies (not devDependencies)
Deploy your Next.js app normally
The studio will be available at your configured basePath
The studio works on any platform that supports Next.js:
Netlify - Deploy as a Next.js site
Railway - Deploy with Docker or Nixpacks
Render - Deploy as a web service
Self-hosted - Use next start or Docker
Troubleshooting
Studio Not Loading
If the studio doesn’t load, check:
basePath matches route : Ensure studio.config.ts basePath matches your route folder name
All HTTP methods exported : Verify you’re exporting GET, POST, PUT, DELETE, and PATCH
Config file location : Make sure studio.config.ts is in the project root or adjust the import path
Access Denied Errors
If you get access denied:
Check access.roles : Ensure your user has one of the allowed roles
Check access.allowEmails : Verify your email is in the allowed list
Authentication : Make sure you’re logged in with Better Auth
TypeScript Errors
For TypeScript issues:
npm install --save-dev @types/better-auth
Ensure your tsconfig.json includes:
{
"compilerOptions" : {
"moduleResolution" : "bundler" ,
"esModuleInterop" : true
}
}
Next Steps
Configuration Learn about all configuration options
Express Setup Set up with Express.js