About clerk-tanstack-patterns
A reference skill for developers integrating Clerk with TanStack React Start. It covers authentication across server functions and routes, along with middleware setup.
Key features
- 01Guard routes with authentication
- 02Read auth state in server functions
- 03Pass auth context to loaders
- 04Configure Vite and Clerk middleware
Use cases
- 01Protect routes that require sign-in
- 02Check user identity in server functions
- 03Pass userId to route loaders
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-tanstack-patternsAfter installing, type/clerk-tanstack-patternsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
evals/evals.jsonreferences/loaders.mdrouter-guards.mdserver-functions.mdserver-setup.mdtemplates/tanstack-basic-auth/package.jsontsconfig.jsonvite.config.tstemplates/tanstack-basic-auth/src/router.tsxstart.tstemplates/tanstack-basic-auth/src/routes/__root.tsxindex.tsxDetails
From the project READMETanStack React Start Patterns
What Do You Need?
| Task | Reference |
|---|---|
| Protect routes with beforeLoad | references/router-guards.md |
| Auth in createServerFn | references/server-functions.md |
| Pass auth to loaders | references/loaders.md |
| Configure Vite + Clerk middleware | references/server-setup.md |
References
| Reference | Description |
|---|---|
references/router-guards.md | beforeLoad auth redirect |
references/server-functions.md | createServerFn with auth() |
references/loaders.md | Auth context in loaders |
references/server-setup.md | Vite and request middleware setup |
Setup
npm install @clerk/tanstack-react-start
.env:
VITE_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
src/start.ts (request middleware):
import { clerkMiddleware } from '@clerk/tanstack-react-start/server'
import { createCsrfMiddleware, createStart } from '@tanstack/react-start'
const csrfMiddleware = createCsrfMiddleware({
filter: (ctx) => ctx.handlerType === 'serverFn',
})
export const startInstance = createStart(() => {
return {
requestMiddleware: [csrfMiddleware, clerkMiddleware()],
}
})
src/routes/__root.tsx — wrap with <ClerkProvider>:
import { ClerkProvider } from '@clerk/tanstack-react-start'
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ClerkProvider>
{children}
</ClerkProvider>
</body>
</html>
)
}
Mental Model
TanStack Start uses the tanstackStart() Vite plugin. Register CSRF protection before Clerk request middleware in src/start.ts; auth then flows through two layers:
- Server layer —
createServerFn+auth()from@clerk/tanstack-react-start/server - Router layer —
beforeLoadon route definitions, throwsredirectfor unauthenticated
Both layers are server-executed. Client hooks (useAuth, useUser) are React hooks for the browser side.
Minimal Pattern
import { createFileRoute, redirect } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { auth } from '@clerk/tanstack-react-start/server'
const authStateFn = createServerFn().handler(async () => {
const { isAuthenticated, userId } = await auth()
if (!isAuthenticated) {
throw redirect({ to: '/sign-in' })
}
return { userId }
})
export const Route = createFileRoute('/dashboard')({
beforeLoad: async () => await authStateFn(),
})
Common Pitfalls
| Symptom | Cause | Fix |
|---|---|---|
auth() throws "without configuring the middleware" | Missing clerkMiddleware in start.ts | Add it after CSRF middleware in requestMiddleware |
redirect not thrown | Using return instead of throw | throw redirect(...) in TanStack |
Wrong import for auth | Mixing client/server imports | Server: @clerk/tanstack-react-start/server |
| Loader context missing userId | Not passing from beforeLoad | Return from beforeLoad, access via context |
ClerkProvider missing | Forgot root wrapping | Add to __root.tsx shell component |
See Also
clerk-setup- Initial Clerk installclerk-custom-ui- Custom flows & appearanceclerk-orgs- B2B organizations
Docs
Comments
Markdown supported