BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
September 10, 2026AuthNextJS

Better Auth UI in an Existing Next.js App: Routes, Sessions, and Permissions

Connect Better Auth UI to an existing Next.js app while keeping API routes, session refresh, and editorial permissions clear.

Better Auth UI in an Existing Next.js App: Routes, Sessions, and Permissions

You already have a Better Auth server. Sign-in works through its API, but your Next.js app still needs sign-in, recovery, account, and security pages. Before adding a UI package, identify which layer each package will own. That decision determines your routes, session refresh behavior, and permission checks.

Updated October 1, 2026: this guide uses Stack and Codegen 4.2.0 with @btst/better-auth-ui@3.0.1 in an existing Next.js App Router application. It is a client integration for an application-owned Better Auth backend. Start with the compatible dependency versions: this companion requires Better Auth 1.7.6, React/React DOM at least 19.2.6 and Tailwind CSS at least 4.3.2. Existing companion 2 integrations also need the version 3 migration guide.

Keep the three route bases separate#

A typical application has three different addresses:

AddressPurpose
/api/authYour Better Auth HTTP handler
/api/dataBTST business-plugin APIs
/pages/auth/sign-inA companion sign-in page

Changing the site path to /p changes the companion page URLs to /p/auth/* and /p/account/*. It does not move Better Auth's HTTP handler. A sign-in form posting to the site-page path is a configuration error, even if that page looks correct.

Keep the existing Better Auth server, database, migrations, secrets, and providers. Check its endpoint before debugging the UI. If the backend does not yet exist, finish that integration first using Better Auth's installation documentation.

Choose the companion explicitly#

Run the generator from a clean commit in the existing app and review the generated diff:

BASH
  1. 1
npx @btst/codegen@4.2.0 init --framework nextjs --plugins better-auth-ui

Use the framework installation guide for shared UI and provider prerequisites. The command selects auth and account UI; optional organization, passkey, and API-key behavior still needs deliberate configuration in the matching Better Auth server and browser client. Installing a peer dependency does not activate its runtime feature.

Do not mix examples from older BTST releases into the generated Stack 4 setup. In this release, createClientStack() receives shared API, site, and QueryClient settings. The companion registers through authClientPlugin() and accountClientPlugin() from @btst/better-auth-ui/client.

The browser client factory below targets an existing same-origin Better Auth handler. Create and reuse a browser client in your application's provider wiring; do not construct a new one on every render.

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
import { createAuthClient } from "better-auth/react";

export function createAppAuthClient(baseURL?: string) {
  return createAuthClient({
    ...(baseURL ? { baseURL } : {}),
    basePath: "/api/auth",
  });
}

If you change basePath, change it to the actual backend handler path. For a separate auth origin, review cookies, allowed origins, and credentials as part of the backend integration; a UI route change alone cannot fix cross-origin session transport.

Check account and verification URLs#

With the default /pages mount, companion 3 opens account details at /pages/account/account and email verification at /pages/auth/verify-email. Codegen 4.2.0 sets the post-authentication redirect to /pages/account/account. Existing links to /pages/account/settings or /pages/auth/email-verification need an explicit migration.

To retain those older URLs, merge this configuration into the existing overrides.auth object:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
import type { AuthPluginOverrides } from "@btst/better-auth-ui/client";

export const preservedAuthPaths = {
  viewPaths: {
    settings: { account: "settings" },
    auth: { verifyEmail: "email-verification" },
  },
  redirectTo: "/pages/account/settings",
} satisfies Pick<AuthPluginOverrides, "viewPaths" | "redirectTo">;

Keep the existing authClient and onSessionChange alongside these settings. If a header uses a standalone AuthProvider, give it the same viewPaths so its links agree with the Stack pages. Adjust the /pages prefix when your site uses another mount. Test direct visits, emailed links and the redirect after sign-in.

Companion 3 also changes provider options and extension registration. Old feature booleans and overrides.account.account are not the new configuration; register the account route factory and use the released migration guide for the corresponding plugin and profile options. Do not fix a TypeScript mismatch with a cast that hides the old configuration.

Refresh server-rendered state after sign-in#

Successful sign-in changes the session used by the browser. Your server-rendered navigation or protected content may still reflect the previous request until Next.js refreshes it.

The companion provider's auth override supports onSessionChange. In the Next.js integration, wire it to router.refresh() using the router from next/navigation. Keep this callback in the client provider. The generated framework wiring is the starting point, especially if your app already has a QueryClient or router provider.

Verify both directions: a signed-out visitor signs in and gains the expected account view; a signed-in visitor signs out and loses protected access. Test a fresh page request as well as navigation inside the app. A stale header and an unauthorized API response are different failures and need different fixes.

Authenticated does not mean authorized to publish#

The UI companion reads the Better Auth session for its own views. Permission to edit Blog posts or CMS content belongs to your business authorization policy. Map your server identity into BTST's authorization contracts separately, using the authorization guide.

For an editorial site, a useful access matrix is:

RequestServer policy
Read a published articleAllow public access
Read a private draftDeny anonymous users and ordinary members; authorize editors explicitly
Create or update a postRequire the intended editorial permission
Publish or deleteRequire the specific publishing or deletion permission

Check this matrix against the server endpoints, not only the presence of buttons. Client-side visibility improves usability, but a caller can send an HTTP request without using your page.

Verify the integration before release#

Open the sign-in page directly, complete a real permitted sign-in in a test environment, refresh the account page, and sign out. Confirm recovery and verification links return to the correct application origin. For any optional auth feature, exercise its backend and UI together before advertising it.

Then test a direct unauthorized business-API request. Adding account pages should not expand a user's editorial permissions. If you also publish articles, the Next.js draft and cache guide covers the separate public-content boundary.

Use the companion evaluation page to check fit, then follow the current installation reference for the integration reference. The released Codegen source and linked version 3 migration guide are the versioned references for this article.

In This Post

Keep the three route bases separateChoose the companion explicitlyCheck account and verification URLsRefresh server-rendered state after sign-inAuthenticated does not mean authorized to publishVerify the integration before release