From dd918d5a7808f8d394183a62f0b0049a68b480f9 Mon Sep 17 00:00:00 2001 From: Brennen Davis Date: Wed, 30 Jul 2025 00:03:12 -0600 Subject: [PATCH 1/4] beforeLoad auth --- .../src/components/sign-in-form.tsx | 5 ++-- .../src/components/sign-up-form.tsx | 5 ++-- .../tanstack-router/src/routes/_auth.tsx | 15 ++++++++++++ .../src/routes/{ => _auth}/dashboard.tsx.hbs | 16 +++---------- .../tanstack-router/src/routes/login.tsx | 13 +++++++++- .../react/tanstack-router/src/main.tsx.hbs | 8 ++++--- .../tanstack-router/src/routes/__root.tsx.hbs | 24 +++++++++++++++++-- bun.lock | 2 +- 8 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth.tsx rename apps/cli/templates/auth/web/react/tanstack-router/src/routes/{ => _auth}/dashboard.tsx.hbs (78%) diff --git a/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-in-form.tsx b/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-in-form.tsx index 65bfab25b..6d3e47dff 100644 --- a/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-in-form.tsx +++ b/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-in-form.tsx @@ -1,6 +1,6 @@ import { authClient } from "@/lib/auth-client"; import { useForm } from "@tanstack/react-form"; -import { useNavigate } from "@tanstack/react-router"; +import { useNavigate, useSearch } from "@tanstack/react-router"; import { toast } from "sonner"; import z from "zod"; import Loader from "./loader"; @@ -13,6 +13,7 @@ export default function SignInForm({ }: { onSwitchToSignUp: () => void; }) { + const search = useSearch({ from: "/login" }); const navigate = useNavigate({ from: "/", }); @@ -32,7 +33,7 @@ export default function SignInForm({ { onSuccess: () => { navigate({ - to: "/dashboard", + to: search.redirect, }); toast.success("Sign in successful"); }, diff --git a/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-up-form.tsx b/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-up-form.tsx index 3bd480f08..826fbeea5 100644 --- a/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-up-form.tsx +++ b/apps/cli/templates/auth/web/react/tanstack-router/src/components/sign-up-form.tsx @@ -1,6 +1,6 @@ import { authClient } from "@/lib/auth-client"; import { useForm } from "@tanstack/react-form"; -import { useNavigate } from "@tanstack/react-router"; +import { useNavigate, useSearch } from "@tanstack/react-router"; import { toast } from "sonner"; import z from "zod"; import Loader from "./loader"; @@ -13,6 +13,7 @@ export default function SignUpForm({ }: { onSwitchToSignIn: () => void; }) { + const search = useSearch({ from: "/login" }); const navigate = useNavigate({ from: "/", }); @@ -34,7 +35,7 @@ export default function SignUpForm({ { onSuccess: () => { navigate({ - to: "/dashboard", + to: search.redirect, }); toast.success("Sign up successful"); }, diff --git a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth.tsx b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth.tsx new file mode 100644 index 000000000..83cc6e202 --- /dev/null +++ b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth.tsx @@ -0,0 +1,15 @@ +import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_auth")({ + beforeLoad: async ({ context }) => { + if (!context.user) { + throw redirect({ + to: "/login", + search: { + redirect: `${location.pathname}${location.search}${location.hash}`, + }, + }); + } + }, + component: () => , +}); diff --git a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth/dashboard.tsx.hbs similarity index 78% rename from apps/cli/templates/auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs rename to apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth/dashboard.tsx.hbs index 3ae44b6f8..97e2dc274 100644 --- a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +++ b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/_auth/dashboard.tsx.hbs @@ -9,32 +9,22 @@ import { trpc } from "@/utils/trpc"; import { useQuery } from "@tanstack/react-query"; {{/if}} import { createFileRoute } from "@tanstack/react-router"; -import { useEffect } from "react"; -export const Route = createFileRoute("/dashboard")({ +export const Route = createFileRoute("/_auth/dashboard")({ component: RouteComponent, }); function RouteComponent() { const { data: session, isPending } = authClient.useSession(); - - const navigate = Route.useNavigate(); - {{#if (eq api "orpc")}} + const privateData = useQuery(orpc.privateData.queryOptions()); {{/if}} {{#if (eq api "trpc")}} + const privateData = useQuery(trpc.privateData.queryOptions()); {{/if}} - useEffect(() => { - if (!session && !isPending) { - navigate({ - to: "/login", - }); - } - }, [session, isPending]); - if (isPending) { return
Loading...
; } diff --git a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx index 81dc6a0a0..688cba29e 100644 --- a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx +++ b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx @@ -1,9 +1,20 @@ import SignInForm from "@/components/sign-in-form"; import SignUpForm from "@/components/sign-up-form"; -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, redirect } from "@tanstack/react-router"; import { useState } from "react"; +import { z } from "zod"; + +const fallbackRedirect = "/dashboard"; export const Route = createFileRoute("/login")({ + validateSearch: z.object({ + redirect: z.string().optional().catch(fallbackRedirect), + }), + beforeLoad: async ({ context, search }) => { + if (context.user) { + throw redirect({ to: search.redirect }); + } + }, component: RouteComponent, }); diff --git a/apps/cli/templates/frontend/react/tanstack-router/src/main.tsx.hbs b/apps/cli/templates/frontend/react/tanstack-router/src/main.tsx.hbs index 2596dae6c..f4cff79b9 100644 --- a/apps/cli/templates/frontend/react/tanstack-router/src/main.tsx.hbs +++ b/apps/cli/templates/frontend/react/tanstack-router/src/main.tsx.hbs @@ -21,7 +21,7 @@ const router = createRouter({ defaultPreload: "intent", defaultPendingComponent: () => , {{#if (eq api "orpc")}} - context: { orpc, queryClient }, + context: { orpc, queryClient{{#if auth}}, user: null{{/if}} }, Wrap: function WrapComponent({ children }: { children: React.ReactNode }) { return ( @@ -30,7 +30,7 @@ const router = createRouter({ ); }, {{else if (eq api "trpc")}} - context: { trpc, queryClient }, + context: { trpc, queryClient{{#if auth}}, user: null{{/if}} }, Wrap: function WrapComponent({ children }: { children: React.ReactNode }) { return ( @@ -44,7 +44,9 @@ const router = createRouter({ return {children}; }, {{else}} - context: {}, + context: { + {{#if auth}}user: null{{/if}} + }, {{/if}} }); diff --git a/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs b/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs index 03c3d2f76..d12e67da4 100644 --- a/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +++ b/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs @@ -17,6 +17,10 @@ import type { trpc } from "@/utils/trpc"; import type { QueryClient } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; {{/if}} +{{#if auth}} +import { authClient } from "@/lib/auth-client"; +import type { User } from "better-auth"; +{{/if}} import { HeadContent, Outlet, @@ -30,17 +34,33 @@ import "../index.css"; export interface RouterAppContext { orpc: typeof orpc; queryClient: QueryClient; + {{#if auth}} + user: User | null; + {{/if}} } {{else if (eq api "trpc")}} export interface RouterAppContext { trpc: typeof trpc; queryClient: QueryClient; + {{#if auth}} + user: User | null; + {{/if}} } {{else}} -export interface RouterAppContext {} +export interface RouterAppContext { + {{#if auth}} + user: User | null; + {{/if}} +} {{/if}} export const Route = createRootRouteWithContext()({ + {{#if auth}} + beforeLoad: async () => { + const session = await authClient.getSession(); + return { user: session.data?.user ?? null }; + }, + {{/if}} component: RootComponent, head: () => ({ meta: [ @@ -65,8 +85,8 @@ function RootComponent() { const isFetching = useRouterState({ select: (s) => s.isLoading, }); - {{#if (eq api "orpc")}} + const [client] = useState>(() => createORPCClient(link)); const [orpcUtils] = useState(() => createTanstackQueryUtils(client)); {{/if}} diff --git a/bun.lock b/bun.lock index da8c4f8f4..569323262 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ }, "apps/cli": { "name": "create-better-t-stack", - "version": "2.27.1", + "version": "2.28.0", "bin": { "create-better-t-stack": "dist/index.js", }, From bba8098ba8701bedd6e372857fb1b0dc39b6fe0b Mon Sep 17 00:00:00 2001 From: Brennen Davis Date: Wed, 30 Jul 2025 08:39:53 -0600 Subject: [PATCH 2/4] Update apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../react/tanstack-router/src/routes/__root.tsx.hbs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs b/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs index d12e67da4..752415d55 100644 --- a/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +++ b/apps/cli/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs @@ -57,8 +57,13 @@ export interface RouterAppContext { export const Route = createRootRouteWithContext()({ {{#if auth}} beforeLoad: async () => { - const session = await authClient.getSession(); - return { user: session.data?.user ?? null }; + try { + const session = await authClient.getSession(); + return { user: session.data?.user ?? null }; + } catch (error) { + console.error('Failed to fetch user session:', error); + return { user: null }; + } }, {{/if}} component: RootComponent, From 28f4eb6cd195d86ed54f6a97ff1629af48cf2a87 Mon Sep 17 00:00:00 2001 From: Brennen Davis Date: Wed, 30 Jul 2025 08:41:57 -0600 Subject: [PATCH 3/4] Use default instead of optional().catch() --- .../auth/web/react/tanstack-router/src/routes/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx index 688cba29e..cc602ee6c 100644 --- a/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx +++ b/apps/cli/templates/auth/web/react/tanstack-router/src/routes/login.tsx @@ -8,7 +8,7 @@ const fallbackRedirect = "/dashboard"; export const Route = createFileRoute("/login")({ validateSearch: z.object({ - redirect: z.string().optional().catch(fallbackRedirect), + redirect: z.string().default(fallbackRedirect), }), beforeLoad: async ({ context, search }) => { if (context.user) { From 8bcf302123b45cacd33b919e7fcb536175fa71a9 Mon Sep 17 00:00:00 2001 From: Brennen Davis Date: Thu, 31 Jul 2025 00:25:42 -0600 Subject: [PATCH 4/4] changeset --- .changeset/salty-windows-crash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/salty-windows-crash.md diff --git a/.changeset/salty-windows-crash.md b/.changeset/salty-windows-crash.md new file mode 100644 index 000000000..62980c1c2 --- /dev/null +++ b/.changeset/salty-windows-crash.md @@ -0,0 +1,5 @@ +--- +"create-better-t-stack": patch +--- + +TanStack Router checks for auth in the beforeLoad function of a Route