Next.js
This guide covers integrating @daveyplate/better-auth-ui into your Next.js project.
Starter Project
Want to skip the installation? Check out the starter here:
App Router
Follow these steps to set up @daveyplate/better-auth-ui in your Next.js project using the App Router:
AuthUIProvider
The first step is to set up the <AuthUIProvider /> client component with your authClient, wrapping your layout. This is required to provide the context & hooks to your authentication components across your application.
"use client"
import { AuthUIProvider } from "@daveyplate/better-auth-ui"
import Link from "next/link"
import { useRouter } from "next/navigation"
import type { ReactNode } from "react"
import { authClient } from "@/lib/auth-client"
export function Providers({ children }: { children: ReactNode }) {
    const router = useRouter()
    return (
        <AuthUIProvider
            authClient={authClient}
            navigate={router.push}
            replace={router.replace}
            onSessionChange={() => {
                // Clear router cache (protected routes)
                router.refresh()
            }}
            Link={Link}
        >
            {children}
        </AuthUIProvider>
    )
}Note: Since the Next.js App Router caches routes by default, navigation to protected routes may fail until you perform a router.refresh() to clear the cache. To prevent this issue, you must use router.refresh() in the provided onSessionChange callback. This forces Next.js to clear the router cache and reload middleware-protected content, ensuring subsequent navigations accurately reflect the current auth state.
Once configured, wrap your layout component with the Providers component:
import type { ReactNode } from "react"
import { Providers } from "./providers"
export default function RootLayout({ children }: { children: ReactNode }) {
    return (
        <html lang="en">
            <body>
                <Providers>{children}</Providers>
            </body>
        </html>
    )
}The <AuthUIProvider /> can be fully customized with plugins, styles, localization and more. For more information and all available props, see the <AuthUIProvider /> component documentation.
Auth Pages
Create a dynamic route segment for authentication views in app/auth/[authView]/page.tsx.
import { AuthView } from "@daveyplate/better-auth-ui"
import { authViewPaths } from "@daveyplate/better-auth-ui/server"
export const dynamicParams = false
export function generateStaticParams() {
    return Object.values(authViewPaths).map((path) => ({ path }))
}
export default async function AuthPage({ params }: { params: Promise<{ path: string }> }) {
    const { path } = await params
    
    return (
        <main className="container flex grow flex-col items-center justify-center self-center p-4 md:p-6">
            <AuthView path={path} />
        </main>
    )
}The newly created dynamic route covers the following paths by default:
- /auth/sign-in– Sign in via email/password and social providers
- /auth/sign-up– New account registration
- /auth/magic-link– Email login without a password
- /auth/forgot-password– Trigger email to reset forgotten password
- /auth/two-factor– Two-factor authentication
- /auth/recover-account– Recover account via backup code
- /auth/reset-password– Set new password after receiving reset link
- /auth/sign-out– Log the user out of the application
- /auth/callback– Internal route to handle Auth callbacks
- /auth/accept-invitation– Accept an invitation to an organization
Ensure that any links to the authentication process utilize these routes accordingly. All routes will render the <AuthView /> component and automatically handle navigation and authentication flow.
Account Pages
import { AccountView } from "@daveyplate/better-auth-ui"
import { accountViewPaths } from "@daveyplate/better-auth-ui/server"
export const dynamicParams = false
export function generateStaticParams() {
    return Object.values(accountViewPaths).map((path) => ({ path }))
}
export default async function AccountPage({ params }: { params: Promise<{ path: string }> }) {
    const { path } = await params
    return (
        <main className="container p-4 md:p-6">
            <AccountView path={path} />
        </main>
    )
}Organization Pages
import { OrganizationView } from "@daveyplate/better-auth-ui"
import { organizationViewPaths } from "@daveyplate/better-auth-ui/server"
export const dynamicParams = false
export function generateStaticParams() {
    return Object.values(organizationViewPaths).map((path) => ({ path }))
}
export default async function OrganizationPage({ params }: { params: Promise<{ path: string }> }) {
    const { path } = await params
    return (
        <main className="container p-4 md:p-6">
            <OrganizationView path={path} />
        </main>
    )
}If you prefer slug-based org URLs, set organization={{ pathMode: "slug", basePath: "/organization", slug: currentSlug }} in the AuthUIProvider and structure your routes accordingly:
import { OrganizationView } from "@daveyplate/better-auth-ui"
import { organizationViewPaths } from "@daveyplate/better-auth-ui/server"
export default async function OrganizationPage({ params }: { params: Promise<{ path: string }> }) {
    const { path } = await params
    return (
        <main className="container p-4 md:p-6">
            <OrganizationView path={path} />
        </main>
    )
}Pages Router
Follow these steps to set up @daveyplate/better-auth-ui in your Next.js project using the Pages Router:
AuthUIProvider
First set up the <AuthUIProvider /> within your custom App component in _app.tsx.
import type { AppProps } from "next/app"
import { AuthUIProvider } from "@daveyplate/better-auth-ui"
import { useRouter } from "next/router"
import Link from "next/link"
import { authClient } from "@/lib/auth-client"
export default function App({ Component, pageProps }: AppProps) {
    const router = useRouter()
    return (
        <AuthUIProvider
            authClient={authClient}
            navigate={router.push}
            replace={router.replace}
            Link={Link}
        >
            <Component {...pageProps} />
        </AuthUIProvider>
    )
}Now the authentication context is available across your entire application.
Auth Pages
Create a page with a dynamic segment in your Pages directory in pages/auth/[authView].tsx
import { AuthView } from "@daveyplate/better-auth-ui"
import { authViewPaths } from "@daveyplate/better-auth-ui/server"
export default function AuthPage({ path }: { path: string }) {
    return (
        <main className="container mx-auto flex grow flex-col items-center justify-center gap-3 self-center p-4 md:p-6">
            <AuthView path={path} />
        </main>
    )
}
export async function getStaticPaths() {
    return {
        paths: Object.values(authViewPaths).map((path) => ({ params: { path } })),
        fallback: false
    }
}
export async function getStaticProps({ params }: { params: { path: string } }) {
    return { props: { path: params.path } }
}These routes match the list shown in the App Router section above.
Account Pages
import { AccountView } from "@daveyplate/better-auth-ui"
import { accountViewPaths } from "@daveyplate/better-auth-ui/server"
export default function AccountPage({ path }: { path: string }) {
    return (
        <main className="container mx-auto p-4 md:p-6">
            <AccountView path={path} />
        </main>
    )
}
export async function getStaticPaths() {
    return {
        paths: Object.values(accountViewPaths).map((path) => ({ params: { path } })),
        fallback: false
    }
}
export async function getStaticProps({ params }: { params: { path: string } }) {
    return { props: { path: params.path } }
}Organization Pages
import { OrganizationView } from "@daveyplate/better-auth-ui"
import { organizationViewPaths } from "@daveyplate/better-auth-ui/server"
export default function OrganizationPage({ path }: { path: string }) {
    return (
        <main className="container mx-auto p-4 md:p-6">
            <OrganizationView path={path} />
        </main>
    )
}
export async function getStaticPaths() {
    return {
        paths: Object.values(organizationViewPaths).map((path) => ({ params: { path } })),
        fallback: false
    }
}
export async function getStaticProps({ params }: { params: { path: string } }) {
    return { props: { organizationView: params.path } }
}