Use case
Add magic link login to Next.js
Magic link login in Next.js needs two things: an auth layer that issues and verifies single-use tokens, and an email path that reliably delivers them. AscendKit provides both, so you enable one provider and the link email goes out through your verified domain on AWS SES. Below: the full App Router setup, how magic link compares with passwords and OAuth, and the delivery failures that break passwordless auth in production.
How it works
- 1. Install and configure the SDKAdd @ascendkit/nextjs, run ascendkit init, and mount the auth route handler. The CLI writes your keys into .env.local.
- 2. Enable magic link as a providerTurn magic link on for the environment. It is a configuration change, not a code change — the SDK loads the Better Auth magic-link plugin when the provider is enabled.
- 3. Verify your sending domainMagic link auth is only as reliable as its email. Verify your domain so links send from your address with DKIM and SPF, not a shared subdomain.
How do you add magic link login to a Next.js App Router app?
Three files. Mount the AscendKit auth runtime, expose the catch-all auth route, and wrap your app in the provider — magic link then works through the same components as every other provider.
The provider list is environment configuration rather than code. When magic link is enabled, the SDK dynamically imports Better Auth's magic-link plugin and wires its sendMagicLink callback to AscendKit's email sender, so the token email routes through your verified domain automatically.
That is the part teams usually underestimate. Issuing a signed token is easy; getting it into an inbox within seconds, from a domain with correct DKIM and SPF, is the work. Because auth and email are the same platform here, there is no webhook between an auth vendor and an email vendor for the link to fall through.
import { createAscendKitAuthRuntime } from "@ascendkit/nextjs/server";
import { createAuthRouteHandlers } from "@ascendkit/nextjs/server";
export const authRuntime = createAscendKitAuthRuntime();
export const { GET, POST } = createAuthRouteHandlers(authRuntime);How do you render the sign-in form?
Wrap the app in AscendKitProvider and drop in a sign-in surface. The provider reads which providers are enabled for the environment and renders the matching form, so turning magic link on in the dashboard changes the UI without a deploy.
You have three UX patterns available: a global modal mounted by the provider, individual SignInButton and SignUpButton components placed anywhere in your layout, or the full AscendKitAuthCard embedded on a dedicated route. Magic link works identically across all three — the user enters an email, receives a link, and lands back authenticated.
import { AscendKitProvider } from "@ascendkit/nextjs";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<AscendKitProvider>{children}</AscendKitProvider>
</body>
</html>
);
}Magic link vs passwords vs OAuth: which should you use?
Magic link removes the password from your threat model entirely — no hashes to leak, no reset flow to build, no credential stuffing. The cost is that every sign-in depends on email delivery, and users on a different device from their inbox hit friction.
One constraint specific to AscendKit is worth knowing before you choose: enabling magic link disables credentials sign-in for that environment. They are alternative primary factors, not complementary ones. OAuth can be combined with either.
| Magic link | Password | OAuth | |
|---|---|---|---|
| Credential to steal | None stored | Password hash | None stored |
| Reset flow needed | No | Yes | No |
| Depends on email delivery | Every sign-in | Reset only | No |
| Works offline from inbox | No | Yes | Yes |
| Setup cost | Domain verification | Lowest | OAuth app registration |
| Combines with AscendKit credentials | No — mutually exclusive | n/a | Yes |
Why do magic links land in spam, and how do you prevent it?
Because the email is transactional but looks promotional to filters, and because unverified domains have no authentication signal. The fix is domain verification with DKIM and SPF before you launch, not after users complain.
AscendKit runs delivery on AWS SES and automates the setup that teams usually defer: domain verification, DKIM record generation, SPF alignment, and DNS provider detection. Sending from your own verified domain rather than a shared subdomain is the single largest factor in whether magic links arrive.
The second factor is content. Keep the link email short, transactional, and free of marketing copy or tracking-heavy markup. A one-line message with a single button outperforms a designed template for this specific email type.
Troubleshooting
The magic link says it has expired even though it was just sent
Cause: The link was opened by an email security scanner before the user clicked it. Corporate mail gateways pre-fetch URLs to check for malware, which consumes a single-use token.
Fix: This is a known trade-off of single-use tokens in enterprise environments. If your users are on corporate mail, prefer OAuth or credentials as the primary method for those accounts.
Clicking the link signs in on the wrong device
Cause: The user requested the link on desktop but opened their email on a phone. The session is created wherever the link is opened.
Fix: Expected behaviour, not a bug. Set expectations in the UI copy on the check-your-email screen so users know to open the link on the device they want to be signed in on.
Emails never arrive during local development
Cause: The sending domain is unverified, so delivery is restricted.
Fix: Verify your domain in the dashboard before testing sign-in flows. Check the email delivery logs to confirm whether a send was attempted and what the provider returned.
FAQ
Do I need a separate email service for the magic link?
No. Email is part of the platform, so the link is delivered from your verified domain on AWS SES without adding a vendor or a webhook between auth and email.
Can I combine magic link with passwords?
No. Enabling magic link disables credentials sign-in for that environment — they are alternative primary factors. OAuth providers can be combined with either.
How long is a magic link valid?
Links are single-use and short-lived, following Better Auth's magic-link plugin defaults. Opening a link consumes it, so a second click on the same link will fail.
Does magic link work outside Next.js?
The React hooks and components work in Vite, Remix, and Astro. The server-side auth runtime requires Next.js; other backends verify access tokens through the Python SDK instead.
Related guides
- Add auth and email to React + Vite — the same components without a Next.js server behind them.
- Add auth and transactional email to Next.js — the broader setup this page is a slice of, covering welcome email and verification alongside sign-in.
- Send email without managing SES and DNS — the domain verification, DKIM, and SPF work that determines whether your magic links actually arrive.
- Add social login without registering OAuth apps — the provider you can safely combine with magic link, unlike credentials.
- AscendKit vs Clerk — how the bundled email path compares with wiring Clerk to a separate email vendor.
Start with one API key
Auth, email, surveys, and journeys share one user record, so you ship this without stitching vendors together.
Start free