# Login Page

> A React login page with password, magic link or code sign-in, Google, Apple and Microsoft buttons, two-step codes and password recovery.

- Type: React block (Blocks)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.1.0
- Install: `npx wingo-ui@latest add login-page`
- Docs: https://wingo-ui.com/components/login-page

## Interaction

Sign in with email and password: fields validate when you leave them and as you fix them, Enter submits, a failed attempt shows a red alert above the form and keeps the email (a second failure also clears the password). The eye button reveals the password without moving the caret, and Caps Lock shows a warning. "Forgot password?" in the password label row carries the typed email to the recovery form. "Email me a sign-in link" and "Use your password" switch between the password form and the email link (or code) form. Provider buttons show a spinner on the one you pressed. After an email is sent, the "Check your email" view shows the address, opens Gmail or Outlook when the domain matches, and offers a resend that waits with a countdown ("Resend in 0:24") that survives a reload. Codes: type or paste the 6 digits and the sixth one submits; a wrong code shakes the cells and says how many tries are left, three wrong codes lock the input for a minute with a live countdown; two-step verification can switch to a 10 character backup code. The new password shows a strength meter and a checklist and must match its confirmation; the last view draws a bouncy check. Every secondary view has "Back to sign in"; each view slides in from the side it belongs to, focus moves to its title and the change is announced. On phones the form fills the screen with 48px fields and the hero is gone; with reduced motion views cross-fade.

## Installation

```bash
npx wingo-ui@latest add login-page
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/login-page` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/blocks/login-page.tsx`
- `components/blocks/login-page-frame.tsx`
- `components/blocks/login-page-data.ts`

npm dependencies: `npm i motion lucide-react react-hook-form zod`

Wingo UI dependencies (installed automatically):

- [Marketing Section](https://wingo-ui.com/components/marketing-section) ([Markdown](https://wingo-ui.com/components/marketing-section.md))
- [Alert](https://wingo-ui.com/components/alert) ([Markdown](https://wingo-ui.com/components/alert.md))
- [Avatar](https://wingo-ui.com/components/avatar) ([Markdown](https://wingo-ui.com/components/avatar.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Checkbox](https://wingo-ui.com/components/checkbox) ([Markdown](https://wingo-ui.com/components/checkbox.md))
- [Input](https://wingo-ui.com/components/input) ([Markdown](https://wingo-ui.com/components/input.md))
- [OTP Input](https://wingo-ui.com/components/otp-input) ([Markdown](https://wingo-ui.com/components/otp-input.md))
- [Password Input](https://wingo-ui.com/components/password-input) ([Markdown](https://wingo-ui.com/components/password-input.md))
- [Separator](https://wingo-ui.com/components/separator) ([Markdown](https://wingo-ui.com/components/separator.md))
- [Toast](https://wingo-ui.com/components/toast) ([Markdown](https://wingo-ui.com/components/toast.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useCooldown](https://wingo-ui.com/components/use-cooldown) ([Markdown](https://wingo-ui.com/components/use-cooldown.md))
- [Form validation](https://wingo-ui.com/components/form-validation) ([Markdown](https://wingo-ui.com/components/form-validation.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
// app/(auth)/login/page.tsx
"use client";

import { useRouter } from "next/navigation";
import { LoginPage } from "@/components/blocks/login-page";

export default function Page() {
  const router = useRouter();
  return (
    <LoginPage
      heroImage="/images/office.jpg"
      twoFactor
      onSignIn={async ({ email, password, remember }) => {
        const response = await fetch("/api/login", { method: "POST", body: JSON.stringify({ email, password, remember }) });
        if (!response.ok) throw new Error("The email or the password is not correct");
      }}
      onSuccess={() => router.push("/")}
    />
  );
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultView?` | `LoginView` | `"sign-in"` | The view shown first (uncontrolled); view / onViewChange control it. |
| `view?` | `LoginView` |  | The current view, controlled. An unknown value shows sign-in. |
| `onViewChange?` | `(view: LoginView) => void` |  | Runs on every view change (mirror it to the URL hash or the router). |
| `layout?` | `"split" \| "centered" \| "card"` | `"split"` | split: the form beside a hero from 1024px; centered: no hero, the form in a card from 640px; card: no hero, the form in a raised card at every width, centered on the page background (dots unless background is set) with the logo above and the links below. |
| `heroSide?` | `"start" \| "end"` | `"end"` | Which side the hero sits on in the split layout. |
| `background?` | `"plain" \| "soft" \| "glow" \| "aurora" \| "grid" \| "dots"` | `"soft"` | The page background: plain is the bare page color; the others tint the page and add a glow, an aurora, a grid or dots over the first screen. The layout picks one unless you set it (split: soft; centered: glow; card: dots). |
| `backgroundColor?` | `string` | `"oklch(0.62 0.14 275)"` | The tint and glow color of the page background. |
| `heroImage?` | `string` | `""` | A photo behind the hero with a dark scrim and white text; empty keeps the neutral card hero. The demo passes /demo/city.jpg. |
| `hero?` | `ReactNode` |  | Replaces the hero content (testimonial, badge and customer names from LOGIN_PAGE_SAMPLE). |
| `logo?` | `ReactNode` |  | Top-left of the form column; default the Northwind CRM mark and name. |
| `methods?` | `LoginMethod[]` |  | How people sign in, default ["password", "magic-link"]; "otp" emails a 6 digit code. The first is the form shown first; [] with providers leaves only the providers (SSO). |
| `providers?` | `AuthProvider[]` |  | Social buttons, default AUTH_PROVIDERS (Google, Apple, Microsoft); [] hides them and the divider. |
| `showProviders?` | `boolean` | `true` | Shortcut for providers={[]} when false. |
| `providerLayout?` | `"stack" \| "row"` | `"stack"` | stack: full-width "Continue with Google" buttons; row: icon buttons side by side. |
| `twoFactor?` | `boolean` | `false` | After a correct password the flow asks for the authenticator code. |
| `remember?` | `boolean` | `true` | Shows the "Remember me for 30 days" checkbox. |
| `defaultEmail?` | `string` | `""` | Prefills the email everywhere (a returning user). |
| `resendSeconds?` | `number` | `30` | Wait before a link or a code can be sent again (use-cooldown, kept across reloads under the key login-resend). |
| `passwordMinLength?` | `number` | `8` | Minimum length of a new password in the reset view (rule, checklist and strength). |
| `onSignIn?` | `(values: SignInValues) => (void \| Promise<unknown>)` |  | Email, password and remember. Default: simulated, rejects "The email or the password is not correct" below 8 characters. |
| `onMagicLink?` | `(email: string) => (void \| Promise<unknown>)` |  | Sends the sign-in link (and resends it). Default: simulated. |
| `onSendCode?` | `(email: string) => (void \| Promise<unknown>)` |  | Sends the 6 digit code for the otp method (and resends it). Default: simulated. |
| `onVerifyCode?` | `(code: string, context: VerifyCodeContext) => (void \| Promise<unknown>)` |  | Checks an otp, two-factor or backup code. Default: 123456 (or any 10 character backup code) passes, anything else rejects "Wrong code. 2 attempts left."; three failures lock the input for 60 s. |
| `onForgot?` | `(email: string) => (void \| Promise<unknown>)` |  | Requests the reset link. Default: simulated and always resolves, so the page never reveals whether an account exists. |
| `onReset?` | `(password: string) => (void \| Promise<unknown>)` |  | Saves the new password. Default: simulated. |
| `onProvider?` | `(id: string) => (void \| Promise<unknown>)` |  | Starts a social sign-in; the button spins until it settles. Default: simulated for 1.2 s. |
| `onSuccess?` | `() => void` |  | After a sign-in, a verified code, a provider or the reset-done button. Default: the toast "Welcome back, Emma" (the page mounts its own Toaster then). |
| `registerHref?` | `string` | `"/signup"` | The "Create one" link; "" hides it. |
| `termsHref?` | `string` | `"/terms"` | The "Terms" link in the footer; "" hides it. |
| `privacyHref?` | `string` | `"/privacy"` | The "Privacy" link; "" hides it. |
| `helpHref?` | `string` | `"/help"` | The "Help" link; "" hides it. |
| `size?` | `"md" \| "lg"` | `"lg"` | Fields and buttons: lg is 48px, md 40px (44px on touch); the code cells stay 48px. |
| `tone?` | `Tone` | `"neutral"` | The primary buttons and the checkbox. |
| `radius?` | `"sm" \| "md" \| "lg"` | `"md"` | The hero, the card, the icon tiles and the controls' corners. |
| `animateIn?` | `boolean` | `true` | The logo, form, footer and hero rise in once when the page mounts on the client (server html is already final). |
| `labels?` | `Partial<LoginPageLabels>` |  | Every visible string, merged over DEFAULT_LOGIN_PAGE_LABELS; validation takes form-validation messages. |
| `locale?` | `string` | `"en-US"` | Sets the page language (lang). |
| `linkComponent?` | `(props: LinkComponentProps) => ReactNode` |  | The router link for the sign-up and footer links (next/link); default <a>. |
| `classNames?` | `Partial<Record<LoginPageSlot, string>>` |  | Classes for formColumn, header, body, footer, hero, view, title and form. |

## Parts

### AuthFrame (component)

The layout of the auth screens on its own: a form column that fills the phone, with a hero beside it from 1024px, or a centered or raised card. Use it for sign-up, invite and other auth pages so they match.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `layout?` | `"split" \| "centered" \| "card"` | `"split"` | split: form column plus hero from 1024px; centered: the form in a card from 640px; card: a raised card at every width. |
| `heroSide?` | `"start" \| "end"` | `"end"` | Which side the hero sits on in the split layout. |
| `radius?` | `"sm" \| "md" \| "lg"` | `"md"` | Corner size of the hero and the card. |
| `logo?` | `ReactNode` |  | The top of the form column. |
| `footer?` | `ReactNode` |  | The bottom of the form column: a sign-up link, legal links. |
| `hero?` | `ReactNode` |  | The hero column's content, shown from 1024px in the split layout. |
| `heroLabel?` | `string` |  | The accessible name of the hero aside. |
| `slot?` | `string` | `"auth-frame"` | The data-slot prefix of the parts. |
| `animateIn?` | `boolean` | `true` | The header, body, footer and hero rise in once when mounted on the client. |
| `classNames?` | `Partial<Record<"formColumn" \| "header" \| "body" \| "footer" \| "hero", string>>` |  | Extra classes for the inner parts. |
| `children?` | `ReactNode` |  | The form. |

### DEFAULT_LOGIN_PAGE_LABELS (constant)

Every English string of the sign-in, code, two-step and password recovery views. Spread it and override what you translate.

### LOGIN_PAGE_LABELS_EN (constant)

An alias of DEFAULT_LOGIN_PAGE_LABELS, kept so older imports keep working.

### AUTH_PROVIDERS (constant)

The default social buttons: Google, Apple and Microsoft with their marks. Filter it or add your own { id, label, icon }.

### LOGIN_PAGE_SAMPLE (constant)

The demo content: the email typed in, the hero testimonial, the customer wordmarks and the code that passes the checks. Replace it with your own copy through the hero prop.

### AuthProvider (type)

A social sign-in button: { id, label, icon }. onProvider receives the id.

### SignInValues (type)

What onSignIn receives from the password form: { email, password, remember }.

## Examples

- **Forgot password**: The forgot password page: the typed email carries over and the request never reveals whether an account exists.
- **Check your email**: After the reset request: Open Gmail for a Gmail address and a resend that waits with a countdown.
- **Set a new password**: Strength meter, rules checklist and a matching confirmation; submit empty to see every error.
- **Two-step verification**: Six digit authenticator code (123456 passes) with a switch to a 10 character backup code.
- **Code by email**: methods={["otp", "password"]}: the first form sends a 6 digit code instead of a link.
- **Centered card, brand**: layout="centered" without providers and with tone="brand" on the primary actions.
- **Card layout**: layout="card": the form in a raised card on a dotted page background, the logo above it and the links below.
- **Neutral hero, en-GB**: locale en-GB and the neutral hero (no photo).

## See also

- [Password Input](https://wingo-ui.com/components/password-input) ([Markdown](https://wingo-ui.com/components/password-input.md))
- [OTP Input](https://wingo-ui.com/components/otp-input) ([Markdown](https://wingo-ui.com/components/otp-input.md))
- [Form validation](https://wingo-ui.com/components/form-validation) ([Markdown](https://wingo-ui.com/components/form-validation.md))
- [useCooldown](https://wingo-ui.com/components/use-cooldown) ([Markdown](https://wingo-ui.com/components/use-cooldown.md))
- [Register Page](https://wingo-ui.com/components/register-page) ([Markdown](https://wingo-ui.com/components/register-page.md))

---

Canonical: https://wingo-ui.com/components/login-page
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
