From bf7db872fb06a8c6979f0a4679f8695449ddfd81 Mon Sep 17 00:00:00 2001 From: Michael Novotny Date: Tue, 16 Jun 2026 14:16:46 -0500 Subject: [PATCH] feat: add a "Protect your pages" step (server-side auth read) Add a /protected route that reads the signed-in user on the server and redirects signed-out visitors, plus a "Protected" nav link shown only when signed in: client components control visibility, the server controls access. Part of DOCS-11853. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/root.tsx | 3 ++- app/routes.ts | 7 +++++-- app/routes/protected.tsx | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 app/routes/protected.tsx diff --git a/app/root.tsx b/app/root.tsx index 6028db8..7440d20 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,5 +1,5 @@ import { ClerkProvider, SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/react-router' -import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' +import { isRouteErrorResponse, Link, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' import { clerkMiddleware, rootAuthLoader } from '@clerk/react-router/server' import type { Route } from './+types/root' @@ -53,6 +53,7 @@ export default function App({ loaderData }: Route.ComponentProps) { {/* Show the user button when the user is signed in */} + Protected diff --git a/app/routes.ts b/app/routes.ts index 102b402..b38f10d 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -1,3 +1,6 @@ -import { type RouteConfig, index } from "@react-router/dev/routes"; +import { type RouteConfig, index, route } from "@react-router/dev/routes"; -export default [index("routes/home.tsx")] satisfies RouteConfig; +export default [ + index("routes/home.tsx"), + route("protected", "routes/protected.tsx"), +] satisfies RouteConfig; diff --git a/app/routes/protected.tsx b/app/routes/protected.tsx new file mode 100644 index 0000000..32ac26b --- /dev/null +++ b/app/routes/protected.tsx @@ -0,0 +1,26 @@ +import { redirect } from 'react-router' +import { getAuth } from '@clerk/react-router/server' +import type { Route } from './+types/protected' + +export async function loader(args: Route.LoaderArgs) { + // Use `getAuth()` to read the session on the server and protect this route. + // Unlike , which only controls what renders, this is the real + // access check: a signed-out user who navigates here directly is redirected. + const { isAuthenticated, userId } = await getAuth(args) + + if (!isAuthenticated) { + return redirect('/') + } + + return { userId } +} + +export default function Protected({ loaderData }: Route.ComponentProps) { + return ( +
+

+ Welcome! Your user ID is {loaderData.userId}. +

+
+ ) +}