UI.MD

Auth Form

The sign-in form on a page of its own: provider buttons, a passkey, an email form, a forgot-password link, and an error you can set.

Installation

$
npx shadcn@latest add https://ui.mattdowney.com/r/auth-form.json

Usage

import { AuthForm } from "@/components/ui/auth-form"

Examples

Default

A sign-in page's form: two providers, email and password, a forgot-password link and a footer link to sign up. Plain anchors in both slots are styled for you.

Sign in

Welcome back. Sign in to your account.

or continue with
Don't have an account? Create one

Wrong password

Many auth clients return a failure instead of throwing it. Pass that message as error; clear it when the next attempt starts. Submit to see it.

Sign in

Choose how you want to continue

Magic link

The default email mode sends a link instead of asking for a password, so the submit reads "Send magic link".

Sign in

Choose how you want to continue

or continue with

API Reference

PropTypeDefaultDescription
providersAuthForm("google" | "github" | "microsoft" | "apple")[]all fourWhich OAuth provider buttons to render, in array order. Pass [] to render none.
showPasskeyAuthFormbooleantrueRenders the passkey button in the method stack.
showEmailAuthFormbooleantrueRenders the email form (and the divider when methods render above it).
emailModeAuthForm"magic-link" | "password""magic-link"Magic link collects email only. Password adds a password field and types password as required in the continue payload.
forgotPasswordAuthFormReactNode-Rendered beside the Password label in password mode. Pass the link itself, an <a> or a router Link; it gets Button's link look with no classes.
errorAuthFormstring | null-An error you already know about, such as a wrong password your auth client returned or a ?error= code from a redirect. Shown and announced under the description. A rejected handler's message wins while it lasts.
submitLabelAuthFormReact.ReactNode"Send magic link" / "Continue"Overrides the email submit button's label. The default follows emailMode.
onProviderContinueAuthForm(provider) => void | Promise<void>-Called with the clicked provider. Return a promise to get pending and error handling; return void for an immediate redirect.
onPasskeyContinueAuthForm() => void | Promise<void>-Called when the passkey button is clicked. Same promise contract.
onEmailContinueAuthForm({ email, password? }) => void | Promise<void>-Called on a valid email submit (native validation gates it). The payload is discriminated by emailMode.
titleAuthFormReactNode"Sign in"Rendered as the page's h1.
descriptionAuthFormReactNode"Choose how you want to continue"Line under the title.
footerAuthFormReactNode-Small centered copy at the bottom, such as a sign-up link or terms. Anchors get the inline-link style.
surfaceAuthForm"card" | "bare""card"card draws the form on a Card, for a page with other things on it. bare drops the Card so the form stands on the page, as it does in Auth Layout, and sets the title one step larger.
classNameAuthFormstring-Applied to the root (the Card, or the bare wrapper). It is w-full max-w-sm; place it with the page's own layout or Auth Layout.
providersAuthForm("google" | "github" | "microsoft" | "apple")[] = all fourWhich OAuth provider buttons to render, in array order. Pass [] to render none.
showPasskeyAuthFormboolean = trueRenders the passkey button in the method stack.
showEmailAuthFormboolean = trueRenders the email form (and the divider when methods render above it).
emailModeAuthForm"magic-link" | "password" = "magic-link"Magic link collects email only. Password adds a password field and types password as required in the continue payload.
forgotPasswordAuthFormReactNodeRendered beside the Password label in password mode. Pass the link itself, an <a> or a router Link; it gets Button's link look with no classes.
errorAuthFormstring | nullAn error you already know about, such as a wrong password your auth client returned or a ?error= code from a redirect. Shown and announced under the description. A rejected handler's message wins while it lasts.
submitLabelAuthFormReact.ReactNode = "Send magic link" / "Continue"Overrides the email submit button's label. The default follows emailMode.
onProviderContinueAuthForm(provider) => void | Promise<void>Called with the clicked provider. Return a promise to get pending and error handling; return void for an immediate redirect.
onPasskeyContinueAuthForm() => void | Promise<void>Called when the passkey button is clicked. Same promise contract.
onEmailContinueAuthForm({ email, password? }) => void | Promise<void>Called on a valid email submit (native validation gates it). The payload is discriminated by emailMode.
titleAuthFormReactNode = "Sign in"Rendered as the page's h1.
descriptionAuthFormReactNode = "Choose how you want to continue"Line under the title.
footerAuthFormReactNodeSmall centered copy at the bottom, such as a sign-up link or terms. Anchors get the inline-link style.
surfaceAuthForm"card" | "bare" = "card"card draws the form on a Card, for a page with other things on it. bare drops the Card so the form stands on the page, as it does in Auth Layout, and sets the title one step larger.
classNameAuthFormstringApplied to the root (the Card, or the bare wrapper). It is w-full max-w-sm; place it with the page's own layout or Auth Layout.