Choosing between Clerk and Auth.js often stalls a Next.js project before a single feature ships. Both solve authentication, but they represent opposite philosophies: managed service versus self-hosted library.
The Clerk vs Auth.js decision comes down to whether you want to own your auth infrastructure or rent it. I've built production apps with both, and the tradeoffs are sharper than most tutorials admit. Let's break down what actually changes when you pick one.
Clerk vs Auth.js: The Key Differences
The core difference is operational. Clerk is a fully managed authentication platform. You send users to Clerk's hosted sign-in pages (or use their prebuilt components), and Clerk handles sessions, password resets, social logins, and user management in their cloud. Auth.js (formerly NextAuth.js) is a library you install into your own codebase. You configure providers, design your own UI, and manage session storage yourself.
This changes your security posture. With Clerk, you're trusting a third party with your users' credentials. With Auth.js, you're responsible for securing that data, but you also have full control over the token lifecycle and database schema.
The second major difference is developer experience. Clerk's setup takes minutes — create an app, add a key, and you're done. Auth.js requires more wiring: a database adapter, session strategy decisions, and provider configuration. But that setup time buys you flexibility.
When to Use Clerk
Use Clerk when authentication is a means to an end, not your product. If you're building a SaaS MVP, a landing page with user accounts, or an internal tool, Clerk removes an entire category of bugs and security reviews.
Here's how a basic Clerk setup looks in a Next.js app:
// app/page.tsx
import { SignInButton, SignedIn, SignedOut } from "@clerk/nextjs";
export default function Home() {
return (
<div>
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>
<p>Welcome back!</p>
</SignedIn>
</div>
);
}
That's it. No session handling, no CSRF tokens, no cookie management. Clerk also gives you a user management dashboard out of the box, which is a huge win if you need to look up users, reset passwords, or handle support tickets without writing admin code.
When to Use Auth.js
Use Auth.js when you need control over the authentication flow, when you're dealing with strict compliance requirements, or when you're integrating with a custom backend that already has user data.
The biggest practical win with Auth.js is database ownership. Clerk stores user data in their database. If you need to join user records with your own tables, you're making API calls to Clerk. With Auth.js, users live in your database as a first-class citizen.
Here's a concrete difference with credentials provider:
// auth.ts
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { compare } from "bcryptjs";
import { prisma } from "@/lib/prisma";
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Credentials({
async authorize(credentials) {
const user = await prisma.user.findUnique({
where: { email: credentials.email },
});
if (!user || !(await compare(credentials.password, user.password))) {
return null;
}
return user;
},
}),
],
session: { strategy: "jwt" },
});
With Auth.js, you're writing the auth logic against your own schema. You decide what happens on failed login, how sessions expire, and what claims go into the JWT. Clerk gives you a config UI for most of this, but you can't step outside their model.
Clerk or Auth.js: Which One Should You Pick?
The question that decides it: Do you have a reason to store user data in your own database?
If yes — because you need custom fields, complex relationships, or compliance mandates — pick Auth.js. If no, and you just need people to log in, pick Clerk.
Auth.js is also the better choice if you're building an open-source project. Contributors shouldn't need to sign up for a third-party service to run your code locally. Clerk is better for commercial products where time-to-market matters more than infrastructure control.
My Take
I default to Clerk for client work unless there's a hard requirement otherwise. The reason is simple: authentication is a solved problem, and solving it again for every project wastes weeks. Clerk's prebuilt components, session management, and admin dashboard are worth the vendor lock-in for most applications.
But if you're building a platform where users are the product — think a social network, a marketplace, or a B2B tool with complex roles — you'll hit Clerk's limits fast. Auth.js gives you the escape hatch to build exactly what you need without fighting an external API.
The one thing that makes this decision obvious: if you can't articulate why you need users in your own database, you don't — use Clerk. If you can, you've already outgrown it.