Clerk React Auth logo

clerk react auth

Add authentication to React apps with Clerk components and hooks

$ npx docs2skills add clerk-react-auth
SKILL.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 isLoaded before 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 useEffect or use suppressHydrationWarning

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 afterSignInUrl and afterSignUpUrl to control redirects
  • Use redirectUrl prop 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 appearance prop for styling, not DOM manipulation
  • For full control, build custom dropdown with useUser() and signOut()

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
Clerk React Auth — docs2skills