
Add Clerk authentication to a TanStack Start app with the Clerk CLI
How do I add Clerk authentication to a TanStack Start app with the Clerk CLI?
Run clerk init --framework tanstack-start against an existing TanStack Start app — the Clerk CLI (released 2026-04-22) wires <ClerkProvider> into __root.tsx, generates catch-all sign-in / sign-up routes, drops a server start.ts with clerkMiddleware() as request middleware, and adds @clerk/tanstack-react-start to your dependencies. Follow that with clerk env pull, clerk doctor, and clerk config patch to manage authentication configuration — passkeys, sign-in methods, session policy — as code. The walkthrough below covers the full flow end-to-end against a fresh @tanstack/cli scaffold, including a sign-in affordance on the landing page and the Core 3 <Show when="signed-in"> component that replaces <SignedIn>.
Validated against TanStack Start 1.167.42, @clerk/tanstack-react-start 1.1.5, Clerk CLI 1.0.2, React 19.2.5, and Vite 8.0.10 on 2026-04-23 — pin equivalent versions if anything drifts. TanStack Start has no middleware.ts/proxy.ts convention like Next.js and many existing tutorials predate the CLI or mix Core 2 component names that Core 3 removed; the CLI output tracks the current SDK surface area instead.
This is part 1 of a two-part series on adding Clerk authentication to a TanStack Start app using the Clerk CLI. In this part, we cover the prerequisites, installing the CLI, scaffolding a new TanStack Start app, initializing Clerk, and verifying your environment configuration using clerk doctor.
Why the Clerk CLI for TanStack Start
TanStack Start is SSR-first React with server functions and beforeLoad route guards — there's no middleware.ts / proxy.ts convention like Next.js, and the integration points for auth are less obvious the first time you look at a Start project. Most existing tutorials either pre-date the CLI, pre-date @clerk/tanstack-react-start's current shape (older posts reference the renamed @clerk/tanstack-start package), or mix Core 2 component names like <SignedIn> / <SignedOut> that Core 3 removed.
clerk init skips all of that. It detects the framework from package.json, installs the right SDK, writes the provider + middleware + auth-page scaffolding, and seeds .env.local in one shot. Because the CLI is version-current, the output tracks @clerk/tanstack-react-start's current surface area, not whatever shipped eight months ago. If you're coming from the Next.js version of this workflow, the spine is the same — the CLI runs the same way across frameworks — but the files it writes are Start-shaped.
TanStack Start is currently in Release Candidate: the official overview describes the API as feature-complete but not guaranteed bug-free. Pin a known-good version for production workloads.
Install or update the Clerk CLI
New install — pick whichever fits your machine. The shell one-liner is the fastest path, but Homebrew and a global npm install both work:
curl -fsSL https://clerk.com/install | shbrew install clerk/stable/clerknpm install -g clerkConfirm the version — you want 1.0.2 or later for this tutorial (the 2026-04-22 release):
clerk --versionThe CLI has no self-update subcommand. To upgrade, rerun whichever installer you used. The curl installer fetches the latest release by default; pass --canary to track the edge channel. For Homebrew, use the standard upgrade subcommand. For a global npm install, reinstall the @latest tag to pull the newest release:
curl -fsSL https://clerk.com/install | sh -s -- --canaryclerk updatenpm install -g clerk@latestOptional but recommended — enable shell completion so subcommand and flag names autocomplete:
clerk completion zsh > "${fpath[1]}/_clerk"clerk completion bash > /etc/bash_completion.d/clerkInstall the Clerk agent skills
If you're using Claude Code, Cursor, or Codex, you have two paths to get the Clerk-maintained skills into your project:
- Let
clerk initdo it (recommended). The next step —clerk init— ends with anInstall agent skills?prompt that defaults to yes. Accept it, or pass-yto skip the prompt and auto-accept. The CLI then runsnpx skills add clerk/skillsunder the hood. - Install manually (if you want skills in a project you're not scaffolding with
clerk init, or if you skipped the prompt):
npx skills add clerk/skillsFor TanStack Start, clerk init installs three skills: clerk (the CLI + core concepts), clerk-setup (the quickstart surface), and clerk-tanstack-patterns (TanStack-specific auth patterns — loaders, beforeLoad, server functions). The base two ship with every framework; the third is selected by matching @tanstack/react-start in package.json against the CLI's framework → skill map.
Coding agents trained on older Clerk content tend to hallucinate the removed <SignedIn> / <SignedOut> components, the old @clerk/tanstack-start package name, and long-retired patterns. The bundled skills are how you keep them honest.
If you already have hand-rolled clerk-* skills under .claude/skills/ from an earlier project, audit them after the install: the Clerk-maintained skills supersede most hand-authored ones and the overlap will confuse your agent. To suppress the prompt inside clerk init (so it doesn't try to reinstall), pass --no-skills.
Log in and pick an application
clerk auth login
clerk whoamiclerk auth login opens the dashboard in a browser, completes OAuth, and persists credentials and config to the OS-standard CLI directory (macOS: ~/Library/Preferences/clerk-cli/config.json, Linux: ~/.config/clerk-cli/config.json, Windows: %APPDATA%\clerk-cli\Config\config.json). Override with CLERK_CONFIG_DIR if you need a custom location, or run clerk doctor to print the resolved path. clerk whoami confirms which account you're operating as — useful when you have multiple logins or switch between personal and work accounts.
List your apps and pick one:
clerk apps list
clerk apps list --json # machine-readable output, pipe to jq for scriptsIf you don't have an app yet, create one from the CLI:
clerk apps create "my-tanstack-app"Or let clerk init prompt you interactively later. Either path works — the article's validation pre-linked a test app with clerk link --app <id> so clerk init could skip the app-picker prompt.
Scaffold a TanStack Start app with @tanstack/cli
Create a working directory and scaffold Start:
pnpm dlx @tanstack/cli@latest create my-clerk-tanstack-start-appThe TanStack scaffolder is interactive. Accept Tailwind CSS, decline ESLint (add it back later if you want — it's out of scope here), and take defaults for the rest. The version-pinned non-interactive equivalent:
pnpm dlx @tanstack/cli@latest create my-clerk-tanstack-start-app \
--framework React \
--package-manager pnpm \
--no-toolchain \
--no-examples \
--no-git \
--yesMove into the new app and confirm it boots:
cd my-clerk-tanstack-start-app
pnpm install
pnpm devYou should see a "Welcome to TanStack Start" page on http://localhost:3000. TanStack Start pins vite dev --port 3000 in the scaffolded package.json. Stop the dev server (Ctrl-C) before the next step — clerk init writes files you'd rather not have HMR-reloaded mid-flight.
The scaffolded structure you care about:
src/
├── router.tsx
├── routes/
│ ├── __root.tsx
│ └── index.tsx
├── routeTree.gen.ts
├── start.ts
└── styles.cssNote the modern src/-rooted layout. Older TanStack Start content still references an app/ layout — that's been replaced. clerk init targets src/ correctly; if you're migrating an older app, move the files first. The scaffold's src/start.ts is where clerk init inserts clerkMiddleware() (see appendix).
Add Clerk with clerk init
From inside the my-clerk-tanstack-start-app/ directory:
clerk init --framework tanstack-startThe CLI auto-detects the framework from package.json, so --framework tanstack-start is technically redundant — but explicit is worth the typing for reproducibility and CI scripts. The package manager is auto-detected from the lockfile the same way. Pass -y for non-interactive mode in CI (accepts the scaffold plan, skips the skills prompt by auto-accepting), or leave it off locally to preview the plan before it writes:
clerk init --framework tanstack-start -yIf you want clerk init to pull keys for a specific app without interactive picking, link the app first:
clerk link --app app_xxx
clerk init --framework tanstack-start -yclerk link --app <id> writes the link to the CLI config directory. When clerk init runs afterward, link({ skipIfLinked: true }) finds the existing link and skips the prompt, and env pull picks up the linked app's keys. Without a pre-existing link, clerk init runs clerk link interactively so you can pick or create an app during the flow. (clerk init itself has no --app flag — only link, env pull, config, and api do.)
What changes in your project when clerk init runs:
package.json— adds"@clerk/tanstack-react-start": "^1.1.5"todependencies. Note the-react-infix; the older@clerk/tanstack-startpackage name was renamed.src/routes/__root.tsx— wraps{children}in<ClerkProvider>from@clerk/tanstack-react-start.src/routes/sign-in.$.tsx(new) — catch-all route rendering<SignIn />.src/routes/sign-up.$.tsx(new) — catch-all route rendering<SignUp />.src/start.ts— addsclerkMiddleware()to therequestMiddlewarearray returned bycreateStart(). TanStack's scaffold createssrc/start.tswith an empty middleware config;clerk initmodifies the existing file rather than writing a new one..env.local— seeded with Clerk env vars. The route URL vars (VITE_CLERK_SIGN_IN_URL,VITE_CLERK_SIGN_UP_URL, and the two_FALLBACK_REDIRECT_URLvars) are written by the framework scaffold step. The keys (VITE_CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEY) are written byclerk init's built-inenv pull, which runs after authentication andlinksucceed — so real keys land in the file in one pass as long as you're logged in and have either pre-linked an app or picked one at the prompt.
The full file-by-file diff is in the appendix below.
Install the new dependency:
pnpm installclerk init does not run the install for you. That's intentional — it means you control when your lockfile updates — but easy to forget.
Link to an existing Clerk app (optional)
If you want to reassign the CLI to a different app after clerk init:
clerk unlink
clerk link --app app_xxxclerk whoami reflects the currently-linked app and instance, so run it to sanity-check which environment you're pointing at before you make config changes. The CLI stores the active app and secret key reference in the OS-standard CLI config directory (see the previous section for the per-platform path), not in your repo.
Pull (or refresh) environment variables
clerk env pullclerk init already runs env pull internally once authentication + link succeed, so right after a successful clerk init your .env.local is already populated. clerk env pull is the standalone command for everything afterward — refreshing keys after a rotation, switching between dev and prod (--instance prod), targeting a different app (--app <id>), or repopulating the file after you accidentally deleted it.
TanStack Start uses Vite, which reads VITE_-prefixed env vars on the client — so your publishable key lands as VITE_CLERK_PUBLISHABLE_KEY, not Next's NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY. The CLI handles the prefix difference automatically based on the framework it detects.
After clerk init (or after a manual clerk env pull), .env.local looks like this (real values redacted):
VITE_CLERK_SIGN_IN_URL=/sign-in
VITE_CLERK_SIGN_UP_URL=/sign-up
VITE_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
VITE_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/
# Clerk
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...Two categories of vars here. The four VITE_CLERK_*_URL entries are seeded by clerk init's framework scaffold step (they wire the routes Clerk's components navigate to). The two keys are written by clerk init's built-in env pull against the linked app and can be refreshed any time with a standalone clerk env pull.
Your CLERK_SECRET_KEY is a secret key — never commit it, never ship it to the client. The default .gitignore that @tanstack/cli writes includes .env*.local, which covers this, but double-check.
Run clerk doctor the first time (it should fail)
Before pulling env vars, clerk doctor is a teaching moment:
mv .env.local .env.local.bak
clerk doctorExpected output flags the missing keys. Doctor validates the CLI version, auth session, linked app + instance IDs, and .env.local contents — the things that actually break integrations. It's framework-agnostic plumbing validation, not a framework-aware linter. It doesn't lint your start.ts or route files for TanStack-specific wiring.
Restore the file:
mv .env.local.bak .env.localRun clerk doctor after env pull (green)
Now re-run doctor:
clerk doctorEvery check should pass. If anything red remains:
clerk doctor --spotlightfilters output to warnings and failures only — useful when most checks pass and you want to focus on what's broken without scrolling.clerk doctor --fixruns in interactive mode and prompts per issue before applying a fix, then re-runs checks to verify. Skip it in CI; it needs a TTY.clerk doctor --verboseshows the full per-check output. Helpful when a check fails for a non-obvious reason.
What's next
In this part, we set up a TanStack Start application, initialized Clerk with the Clerk CLI, and validated the configuration. In the next part, we will wire up the landing page UI, start the dev server, and explore configuring instance settings as code using clerk config.
FAQ
In this series
- Add Clerk authentication to a TanStack Start app with the Clerk CLI (you are here)
- Add Clerk authentication to a TanStack Start app with the Clerk CLI - Part 2