BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 15, 2026ReactBetter Auth UI

Add Magic-Link Sign-In to BTST Better Auth UI

Connect email delivery, the Better Auth client plugin, and BTST auth overrides; verify redirects, registration policy, and the resulting session.

Add Magic-Link Sign-In to BTST Better Auth UI

Magic-link sign-in needs three connected pieces: Better Auth must issue and verify the link, its browser client must expose the matching method, and BTST must show the email form. Turning on a UI option alone does not configure email delivery.

This guide extends a working BTST auth integration using @btst/better-auth-ui@2.0.0 and Better Auth 1.6.16. Start with the Better Auth UI integration guide if routes, sessions, and the provider are not connected yet. Keep the compatible dependency group together.

1. Add the server plugin and real delivery#

Create a small plugin configuration beside your existing server auth configuration:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
import { magicLink } from "better-auth/plugins/magic-link";

type DeliverEmail = (message: {
  to: string;
  subject: string;
  text: string;
}) => Promise<void>;

export function emailSignIn(deliverEmail: DeliverEmail) {
  return magicLink({
    expiresIn: 300,
    disableSignUp: true,
    sendMagicLink: async ({ email, url }) => {
      await deliverEmail({
        to: email,
        subject: "Your sign-in link",
        text: `Sign in using this link: ${url}`,
      });
    },
  });
}

Add emailSignIn(deliverEmail) to the existing betterAuth({ plugins: [...] }) array. Preserve your database, other plugins, secret, trusted origins, and auth handler. deliverEmail is your server email integration; make it reject when the provider reports failure, including providers that return an error object without throwing. The example uses a plain-text email so a URL does not need HTML-attribute escaping.

This configuration deliberately allows existing accounts only. Set disableSignUp: false if email-link registration is part of your product. Hiding a sign-up link in BTST does not enforce that policy on the server. Better Auth's magic-link documentation describes the delivery callback, expiration, and registration setting.

Use the verification URL that Better Auth supplies. It contains the token and points at the auth API; replacing it with the UI form URL discards the verification step. Keep token-bearing URLs out of application logs and analytics. Retain the configured origin checks and rate limits, and verify production email delivery with a test account before making this the only login method.

2. Add the browser plugin#

Add magicLinkClient() to the plugins of the same auth client that your BTST provider uses:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
import { createAuthClient } from "better-auth/react";
import { magicLinkClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [magicLinkClient()],
});

This is the minimal client example. In an existing application, preserve other client plugins and any configured API origin or base path. Creating an unrelated second client can leave the rendered form using a client without signIn.magicLink.

The server plugin and client plugin have different jobs. One owns token creation and verification; the other makes the browser method available. The released BTST form calls authClient.signIn.magicLink and handles pending, success, and error states.

3. Enable the BTST form#

Merge these fields into your existing StackProvider's overrides.auth object:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
{
  authClient,
  magicLink: true,
  redirectTo: "/p/account/settings",
  onSessionChange: () => router.refresh(),
}

This is a configuration fragment inside an existing Next.js client layout, where router comes from useRouter. Keep the provider's authorization adapter, identity handling, and other overrides. Use an existing route in your application for redirectTo.

With a site mount of /p, the default email form is /p/auth/magic-link. The released route definition supplies the route; its magicLink override enables the UI feature. A different site mount changes the public URL.

Keep the companion's callback route and navigation configuration intact. The form can route verification through its callback page when client persistence is enabled. Avoid constructing a competing callback flow until the built-in flow works. For copy and layout changes, use the per-page customization guide.

Trace one complete sign-in#

StepExpected resultWhere to investigate a failure
Submit the email formThe auth API accepts the requestClient plugin, endpoint mount, origin checks, rate limit
Deliver the messageA usable message reaches the test inboxServer callback and email-provider result
Open the supplied linkBetter Auth verifies the token and establishes a sessionExpiry, prior token use, configured API/site origins
Return to the appThe intended page shows the signed-in stateCallback route, provider session refresh, server identity
Request protected dataThe backend applies the account's permissionsAuthorization contract and request credentials

A successful delivery toast does not establish a session. A session does not grant access to every plugin operation. Test an existing account, an unknown email under your chosen registration policy, an expired link, reuse of a redeemed link, and a protected page after sign-in. Use controlled test accounts and inspect provider results without recording tokens.

The snippets here were checked against the stated package versions. They do not constitute an end-to-end email-provider or browser-session test. Your deployment's email delivery, redirects, and authorization need that final exercise.

Continue with the Better Auth UI documentation for the complete provider setup and BTST installation for the framework routes.

In This Post

1. Add the server plugin and real delivery2. Add the browser plugin3. Enable the BTST formTrace one complete sign-in