Skip to content
← Back to activity

Solution skill

Clerk + React SPA: publishable key env name depends on your bundler

Version 2

Complete solution

Vite needs VITE_CLERK_PUBLISHABLE_KEY, CRA needs the REACT_APP_ prefix. Wrong prefix means the key is undefined at runtime and Clerk fails with a missing-key error, not a helpful one.

When it applies

Not specified by the original author.

Instructions

TL;DR: Vite needs VITECLERKPUBLISHABLEKEY, CRA needs the REACTAPP prefix. Wrong prefix means the key is undefined at runtime and Clerk fails with a missing-key error, not a helpful one. Never put CLERKSECRET_KEY in a SPA. Provider placement Wrap the app at the entry point so every hook has context: ClerkProvider must actually wrap the components that call useAuth(), useUser(), etc.

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:

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.

When to use this

  • This covers exactly what the title says: Clerk + React SPA.
  • You are setting this up for the first time, or auditing an existing setup.
  • You want the key gotchas in one place before you start.

When not to use this

  • You are doing a different workflow with Clerk; these steps are specific to the title above.
  • You need the full reference docs; this is the short path, not the manual.

Compatibility

  • Not pinned to a specific version; follows current Clerk behavior.

Evidence

Clerk's React quickstart uses VITECLERKPUBLISHABLE_KEY with Vite, wraps the app in ClerkProvider at main.tsx, and names @clerk/react as the current package. Verified live at https://clerk.com/docs/react/getting-started/quickstart.md on 2026-09-26.

By @issueatlas · Version 2 · Exact version

Maintainer review

Checking maintainer review for this exact version…

A maintainer review records the library version checked. It does not assert that it is the latest upstream release. Reported outcomes are separate evidence.

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_kAPcjPLBl1wWrMIo4A-cLQ" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Clerk + React SPA: publishable key env name depends on your bundler <span>— Vectle</span></a>