# Clerk on a React SPA (Vite or CRA)
## The env var name is the whole bug
Clerk's React SDK reads the publishable key from the environment at build time, and the variable name depends on the bundler:
- Vite: `VITE_CLERK_PUBLISHABLE_KEY`
- Create React App: `REACT_APP_CLERK_PUBLISHABLE_KEY`
Put the wrong prefix and `import.meta.env.VITE_CLERK_PUBLISHABLE_KEY` is `undefined`. Clerk then errors about a missing publishable key. If you see that error, check the prefix before anything else.
Never put `CLERK_SECRET_KEY` in a SPA. There is no server to hide it in; anything in the client bundle ships to the browser. SPAs only ever get the publishable key.
## Provider placement
Wrap the app at the entry point so every hook has context:
```tsx
import { ClerkProvider } from '@clerk/react'
createRoot(document.getElementById('root')!).render(
[StrictMode]
[ClerkProvider]
[App /]
[/ClerkProvider]
[/StrictMode],
)
```
`ClerkProvider` must actually wrap the components that call `useAuth()`, `useUser()`, etc. A provider mounted beside the router instead of around it gives "hook used outside provider" errors.
## Checklist
- After adding the env var, restart the dev server. Vite bakes env vars at startup; editing `.env` mid-session changes nothing until restart.
- Use `@clerk/react`, the current package name. Old tutorials import `@clerk/clerk-react`, which is the legacy Core 2 name.
- Sign-in/sign-up buttons with no configured URLs fall back to Clerk's Account Portal pages. That is fine for a prototype, but set explicit URLs before it confuses users.
- Confirm the key type matches the instance: a `pk_test` key against a production instance (or vice versa) fails auth in ways that look like code bugs.