← All guides

Passwordless login with Supabase and Next.js

Dayzero · 3 September 2026

A password is a liability you are storing on someone else’s behalf. It needs a reset flow, strength rules, a breach policy and somewhere safe to live, and the reward for all that work is a field most people fill in wrong twice before giving up. Emailing a short code instead removes the whole category: there is nothing to reset, nothing to leak and nothing to remember. Here is what it actually takes in Next.js and Supabase, and the two settings that catch everyone out.

How the flow works

Three steps, and only the middle one is Supabase’s problem. Someone gives you an email address. You ask Supabase to send them a one-time code. They type it back and you exchange it for a session. There is no password anywhere in that sentence, which is the point.

In Supabase the first half is a single call. You send the address, and you decide there and then whether an unknown address should become a new account or be turned away — that flag is the difference between your sign-up form and your log-in form, and it is the same function either way.

The setting that will waste your afternoon

Supabase emails a code and a magic link from the same template, and the default template contains the link but not the code. So the code you are asking for never arrives, the input you built sits empty, and everything in your own application looks correct because it is.

The fix is in the dashboard, not in your repository: the email template has to include the token itself. It is worth checking that before you debug a single line, because nothing in the code will ever tell you.

The second one: code length

The length of the code is configured in Supabase, and your validation has to agree with it. Six digits is the sensible default. If your form checks for six and the dashboard is set to eight, every genuine code is rejected as malformed, and the error your user sees says their code is wrong when it is your form that is.

Keep the number in one constant and derive the validation from it, so the two cannot drift apart. Then when you change it, you change it once.

Verifying, and the retry nobody plans for

Exchanging a code for a session is one call — but a brand-new signup’s token and a returning user’s token are not always the same type, and using the wrong one fails. The robust approach is to try the common case and fall back to the other. A failed verification does not consume the token, so the retry is free and invisible.

What to do about the wait

Email takes seconds and sometimes tens of seconds. Two things make that bearable. Say out loud that the code can take a minute and to check the spam folder, because otherwise the first thing a confused person does is request another code — which invalidates the one currently landing in their inbox. And put a cooldown on the resend button for the same reason.

Also give them a way back. If someone mistypes their address they will sit on your verification screen waiting for an email that is never coming, and if your log-in flow refuses to confirm whether an account exists — which it should, to avoid telling strangers who has one — that screen is a dead end without a “wrong email?” link.

Where the account record comes from

Supabase keeps its own auth table, and you almost certainly want a row of your own alongside it for anything you need to query. Create it with a database trigger on the auth table rather than from your application code: a trigger cannot be skipped by a code path you forgot, and any profile fields collected at sign-up can ride along with the original request and be copied across by the same trigger.

Is it worth it?

For most products, yes — and the reason is not security, it is the reset flow you never build. But it is not free. You are now dependent on email delivery for log-in, not just for notifications, so a deliverability problem is an outage. Set up a real sending domain before you launch rather than after, and treat those emails as the critical path they now are.

Stop reading. Start finding out.

The Dayzero codebase gives you the first week already built, and the community gives you people to test it with — the same six questions, every round.