mike :)
mike :)17h ago

TanStack Router + Better Auth Setup

hi, i'm setting convex better auth up in a tanstack router vite app. i primarily followed the start docs and only deviated away towards the react docs whenever i couldn't do something server related. for those moments, i mostly guessed so i was hoping someone could proof-read my implementation and see if i'm missing something lol. not using start because i don't need ssr btw. thanks :)
2 Replies
Convex Bot
Convex Bot17h ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
mike :)
mike :)OP16h ago
// main.tsx
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL, {
expectAuth: true,
unsavedChangesWarning: false,
});

const convexQueryClient = new ConvexQueryClient(convex);

const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});

convexQueryClient.connect(queryClient);

const router = createRouter({
routeTree,
context: {
queryClient,
convexClient: convex,
convexQueryClient,
},
defaultPreload: "intent",
// Since we're using React Query, we don't want loader calls to ever be stale
// This will ensure that the loader is always called when the route is preloaded or visited
defaultPreloadStaleTime: 0,
scrollRestoration: true,
});

declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}

const root = document.getElementById("root");
if (!root) throw new Error("Failed to find the root element");

createRoot(root).render(
<StrictMode>
<ConvexBetterAuthProvider client={convex} authClient={auth}>
<RouterProvider router={router} />
</ConvexBetterAuthProvider>
</StrictMode>,
);
// main.tsx
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL, {
expectAuth: true,
unsavedChangesWarning: false,
});

const convexQueryClient = new ConvexQueryClient(convex);

const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});

convexQueryClient.connect(queryClient);

const router = createRouter({
routeTree,
context: {
queryClient,
convexClient: convex,
convexQueryClient,
},
defaultPreload: "intent",
// Since we're using React Query, we don't want loader calls to ever be stale
// This will ensure that the loader is always called when the route is preloaded or visited
defaultPreloadStaleTime: 0,
scrollRestoration: true,
});

declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}

const root = document.getElementById("root");
if (!root) throw new Error("Failed to find the root element");

createRoot(root).render(
<StrictMode>
<ConvexBetterAuthProvider client={convex} authClient={auth}>
<RouterProvider router={router} />
</ConvexBetterAuthProvider>
</StrictMode>,
);
// routes/__root.tsx
export const Route = createRootRouteWithContext<{
queryClient: QueryClient;
convexClient: ConvexReactClient;
convexQueryClient: ConvexQueryClient;
}>()({
component: RootComponent,
});

function RootComponent() {
return <Outlet />;
}
// routes/__root.tsx
export const Route = createRootRouteWithContext<{
queryClient: QueryClient;
convexClient: ConvexReactClient;
convexQueryClient: ConvexQueryClient;
}>()({
component: RootComponent,
});

function RootComponent() {
return <Outlet />;
}
// routes/index.tsx
export const Route = createFileRoute("/")({
component: RouteComponent,
});

function RouteComponent() {
return (
<>
<Unauthenticated>
<p>Sign in form</p>
</Unauthenticated>

{/* This lives in a pathless layout route _authenticated */}
<Authenticated>
<Navigate to="/authenticated-route" />
</Authenticated>
</>
);
}
// routes/index.tsx
export const Route = createFileRoute("/")({
component: RouteComponent,
});

function RouteComponent() {
return (
<>
<Unauthenticated>
<p>Sign in form</p>
</Unauthenticated>

{/* This lives in a pathless layout route _authenticated */}
<Authenticated>
<Navigate to="/authenticated-route" />
</Authenticated>
</>
);
}
// routes/_authenticated/route.tsx
// this wraps all routes that live in this pathless layout route
export const Route = createFileRoute("/_authenticated")({
component: RouteComponent,
});

function RouteComponent() {
return (
<>
<Unauthenticated>
<Navigate to="/" />
</Unauthenticated>

<Authenticated>
<Outlet />
</Authenticated>
</>
);
}
// routes/_authenticated/route.tsx
// this wraps all routes that live in this pathless layout route
export const Route = createFileRoute("/_authenticated")({
component: RouteComponent,
});

function RouteComponent() {
return (
<>
<Unauthenticated>
<Navigate to="/" />
</Unauthenticated>

<Authenticated>
<Outlet />
</Authenticated>
</>
);
}

Did you find this page helpful?