BETTER-AUTH. UI
Integrations

TanStack Start

This guide covers integrating better-auth-ui v3 with TanStack Start. We also install better-auth-tanstack to handle the authentication state and queries.

Starter Project

Want to skip the installation? Check out the starter here:

GitHub - Demo

Installation

Set up the Auth Provider

TanStack Start requires setting up providers slightly differently than a standard React application. Create a root-level provider component:

app/providers.tsx
import { AuthQueryProvider } from "better-auth-tanstack";
import { AuthUIProviderTanstack } from "better-auth-ui/tanstack";
import { Link, useRouter } from "@tanstack/react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { ReactNode } from "react";

import { authClient } from "./lib/auth-client";

// Create a client
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60,
    },
  },
});

export function Providers({ children }: { children: ReactNode }) {
  const router = useRouter();

  return (
    <QueryClientProvider client={queryClient}>
      <AuthQueryProvider>
        <AuthUIProviderTanstack
          authClient={authClient}
          navigate={(href) => router.navigate({ href })}
          replace={(href) => router.navigate({ href, replace: true })}
          Link={({ href, ...props }) => <Link to={href} {...props} />}
        >
          {children}
        </AuthUIProviderTanstack>
      </AuthQueryProvider>
    </QueryClientProvider>
  );
}

Note how we use TanStack Router's useRouter hook and Link component to handle navigation.

Configure the Root Route

Update your root route to use the Providers component:

app/routes/__root.tsx
import {
  HeadContent,
  Outlet,
  Scripts,
  createRootRoute,
} from "@tanstack/react-router";
import type { ReactNode } from "react";

import { Header } from "@/components/header";
import globalsCss from "@/styles/globals.css?url";
import { Providers } from "../providers";

export const Route = createRootRoute({
  head: () => ({
    meta: [
      {
        charSet: "utf-8",
      },
      {
        name: "viewport",
        content: "width=device-width, initial-scale=1",
      },
      {
        title: "Better Auth TanStack Starter",
      },
    ],
    links: [
      { rel: "stylesheet", href: globalsCss },
      { rel: "icon", href: "/favicon.ico" },
      { rel: "apple-touch-icon", href: "/apple-touch-icon.png" },
      { rel: "manifest", href: "/manifest.webmanifest" },
    ],
  }),
  component: RootComponent,
});

function RootComponent() {
  return (
    <RootDocument>
      <Outlet />
    </RootDocument>
  );
}

function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta
          name="viewport"
          content="initial-scale=1, viewport-fit=cover, width=device-width"
        />
        <meta
          name="theme-color"
          media="(prefers-color-scheme: light)"
          content="oklch(1 0 0)"
        />
        <meta
          name="theme-color"
          media="(prefers-color-scheme: dark)"
          content="oklch(0.145 0 0)"
        />

        <HeadContent />
      </head>

      <body>
        <Providers>{children}</Providers>

        <Scripts />
      </body>
    </html>
  );
}

Setting Up Routes

TanStack Start uses a file-based routing system. Here's how to set up your authentication, account, and organization routes using the new v3 containers.

Auth Pages

Create the following route for authentication views:

app/routes/auth/$authView.tsx
import { cn } from "@/lib/utils";
import { AuthView } from "@/components/auth/auth-view";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/auth/$authView")({
  component: RouteComponent,
});

function RouteComponent() {
  const { authView } = Route.useParams();

  return (
    <main className="container flex grow flex-col items-center justify-center gap-3 self-center p-4 md:p-6">
      <AuthView pathname={authView} />

      <p
        className={cn(
          ["callback", "sign-out"].includes(authView) && "hidden",
          "text-muted-foreground text-xs",
        )}
      >
        Powered by{" "}
        <a
          className="text-warning underline"
          href="https://better-auth.com"
          target="_blank"
          rel="noreferrer"
        >
          better-auth.
        </a>
      </p>
    </main>
  );
}

This dynamic route covers all authentication paths, such as sign-in, sign-up, magic-link, forgot-password, two-factor, recover-account, reset-password, sign-out, and the internal callback.

Account Pages

Create a dynamic route for account settings using the AccountView container (defaults to the /account base path):

app/routes/account/$accountView.tsx
import { AccountView } from "@/components/settings/account-view";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/account/$accountView")({
  component: RouteComponent,
});

function RouteComponent() {
  const { accountView } = Route.useParams();
  return (
    <main className="container p-4 md:p-6">
      <AccountView pathname={accountView} />
    </main>
  );
}

You can customize the base path via the provider using account={{ basePath: "/account" }} if needed.

Organization Pages

Create a dynamic route for organization settings using the OrganizationView container (defaults to the /organization base path):

app/routes/organization/$organizationView.tsx
import { OrganizationView } from "@/components/organization/organization-view";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/organization/$organizationView")({
  component: RouteComponent,
});

function RouteComponent() {
  const { organizationView } = Route.useParams();
  return (
    <main className="container p-4 md:p-6">
      <OrganizationView pathname={organizationView} />
    </main>
  );
}

If you prefer slug-based org URLs, set organization={{ pathMode: "slug", basePath: "/organization", slug: currentSlug }} in the provider and structure your routes accordingly:

app/routes/organization/$slug/$organizationView.tsx
import { OrganizationView } from "better-auth-ui";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/organization/$slug/$organizationView")({
  component: RouteComponent,
});

function RouteComponent() {
  const { organizationView } = Route.useParams();
  return (
    <main className="container p-4 md:p-6">
      <OrganizationView pathname={organizationView} />
    </main>
  );
}

This setup provides a solid foundation for integrating Better Auth UI v3 with TanStack Start. You'll get all the benefits of TanStack's powerful routing system along with Better Auth UI's new container-based authentication and settings experiences.