Tutorials / Backend integrations / Auth with Clerk
πŸ“ Written ● Beginner Updated 2026-05-13

How do I add auth to my app with Clerk?

TL;DR: Sign up at clerk.com, create an application, install @clerk/nextjs (or your framework's package), wrap the app in <ClerkProvider>, drop in <SignIn /> and <UserButton />. Free tier: 10,000 MAUs.

Drop in <SignIn /> and you have a working auth UI. Clerk handles passwords, OAuth, MFA, sessions, organizations β€” you don't write any of it. Free tier covers 10K monthly active users.

When to pick Clerk (vs. alternatives)

0
  • Pick Clerk when β€” you want the auth UI to look polished without designing one, you need organizations / multi-tenancy out of the box, or you don't want to manage session refresh / CSRF / OAuth flows.
  • Pick Supabase Auth if you're already on Supabase for the database. Free, in one signup.
  • Pick Google OAuth manually if you specifically want one provider, no UI library, no monthly cost ceiling.
  • Pick Auth0 β†— for enterprise SAML / SSO / heavy compliance needs.

Clerk's sweet spot: fast-moving SaaS startups that want auth done well without thinking about it.

Sign up + create an app

1

Sign up at dashboard.clerk.com β†—. Free tier: 10,000 monthly active users, no credit card needed.

Create an application. Pick which sign-in methods to enable:

  • Email + password
  • Magic link (passwordless)
  • Phone (SMS code)
  • Google, GitHub, Apple, Microsoft, Discord, Twitter, LinkedIn, Facebook, and more β€” toggle on, Clerk handles the OAuth registration for the social providers (no client ID setup on your end).

Grab your keys

2

Dashboard β†’ API Keys. Copy two values:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
CLERK_SECRET_KEY=sk_test_xxxxx

Publishable is safe to ship to the browser; secret is backend-only.

Install + wrap your app

3

Next.js App Router (most common):

npm install @clerk/nextjs

In middleware.ts at the project root:

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

export default clerkMiddleware();

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

In app/layout.tsx:

import { ClerkProvider } from '@clerk/nextjs';

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

Done. Auth is now wired through your whole app.

Other frameworks

Drop in the sign-in UI

5

Clerk ships pre-built components. In Next.js:

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

export default function Page() {
  return <SignIn />;
}

That's the entire sign-in page. Polished UI with whatever providers you enabled in Step 1. Same shape for <SignUp />, <UserButton /> (avatar dropdown with account management), and <OrganizationSwitcher />.

All components β†—.

Protect a page

6
// app/dashboard/page.tsx
import { auth } from '@clerk/nextjs/server';
import { redirect } from 'next/navigation';

export default async function Dashboard() {
  const { userId } = await auth();
  if (!userId) redirect('/sign-in');

  return <div>Welcome to your dashboard</div>;
}

Or use middleware to protect entire route groups β€” see clerkMiddleware docs β†— for the matcher patterns.

Get the current user in your API

7
// app/api/me/route.ts
import { auth, currentUser } from '@clerk/nextjs/server';

export async function GET() {
  const { userId } = await auth();
  if (!userId) return new Response('Unauthorized', { status: 401 });

  const user = await currentUser();
  return Response.json({
    email: user.emailAddresses[0].emailAddress,
    name: user.firstName,
  });
}

For non-Next frameworks: use JWT verification β†— from the official Backend SDK.

Customize the UI

8

Clerk components ship looking generic-but-clean. To match your brand:

<ClerkProvider appearance={{
  baseTheme: undefined,
  variables: {
    colorPrimary: '#6366f1',
    colorBackground: '#ffffff',
    borderRadius: '8px',
    fontFamily: '"DM Sans", system-ui, sans-serif',
  },
}}>
  ...
</ClerkProvider>

For deeper customization (your own components), use Clerk's Elements primitives β†— β€” unstyled headless components you wrap with your own design system.

Multi-tenant: Organizations

9

If your product is B2B (each user belongs to one or more companies), enable Organizations in the dashboard:

Configure β†’ Organizations β†’ Enable. Then add <OrganizationSwitcher /> to your UI β€” gives users a switcher dropdown to flip between orgs they belong to.

Server-side, the current org context is on auth():

const { userId, orgId, orgRole } = await auth();

Built-in roles: admin, basic_member. Add custom permissions in the dashboard. Organizations docs β†—.

Webhooks: sync to your database

10

Clerk holds the user records. Your database holds your app data. To link them, listen for Clerk webhooks:

Clerk dashboard β†’ Webhooks β†’ New endpoint. Pick events (user.created, user.updated, user.deleted). Get the signing secret.

// app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix';

export async function POST(req) {
  const payload = await req.text();
  const headers = Object.fromEntries(req.headers);
  const wh = new Webhook(process.env.CLERK_WEBHOOK_SECRET);
  const event = wh.verify(payload, headers);

  if (event.type === 'user.created') {
    await db.users.insert({
      clerk_id: event.data.id,
      email: event.data.email_addresses[0].email_address,
    });
  }
  return new Response('ok');
}

Same signature-verify pattern as Stripe webhooks. Clerk webhooks docs β†—.

Migrate users in / out

11

If you already have a user table and want to import: use the migration guides β†—. Bulk-import API accepts hashed passwords from common formats (bcrypt, argon2, PBKDF2).

If you're worried about vendor lock-in: Clerk exports all users + sessions on demand. Migrating out is annoying but possible.

Production checklist

12
  • Switch to production instance in the dashboard before launch β€” separate keys from dev.
  • Custom domain β€” auth pages on auth.yourdomain.com instead of your-app.clerk.accounts.dev. Free; one DNS record.
  • Enable MFA β€” Configure β†’ User & Authentication β†’ Multi-factor. TOTP + SMS + backup codes.
  • Bot protection β€” toggle on. Free Cloudflare Turnstile by default.
  • Session length β€” default 7 days. Tighten if your app is sensitive.
Pricing climbs past 10K MAU. Free tier is 10,000 monthly active users. Pro tier starts at $25/mo + per-MAU after that. For B2C products that grow fast, the bill grows fast. Run the math at your projected scale; Clerk pricing β†—.

Official references

What's next