DEV Community

RAXXO Studios
RAXXO Studios

Posted on • Originally published at raxxo.shop

How to Set Up Clerk Authentication for Any Next.js Project

  • Install @clerk/nextjs, create account, add publishable and secret keys to environment variables.

  • Wrap app in ClerkProvider and use middleware.ts to protect routes based on authentication status.

  • Create custom sign-in/sign-up pages with Clerk's components and customize appearance to match brand design.

  • Sync Clerk users to your database automatically on first API call using clerkId as foreign key.

Authentication is one of those things that sounds simple ("just add login") but has a hundred edge cases that can ruin your launch. Clerk handles the hard parts, but setting it up properly for production still requires some decisions. Here's the complete setup from zero to deployed, based on running it in production for RAXXO Studio.

Why Clerk Over Auth.js/NextAuth?

I've used both. Clerk wins for solo developers because:

  • You don't manage passwords, sessions, or tokens

  • The UI components are pre-built and customizable

  • User management dashboard is included

  • OAuth providers (Google, GitHub) are configured in a dashboard, not in code

  • Webhook support for syncing user events to your database

The trade-off: Clerk is a hosted service with a pricing tier. For most projects, the free tier (10,000 monthly active users) is more than enough.

Initial Setup

Install the packages:

npm install @clerk/nextjs
Enter fullscreen mode Exit fullscreen mode

Create a Clerk account, create an application, and grab your keys:

# .env file
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key
CLERK_KEY=your_secret_key
Enter fullscreen mode Exit fullscreen mode

Wrap your app in the ClerkProvider:

// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs';

export default function RootLayout({ children }) {
  return (
    <ClerkProvider>
      <html><body>{children}</body></html>
    </ClerkProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Middleware: Protecting Routes

Clerk's middleware handles route protection. Create middleware.ts in your project root:

import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

export const config = {
  matcher: ['/((?!.*\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};
Enter fullscreen mode Exit fullscreen mode

Then define which routes are public and which require authentication. The pattern I use: marketing pages are public, the app is protected, API routes check auth individually.

Custom Sign-In and Sign-Up Pages

Clerk provides hosted sign-in pages, but custom pages that match your brand are better. Create them with Clerk's components:

// app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs';

export default function SignInPage() {
  return (
    <div className="your-glass-container">
      <SignIn
        signUpUrl="/sign-up"
        appearance={{
          elements: {
            rootBox: 'your-custom-styles',
            card: 'glass-card',
          }
        }}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The appearance prop lets you override every CSS class. At RAXXO Studio, the sign-in page uses our glassmorphism design system, so it feels like part of the app, not a third-party login form.

Syncing Clerk Users to Your Database

Clerk manages authentication, but your app needs user data in your own database for things like plan management, usage tracking, and feature flags. The pattern:

async function getUserFromSession() {
  const clerkUser = await currentUser();
  if (!clerkUser) throw new Error('Not authenticated');

  let dbUser = await db.select().from(users)
    .where(eq(users.clerkId, clerkUser.id))
    .limit(1);

  if (!dbUser.length) {
    // Auto-create on first API call
    dbUser = await db.insert(users).values({
      clerkId: clerkUser.id,
      email: clerkUser.emailAddresses[0].emailAddress,
      plan: 'free'
    }).returning();
  }

  return dbUser[0];
}
Enter fullscreen mode Exit fullscreen mode

This lazy-sync pattern means you never need a separate "onboarding" step. The first time a user hits any API route, their database record is created automatically.

Dev vs Production Instances

Clerk uses separate instances for development and production. This is important:

  • Development: Uses your_clerk_key_ / sk_test_ keys. Auth happens on your-clerk-dev-domain. Perfect for local development.

  • Production: Uses your_live_ / your_live_secret_ keys. You configure a custom domain (like clerk.yourdomain.com). Users created in dev don't exist in production and vice versa.

Set dev keys in .env file and production keys in your Vercel environment variables. Never mix them.

Google OAuth Setup

Adding Google sign-in through Clerk's dashboard is straightforward for development. For production, you need your own Google OAuth credentials:

  • Create a project in Google Cloud Console

  • Configure the OAuth consent screen (needs privacy policy URL)

  • Create OAuth 2.0 credentials

  • Add the client ID and secret to Clerk's production instance

The consent screen review process takes a few days. Plan for this before launch. You can use email-based sign-in while waiting for Google approval.

Webhooks for External Events

If external services need to interact with your user system (like Shopify orders triggering plan upgrades), you need to match users by email since external services don't know Clerk IDs. Build your database queries to support both Clerk ID lookup (for authenticated requests) and email lookup (for webhook processing).

Common Pitfalls

Middleware matcher: Get the regex wrong and either everything is public or everything requires auth. Test thoroughly.

Server vs client: currentUser() works in Server Components and API routes. useUser() works in Client Components. Using the wrong one gives confusing errors.

Appearance customization: The appearance prop is powerful but the documentation for specific element classes can be sparse. Use browser dev tools to find the exact class names you need to override.

RAXXO Studio uses Clerk for all authentication. Try the sign-up flow at studio.raxxo.shop.

This article contains affiliate links. If you sign up through them, I earn a small commission at no extra cost to you.

Top comments (0)