Clerk

Clerk is a complete authentication and user management solution that eliminates the complexity of building auth from scratch. With pre-built UI components, social logins, multi-factor authentication, and session management, Clerk lets you add secure authentication to your Astro and React applications in minutes while maintaining best security practices and providing excellent developer experience.

Last updated: 2026-03-29

Installing Clerk

Clerk provides official packages for Astro and React. Start by creating a free Clerk account, which gives you a publishable key and secret key. Install the appropriate package based on your framework, then configure environment variables for authentication to work across your application.

Terminal
// Install Clerk for Astro
npm install @clerk/astro

// Or for standalone React
npm install @clerk/clerk-react

// Create .env file with Clerk keys
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_YOUR_KEY
CLERK_SECRET_KEY=sk_test_YOUR_KEY

// Configure Clerk in astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import clerk from '@clerk/astro';

export default defineConfig({
  integrations: [
    react(),
    clerk({
      afterSignInUrl: '/dashboard',
      afterSignUpUrl: '/onboarding',
      signInUrl: '/sign-in',
      signUpUrl: '/sign-up'
    })
  ],
  output: 'server' // Required for Clerk
});

// Add Clerk middleware
// src/middleware.ts
import { clerkMiddleware } from '@clerk/astro/server';

export const onRequest = clerkMiddleware();

Creating Sign-In Pages

Clerk provides pre-built UI components for sign-in, sign-up, and user profile pages. These components handle all the complexity of authentication flows, including email/password, social logins, magic links, and multi-factor authentication. Customize the appearance to match your brand.

Terminal
// Sign-in page
// src/pages/sign-in.astro
---
import { SignIn } from '@clerk/astro/components';
import Layout from '../layouts/Layout.astro';
---

<Layout title="Sign In">
  <div class="min-h-screen flex items-center justify-center bg-gray-50 px-4">
    <SignIn 
      routing="path" 
      path="/sign-in"
      appearance={{
        elements: {
          rootBox: 'mx-auto',
          card: 'shadow-lg'
        }
      }}
    />
  </div>
</Layout>

// Sign-up page
// src/pages/sign-up.astro
---
import { SignUp } from '@clerk/astro/components';
import Layout from '../layouts/Layout.astro';
---

<Layout title="Sign Up">
  <div class="min-h-screen flex items-center justify-center bg-gray-50 px-4">
    <SignUp 
      routing="path" 
      path="/sign-up"
      appearance={{
        elements: {
          rootBox: 'mx-auto',
          card: 'shadow-lg',
          formButtonPrimary: 'bg-blue-600 hover:bg-blue-700'
        }
      }}
    />
  </div>
</Layout>

// User profile page
// src/pages/profile.astro
---
import { UserProfile } from '@clerk/astro/components';
import { getAuth } from '@clerk/astro/server';
import Layout from '../layouts/Layout.astro';

const auth = getAuth(Astro);

if (!auth.userId) {
  return Astro.redirect('/sign-in');
}
---

<Layout title="Profile">
  <div class="container mx-auto px-4 py-12">
    <h1 class="text-3xl font-bold mb-8">Your Profile</h1>
    <UserProfile routing="path" path="/profile" />
  </div>
</Layout>

Protecting Routes

Protect pages and API routes by checking authentication status. Clerk provides helpers to get the current user's authentication state. Redirect unauthenticated users to the sign-in page and conditionally render content based on user roles or permissions.

Terminal
// Protected dashboard page
// src/pages/dashboard.astro
---
import { getAuth } from '@clerk/astro/server';
import Layout from '../layouts/Layout.astro';

const auth = getAuth(Astro);

// Redirect if not authenticated
if (!auth.userId) {
  return Astro.redirect('/sign-in');
}

// Fetch user details
const user = await Astro.locals.clerk.users.getUser(auth.userId);
---

<Layout title="Dashboard">
  <div class="container mx-auto px-4 py-12">
    <h1 class="text-3xl font-bold mb-4">Welcome, {user.firstName}!</h1>
    <div class="flex items-center gap-4 mb-8">
      <img
        src={user.imageUrl}
        alt="Profile"
        class="w-24 h-24 rounded-full border-4 border-blue-500"
      />
      <div>
        <p class="text-gray-600">Email: {user.primaryEmailAddress?.emailAddress}</p>
        <p class="text-gray-600">Member since: {new Date(user.createdAt).toLocaleDateString()}</p>
      </div>
    </div>
    <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
      <div class="p-6 bg-white rounded-lg shadow">
        <h2 class="text-xl font-semibold mb-2">Account Status</h2>
        <p class="text-green-600">Active</p>
      </div>
      <div class="p-6 bg-white rounded-lg shadow">
        <h2 class="text-xl font-semibold mb-2">Subscription</h2>
        <p class="text-gray-600">Pro Plan</p>
      </div>
      <div class="p-6 bg-white rounded-lg shadow">
        <h2 class="text-xl font-semibold mb-2">Security</h2>
        <p class="text-gray-600">MFA {user.twoFactorEnabled ? 'Enabled' : 'Disabled'}</p>
      </div>
    </div>
  </div>
</Layout>

// Protected API route
// src/pages/api/user-data.ts
import type { APIRoute } from 'astro';
import { getAuth } from '@clerk/astro/server';

export const GET: APIRoute = async ({ locals, redirect }) => {
  const auth = getAuth({ locals });

  if (!auth.userId) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Fetch user-specific data
  const userData = {
    userId: auth.userId,
    // ... fetch from database
  };

  return new Response(JSON.stringify(userData), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  });
};

Using Clerk in React Components

Clerk's React hooks provide authentication state and user information in client components. Use these hooks to conditionally render UI, display user info, and handle sign-out. The UserButton component provides a dropdown menu with account management options.

Terminal
// Header component with user button
// src/components/Header.jsx
import { SignedIn, SignedOut, UserButton, useUser } from '@clerk/clerk-react';

export function Header() {
  const { user, isLoaded } = useUser();

  return (
    <header className="bg-white shadow-sm border-b">
      <div className="container mx-auto px-4 py-4 flex items-center justify-between">
        <a href="/" className="text-2xl font-bold text-blue-600">
          MyApp
        </a>

        <nav className="flex items-center gap-6">
          <a href="/features" className="text-gray-600 hover:text-gray-900">
            Features
          </a>
          <a href="/pricing" className="text-gray-600 hover:text-gray-900">
            Pricing
          </a>

          {/* Show when signed out */}
          <SignedOut>
            <a
              href="/sign-in"
              className="px-4 py-2 text-blue-600 border border-blue-600 rounded-lg hover:bg-blue-50"
            >
              Sign In
            </a>
            <a
              href="/sign-up"
              className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
            >
              Sign Up
            </a>
          </SignedOut>

          {/* Show when signed in */}
          <SignedIn>
            <a href="/dashboard" className="text-gray-600 hover:text-gray-900">
              Dashboard
            </a>
            <div className="flex items-center gap-3">
              {isLoaded && user && (
                <span className="text-sm text-gray-600">
                  Hi, {user.firstName}!
                </span>
              )}
              <UserButton 
                afterSignOutUrl="/"
                appearance={{
                  elements: {
                    avatarBox: 'w-10 h-10'
                  }
                }}
              />
            </div>
          </SignedIn>
        </nav>
      </div>
    </header>
  );
}

// Protected component
// src/components/ProtectedContent.jsx
import { useAuth } from '@clerk/clerk-react';

export function ProtectedContent() {
  const { isLoaded, userId, sessionId } = useAuth();

  if (!isLoaded) {
    return <div>Loading...</div>;
  }

  if (!userId) {
    return (
      <div className="text-center py-12">
        <p className="text-gray-600 mb-4">Please sign in to view this content</p>
        <a href="/sign-in" className="text-blue-600 hover:underline">
          Sign In →
        </a>
      </div>
    );
  }

  return (
    <div className="p-6 bg-white rounded-lg shadow">
      <h2 className="text-2xl font-bold mb-4">Protected Content</h2>
      <p>Session ID: {sessionId}</p>
      <p>This content is only visible to authenticated users.</p>
    </div>
  );
}