TanStack Start
Integrate Better Auth UI with TanStack Start
Prerequisites
Make sure you've completed the Quick Start guide first.
Integration
Configure AuthProvider
Configure AuthProvider with TanStack Router's navigation.
import { Link, useNavigate } from "@tanstack/react-router"
import { ThemeProvider } from "next-themes"
import type { ReactNode } from "react"
import { AuthProvider } from "@/components/auth/auth-provider"
import { authClient } from "@/lib/auth-client"
import { Toaster } from "./ui/sonner"
export function Providers({ children }: { children: ReactNode }) {
const navigate = useNavigate()
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<AuthProvider
authClient={authClient}
magicLink
socialProviders={["github", "google"]}
navigate={navigate}
Link={({ href, ...props }) => <Link to={href} {...props} />}
>
{children}
<Toaster />
</AuthProvider>
</ThemeProvider>
)
}import { AuthProvider } from "@better-auth-ui/shadcn"
import { Link, useNavigate } from "@tanstack/react-router"
import { useTheme } from "next-themes"
import type { ReactNode } from "react"
import { authClient } from "@/lib/auth-client"
import { Toaster } from "./ui/sonner"
export function Providers({ children }: { children: ReactNode }) {
const navigate = useNavigate()
const { theme, setTheme } = useTheme()
return (
<AuthProvider
authClient={authClient}
magicLink
multiSession
socialProviders={["github", "google"]}
redirectTo="/dashboard"
navigate={navigate}
settings={{
theme,
setTheme
}}
Link={({ href, ...props }) => <Link to={href} {...props} />}
>
{children}
<Toaster />
</AuthProvider>
)
}The navigate and Link props integrate with TanStack Router's navigation system. For TanStack Router, you can pass the navigate function directly since it accepts { href, replace } options.
Update the Root Route
Wrap your application with the Providers component in your root route.
import { TanStackDevtools } from "@tanstack/react-devtools"
import { createRootRoute, HeadContent, Scripts } from "@tanstack/react-router"
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"
import { ThemeProvider } from "next-themes"
import type { ReactNode } from "react"
import { Header } from "@/components/header"
import { Providers } from "@/components/providers"
import appCss from "@/styles/app.css?url"
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: "utf-8"
},
{
name: "viewport",
content: "width=device-width, initial-scale=1"
},
{
title: "Start shadcn/ui Example"
}
],
links: [
{
rel: "stylesheet",
href: appCss
}
]
}),
shellComponent: RootDocument
})
function RootDocument({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<HeadContent />
</head>
<body className="antialiased min-h-svh flex flex-col">
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<Providers>
<Header />
{children}
</Providers>
</ThemeProvider>
<TanStackDevtools
config={{
position: "bottom-right"
}}
plugins={[
{
name: "TanStack Router",
render: <TanStackRouterDevtoolsPanel />
}
]}
/>
<Scripts />
</body>
</html>
)
}Create the Auth Page
Create a dynamic auth page that renders the appropriate authentication view based on the path.
import { viewPaths } from "@better-auth-ui/react/core"
import { createFileRoute, redirect } from "@tanstack/react-router"
import { Auth } from "@/components/auth/auth"
export const Route = createFileRoute("/auth/$path")({
beforeLoad({ params: { path } }) {
if (!Object.values(viewPaths.auth).includes(path)) {
throw redirect({ to: "/" })
}
},
component: AuthPage
})
function AuthPage() {
const { path } = Route.useParams()
return (
<div className="min-h-svh flex items-center justify-center p-4 md:p-6">
<Auth path={path} />
</div>
)
}import { Auth } from "@better-auth-ui/shadcn"
import { viewPaths } from "@better-auth-ui/shadcn/core"
import { createFileRoute, redirect } from "@tanstack/react-router"
export const Route = createFileRoute("/auth/$path")({
beforeLoad({ params: { path } }) {
if (!Object.values(viewPaths.auth).includes(path)) {
throw redirect({ to: "/" })
}
},
component: AuthPage
})
function AuthPage() {
const { path } = Route.useParams()
return (
<div className="grow flex items-center justify-center p-4 md:p-6">
<Auth path={path} />
</div>
)
}The viewPaths.auth object contains all valid auth paths: sign-in, sign-up, sign-out, forgot-password, reset-password, and magic-link.
Protecting Routes
Use the useAuthenticate hook to protect routes and access session data.
import { createFileRoute, Link } from "@tanstack/react-router"
import { Spinner } from "@/components/ui/spinner"
import { useAuthenticate } from "@/hooks/auth/use-authenticate"
export const Route = createFileRoute("/dashboard")({
component: Dashboard
})
function Dashboard() {
const { data: sessionData } = useAuthenticate()
if (!sessionData) {
return (
<div className="min-h-svh flex items-center justify-center">
<Spinner />
</div>
)
}
return (
<div className="min-h-svh flex flex-col items-center justify-center gap-4">
<h1 className="text-2xl">Hello, {sessionData.user.email}</h1>
<Link to="/auth/$path" params={{ path: "sign-out" }}>
Sign Out
</Link>
</div>
)
}import { UserButton } from "@better-auth-ui/shadcn"
import { useAuthenticate } from "@better-auth-ui/shadcn/react"
import { createFileRoute } from "@tanstack/react-router"
import { Spinner } from "@/components/ui/spinner"
export const Route = createFileRoute("/dashboard")({
component: Dashboard
})
function Dashboard() {
const { data: sessionData } = useAuthenticate()
if (!sessionData) {
return (
<div className="min-h-svh flex items-center justify-center">
<Spinner />
</div>
)
}
return (
<div className="min-h-svh flex flex-col items-center justify-center gap-4">
<UserButton />
</div>
)
}The useAuthenticate hook will automatically redirect unauthenticated users to the sign-in page.
Example Project
For a complete working example, check out the start-shadcn-example in the repository.
Last updated on