Why we migrated from Clerk to NextAuth (Auth.js).

Why we migrated from Clerk to NextAuth (Auth.js).
Thibault Le Ouay Ducasse

May 15, 2024โ€ข5 min read

We recently switched from Clerk to NextAuth (Auth.js) for our authentication. Now, let's explore the reasons for this decision.

Why did we migrate from Clerk to NextAuth (Auth.js)? ๐Ÿค”

First, Clerk is an amazing product. But we are building an open-source project and creating an account on Clerk, setting up social login, and routing the webhook to localhost was a pain for new contributors as it required a tunnel to localhost:3000 via e.g. ngrok.

Our goal is to improve the contributor experience (CX) for OpenStatus by speeding up the time to first line of code. #TTFLOC

Our efforts have already simplified the process significantly. With SQLite and now NextAuth, we can seed the DB and login with an email (magic link gets printed in the console) within less than a minute.

Not gonna lie, we still have some more improvements to make, like adding a better Tinybird setup, but saving it for later! โœ๏ธ

Why did we start with Clerk? ๐Ÿง‘โ€๐Ÿ’ป

When we started OpenStatus, we needed a way to authenticate users. We wanted to make it easy for users to sign up for our platform. Clerk was a good choice for us because it provided a simple way to authenticate users.

The documentation is clear, the setup is straightforward, and it is working flawlessly with Next.js App Router.

At the same time, NextAuth was starting a transition to Auth.js and while we wanted to use the latest tech and have chosen drizzle over prisma, there was no proper adapter for it.

Back then, Clerk was the perfect fit.

Why did we pick NextAuth (Auth.js) and not Lucia? ๐Ÿ”‘

First, when we thought about migrating from Clerk, we considered Lucia. Lucia is an amazing authentication library that is gaining popularity built by Pilcrow.

We were scared about the stability and direction of the project. When you implement authentication, you want to do it once and forget about it after.

I think in the future, Lucia will just be a session management library. Remove user handling from it entirely. If you need the user object, either create your own adapter or, for small projects, just fetch users after you validate the session


We decided to go with NextAuth because it was more mature, and it seemed more stable to us. Additionally, we both had some experience with NextAuth. It was a good choice for us as we did not have to learn a new library.

How did we migrate from Clerk to NextAuth (Auth.js)? ๐Ÿš€

We were scared of the migration at first. We thought it would be long and hard. But the migration was quite simple, as we were already storing most of the user data in our database.

What we already had in place

Whenever a user signed up with Clerk, we stored the user's data in our database. We had to store the tenantId from Clerk to be able to fetch map the user, and we additionally stored email, name, photoUrl, and SQLite created an additional primary key id.

All the other information of the user's social account (Google, GitHub OAuth) was stored in Clerk.

Starting with NextAuth

Whenever you start with NextAuth, you choose your DB/ORM adapter and create the tables you need:

  • users
  • sessions
  • accounts
  • verificationTokens

With drizzle as our ORM, we followed the following steps from here, including using SQLite as schema.

Now because we already had the users table, we extended it with the necessary fields or mappings in our extended adapter (e.g., we have kept the photoUrl field and did not use the image field from NextAuth to store the social image of the user). NextAuth allows you to override the adapter functions and that's what we did: we overrode the getUser and createUser functions to match our current user db schema.

The other tables sessions, accounts, and verificationTokens were created as is.

Now you might wonder: How did we migrate the data? We simply did not.

While Clerk has all the information for the social accounts of every user, we are connecting the user to the account when they log in the next time with. NextAuth provides a allowDangerousEmailAccountLinking property that re-links the user to the account when they log in with the same email.

We still have some issue with types and NextAuth. We have an open issue #812 if you want to help us ๐Ÿค—.

Customizing the UI

We have been using Clerk's UI for a long time and swapped it with custom components. But luckily our website is powered by shadcn UI and we could easily create the components.


If you are building an open-source project, we recommend NextAuth. But if it's a closed-source SaaS project, Clerk is a good choice.

We are happy with our decision to migrate from Clerk to NextAuth (Auth.js). While we will miss some Clerk features (analytics), we look forward to exploring what NextAuth offers us.

If you have any questions about our migration, feel free to send us an email at

Our main PR for the migration is #801 while we implemented the magic link for dev mode in #806 and improved the types in #807.