
In our previous guide, we set up Better Auth with Nodemailer to handle email verification and invites. Today, we are tackling a critical feature for any production app: The "Forgot Password" flow.
While Magic Links (links that log you in or reset your password immediately) are popular, they force the user to leave your app, open their email, and click a link that opens a new tab.
Sometimes, you want to keep the user right where they are. In this guide, we will build an OTP (One-Time Password) reset flow. The user enters their email, receives a 6-digit code, and resets their password without ever closing your tab.
Good news: If you followed the previous guide, your backend is already ready.
The emailOTP plugin we installed previously handles the forget-password type automatically. When we call the forgot password function from the client, Better Auth will trigger the sendVerificationOTP hook we defined in auth.ts with the type set to "forget-password".
Just ensure your email.ts template handles the subject line dynamically (or generic enough) for password resets.
We need to handle two distinct stages in our UI:
Instead of dumping 200 lines of UI code, let's look at the core functions using authClient.
Here is how we compose the UI. I've simplified the styling to focus on the structure, but you can keep your Tailwind gradients for that premium feel.
The Request Form (Stage 1):
The Reset Form (Stage 2):
I chose OTP for implementation, but Better Auth support both. Here is a quick breakdown to help you decide which fits your app better.
Pros:
Cons:
Pros:
Cons:
By using the emailOTP plugin in Better Auth, we've created a password reset flow that keeps users engaged within our application. It's a small UX detail, but keeping users inside your app ecosystem always leads to higher conversion and retention.