{"id":14582,"plugin_id":"plugin_asdk_app_6a9a3c1cd0108191807da33c4cd319ed","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:09:49.433Z","digest":"7a4c1d6200bcadcfc9dfa63ce5c0e8e8431a1c6221be8a4fdb5628043631c887","against":null,"payload":{"name":"clerk-react-patterns","description":"React SPA auth patterns with @clerk/react for Vite/CRA - ClerkProvider setup, useAuth/useUser/useClerk hooks, React Router protected routes, custom sign-in flows. Triggers on: Vite Clerk setup, React Router auth, useAuth hook, protected route, custom sign-in form React.","included_files":[{"relative_path":"evals/evals.json","size_in_bytes":4586},{"relative_path":"references/custom-flows.md","size_in_bytes":2741},{"relative_path":"references/hooks.md","size_in_bytes":2366},{"relative_path":"references/protected-routes.md","size_in_bytes":2230},{"relative_path":"references/router-integration.md","size_in_bytes":2084},{"relative_path":"templates/react-basic-auth/index.html","size_in_bytes":300},{"relative_path":"templates/react-basic-auth/package.json","size_in_bytes":429},{"relative_path":"templates/react-basic-auth/src/App.tsx","size_in_bytes":310},{"relative_path":"templates/react-basic-auth/src/main.tsx","size_in_bytes":288},{"relative_path":"templates/react-basic-auth/tsconfig.json","size_in_bytes":234},{"relative_path":"templates/react-basic-auth/vite.config.ts","size_in_bytes":132}],"skill_md_contents":"---\nname: clerk-react-patterns\ndescription: 'React SPA auth patterns with @clerk/react for Vite/CRA - ClerkProvider\n  setup, useAuth/useUser/useClerk hooks, React Router protected routes, custom sign-in\n  flows. Triggers on: Vite Clerk setup, React Router auth, useAuth hook, protected\n  route, custom sign-in form React.'\nlicense: MIT\nallowed-tools: WebFetch\nmetadata:\n  author: clerk\n  version: 1.0.0\n---\n\n# React SPA Patterns\n\n> This skill covers `@clerk/react` for Vite/CRA SPAs. For Next.js use `clerk-nextjs-patterns`. For TanStack Start use `clerk-tanstack-patterns`.\n\n## What Do You Need?\n\n| Task | Reference |\n|------|-----------|\n| useAuth / useUser / useClerk hooks | references/hooks.md |\n| Protected routes with React Router | references/protected-routes.md |\n| Custom sign-in / sign-up forms | references/custom-flows.md |\n| React Router v6/v7 integration | references/router-integration.md |\n\n## References\n\n| Reference | Description |\n|-----------|-------------|\n| `references/hooks.md` | useAuth, isLoaded guard |\n| `references/protected-routes.md` | ProtectedRoute pattern |\n| `references/custom-flows.md` | useSignIn, useSignUp flows |\n| `references/router-integration.md` | React Router v6/v7 setup |\n\n## Setup\n\n```\nnpm install @clerk/react\n```\n\n`.env`:\n```\nVITE_CLERK_PUBLISHABLE_KEY=pk_...\n```\n\n`src/main.tsx`:\n```tsx\nimport { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { ClerkProvider } from '@clerk/react'\nimport App from './App.tsx'\n\nconst PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY\n\ncreateRoot(document.getElementById('root')!).render(\n  <StrictMode>\n    <ClerkProvider publishableKey={PUBLISHABLE_KEY}>\n      <App />\n    </ClerkProvider>\n  </StrictMode>,\n)\n```\n\n## Mental Model\n\n`@clerk/react` is client-only — there is no server-side `auth()`. All auth state comes from hooks.\n\n- `isLoaded` must be `true` before trusting `isSignedIn` — always guard on `isLoaded`\n- `useClerk()` gives access to `signOut`, `openSignIn`, `openUserProfile` and other methods\n- `getToken()` from `useAuth()` fetches the session JWT for API calls\n\n## Minimal Pattern\n\n```tsx\nimport { useAuth } from '@clerk/react'\n\nexport function Dashboard() {\n  const { isLoaded, isSignedIn, userId } = useAuth()\n\n  if (!isLoaded) return <div>Loading...</div>\n  if (!isSignedIn) return <div>Please sign in</div>\n\n  return <div>Hello {userId}</div>\n}\n```\n\n## Protected Route (React Router v6/v7)\n\n```tsx\nimport { Navigate, Outlet } from 'react-router-dom'\nimport { useAuth } from '@clerk/react'\n\nexport function ProtectedRoute() {\n  const { isLoaded, isSignedIn } = useAuth()\n\n  if (!isLoaded) return <div>Loading...</div>\n  if (!isSignedIn) return <Navigate to=\"/sign-in\" replace />\n\n  return <Outlet />\n}\n```\n\n```tsx\n<Routes>\n  <Route element={<ProtectedRoute />}>\n    <Route path=\"/dashboard\" element={<Dashboard />} />\n    <Route path=\"/settings\" element={<Settings />} />\n  </Route>\n  <Route path=\"/sign-in\" element={<SignIn />} />\n</Routes>\n```\n\n## Token for API Calls\n\n```tsx\nimport { useAuth } from '@clerk/react'\n\nexport function DataFetcher() {\n  const { getToken } = useAuth()\n\n  async function fetchData() {\n    const token = await getToken()\n    if (!token) return\n\n    const res = await fetch('/api/data', {\n      headers: { Authorization: `Bearer ${token}` },\n    })\n    return res.json()\n  }\n\n  return <button onClick={fetchData}>Load</button>\n}\n```\n\n## Common Pitfalls\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| `isSignedIn` is `undefined` | `isLoaded` is still `false` | Always check `isLoaded` first |\n| `ClerkProvider` missing | Provider not at root | Wrap `<App>` in `main.tsx` |\n| Env var undefined | Wrong Vite prefix | Use `VITE_CLERK_PUBLISHABLE_KEY`, access via `import.meta.env` |\n| Token is `null` | User not signed in | Null-check `getToken()` result |\n| Sign-in component shows blank | No `publishableKey` on provider | Pass `publishableKey` explicitly |\n\n## See Also\n\n- `clerk-setup` - Initial Clerk install\n- `clerk-custom-ui` - Custom flows & appearance\n- `clerk-orgs` - B2B organizations\n\n## Docs\n\n[React SDK](https://clerk.com/docs/react/getting-started/quickstart)\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}