clerk react auth
Add authentication to React apps with Clerk components and hooks
$ npx docs2skills add clerk-react-authSKILL.md
Clerk React SDK
Add authentication to React apps with Clerk components and hooks.
Quick Start
npm install @clerk/clerk-react
Create .env:
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
Wrap app with ClerkProvider:
// main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ClerkProvider } from '@clerk/clerk-react'
import App from './App.tsx'
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Add your Clerk Publishable Key to the .env file')
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY}>
<App />
</ClerkProvider>
</StrictMode>,
)
Add auth components:
// App.tsx
import { SignedIn, SignedOut, SignInButton, SignUpButton, UserButton } from '@clerk/clerk-react'
function App() {
return (
<header>
<SignedOut>
<SignInButton />
<SignUpButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
)
}
Core Concepts
- ClerkProvider: Context provider that wraps your app, provides auth state
- Control Components: SignedIn/SignedOut conditionally render content based on auth state
- UI Components: UserButton, SignInButton provide styled auth interfaces
- Hooks: useUser(), useAuth() access user data and auth methods programmatically
- Publishable Key: Public key that connects your app to Clerk backend
Key Components & Hooks
Control Components
import { SignedIn, SignedOut } from '@clerk/clerk-react'
<SignedIn>
{/* Only visible when authenticated */}
</SignedIn>
<SignedOut>
{/* Only visible when not authenticated */}
</SignedOut>
UI Components
import {
UserButton, // User avatar with dropdown menu
SignInButton, // Link to sign-in page
SignUpButton, // Link to sign-up page
SignOutButton // Sign-out trigger
} from '@clerk/clerk-react'
<UserButton />
<SignInButton mode="modal" />
<SignUpButton redirectUrl="/dashboard" />
<SignOutButton signOutCallback={() => window.location.href = '/'} />
Hooks
import { useUser, useAuth, useClerk } from '@clerk/clerk-react'
// Access user data
const { user, isLoaded, isSignedIn } = useUser()
// Access auth methods
const { signOut, getToken } = useAuth()
// Access Clerk instance
const clerk = useClerk()
await clerk.openSignIn()
Session Management
import { useSession, useSessionList } from '@clerk/clerk-react'
const { session, isLoaded } = useSession()
const { sessions, setActiveSession } = useSessionList()
// Switch between sessions
await setActiveSession(sessions[0])
Configuration
ClerkProvider Options
<ClerkProvider
publishableKey={PUBLISHABLE_KEY}
afterSignInUrl="/dashboard"
afterSignUpUrl="/onboarding"
signInUrl="/sign-in"
signUpUrl="/sign-up"
appearance={{
theme: 'dark',
variables: { colorPrimary: '#000' }
}}
>
Environment Variables
# Required
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...
# Optional - customize redirect URLs
VITE_CLERK_SIGN_IN_URL=/custom-sign-in
VITE_CLERK_SIGN_UP_URL=/custom-sign-up
VITE_CLERK_AFTER_SIGN_IN_URL=/dashboard
VITE_CLERK_AFTER_SIGN_UP_URL=/onboarding
Common Patterns
Protected Routes
import { SignedIn, SignedOut, RedirectToSignIn } from '@clerk/clerk-react'
function ProtectedPage() {
return (
<>
<SignedIn>
<div>Protected content</div>
</SignedIn>
<SignedOut>
<RedirectToSignIn />
</SignedOut>
</>
)
}
Loading States
import { useUser } from '@clerk/clerk-react'
function Profile() {
const { user, isLoaded } = useUser()
if (!isLoaded) return <div>Loading...</div>
if (!user) return <div>Not signed in</div>
return <div>Hello {user.firstName}!</div>
}
Custom Sign In Modal
import { useClerk } from '@clerk/clerk-react'
function CustomSignInButton() {
const clerk = useClerk()
return (
<button onClick={() => clerk.openSignIn()}>
Custom Sign In
</button>
)
}
API Token for Backend Calls
import { useAuth } from '@clerk/clerk-react'
function DataComponent() {
const { getToken } = useAuth()
const fetchData = async () => {
const token = await getToken()
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` }
})
return response.json()
}
}
User Metadata Access
import { useUser } from '@clerk/clerk-react'
function UserProfile() {
const { user } = useUser()
return (
<div>
<p>Email: {user?.primaryEmailAddress?.emailAddress}</p>
<p>Role: {user?.publicMetadata?.role}</p>
<p>Plan: {user?.privateMetadata?.plan}</p>
</div>
)
}
Gotchas and Best Practices
Environment Variables
- Use
VITE_prefix for Vite projects,REACT_APP_for Create React App - Never use secret key in frontend - only publishable key
- Check key exists before rendering to prevent runtime errors
Loading States
- Always check
isLoadedbefore accessing user data to avoid hydration issues - Components render immediately but user data loads async
- Use loading states to prevent flickering between signed in/out states
SSR/Hydration
- Clerk components cause hydration mismatches in SSR
- User state is undefined on server, defined on client
- Wrap Clerk components in
useEffector usesuppressHydrationWarning
Token Handling
getToken()returns null if user not signed in- Token auto-refreshes, don't cache manually
- Use
getToken({ template: 'templateName' })for custom JWT templates
Navigation
- Set
afterSignInUrlandafterSignUpUrlto control redirects - Use
redirectUrlprop on SignInButton/SignUpButton for per-component control - Default redirects go to Account Portal if no custom URLs set
Component Nesting
- Don't nest SignedIn/SignedOut components - causes render issues
- Use early returns instead of deeply nested auth checks
- Control components don't re-render on auth state change if improperly placed
Modal vs Page Mode
- SignInButton/SignUpButton default to Account Portal pages
- Use
mode="modal"for overlay experience - Modal mode requires additional setup for custom styling
User Button Customization
- UserButton dropdown items can't be fully customized
- Use
appearanceprop for styling, not DOM manipulation - For full control, build custom dropdown with
useUser()andsignOut()
Development vs Production
- Account Portal URLs differ between dev and prod environments
- Test auth flows in production-like environment before deploying
- Publishable keys are environment-specific
Error Handling
- Wrap auth operations in try-catch blocks
- Network failures don't automatically retry
- Check Clerk status page if auth suddenly fails across users