SpectreSkills
← All skills

clerk-nextjs-auth

v1.0.0

Wire Clerk into a Next.js App Router app — middleware-protected dashboard routes, sign-in/sign-up pages, an auth-aware home CTA, and the user menu. Trigger when adding authentication to a dashboard app or protecting a route subtree.

Web & Frontendclerkauthnextjsmiddleware
Install
npx @spectre-apps/skills add clerk-nextjs-auth

Writes .claude/skills/clerk-nextjs-auth/SKILL.md. Add --user to install globally.

What it does

Clerk auth for Next.js

Use Clerk for the whole auth surface — session, sign-in/sign-up UI, and the user menu — and enforce protection in one place: the middleware. Pages and layouts may read auth state, but never gate on it.

Install & provider

npm install @clerk/nextjs

Wrap the root layout — Clerk sits above everything, including marketing pages, so public pages can render <SignedIn> / <SignedOut> branches:

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

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  );
}

Middleware — the single gate

Protect the dashboard subtree by path prefix. Everything else stays public:

// middleware.ts
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtected = createRouteMatcher(["/dashboard(.*)"]);

export default clerkMiddleware(async (auth, req) => {
  if (isProtected(req)) await auth.protect();
});

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

auth.protect() redirects signed-out visitors to sign-in and returns them to the page they wanted after authenticating.

Sign-in / sign-up pages

Use Clerk's prebuilt components on catch-all routes:

app/sign-in/[[...sign-in]]/page.tsx   →  <SignIn />
app/sign-up/[[...sign-up]]/page.tsx   →  <SignUp />

Point Clerk at them in .env.local:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_…
CLERK_SECRET_KEY=sk_test_…
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/dashboard

After sign-in, land users in the dashboard — not back on the marketing page.

Reading auth state

  • Public pages / nav: <SignedIn> / <SignedOut> for the CTA swap, and <UserButton /> in the dashboard header for the account menu.
  • Server Components & server actions: const { userId } = await auth() from @clerk/nextjs/server. In server actions, treat a missing userId as an error even though middleware should have caught it — actions are an API surface of their own.
  • Store userId (the Clerk ID) as the owner column on your own tables — see drizzle-postgres-docker. Don't mirror Clerk's user profile into Postgres unless you need to query it.