WebberUI

Auth Pages

A sign-in / sign-up page pair with form motion — fields expanding and collapsing, heading and button text morphing, and a submit loading state.

This is a WebberUI Pro component

Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.

How to install Pro components →See the plans →

Loading preview…
npx shadcn@latest add "https://webberui.com/r/auth-pages.json?t=<install token>"

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

<AuthPages />

Installation

npx shadcn@latest add "https://webberui.com/r/auth-pages.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/auth-pages.

Usage

import { AuthPages } from "@/components/ui/auth-pages";
import { Apple, Mail } from "lucide-react";

<AuthPages
  defaultMode="login"
  onSubmit={async (mode, values) => {
    await signIn(mode, values);
  }}
  onSocial={(id) => oauth(id)}
  socialProviders={[
    { id: "email", label: "Email", icon: Mail },
    { id: "apple", label: "Apple", icon: Apple },
  ]}
/>;

Controlled mode

When mode is not passed, the component manages the sign-in / sign-up state itself (uncontrolled mode). To keep it in sync with routing or external state, switch to the controlled form:

const [mode, setMode] = React.useState<AuthMode>("login");

<AuthPages mode={mode} onModeChange={setMode} onSubmit={handleSubmit} />;

The split style

variant="split" adds a brand sidebar with a flowing gradient on the left on wide screens, collapsing automatically into a single-column card on narrow ones. Use aside to override the sidebar's content.

<AuthPages variant="split" aside={<MyBrandPanel />} onSubmit={handleSubmit} />;

Props

PropTypeDefaultDescription
mode"login" | "signup"The current mode in controlled mode; when omitted, the component is uncontrolled
defaultMode"login" | "signup""login"Initial mode in uncontrolled mode
onModeChange(mode) => voidFires when the mode switches
onSubmit(mode, values) => void | Promise<void>Submits the form; when it returns a Promise, the loading state is shown
onSocial(providerId) => voidFires when a social sign-in button is clicked
variant"card" | "split""card"Layout style
brandReactNodeThe brand block at the top of the card
socialProvidersAuthSocialProvider[]The social sign-in list; leave it empty to hide it
asideReactNodeSidebar content for the split style
classNamestringStyles for the outer container

The values in onSubmit are AuthSubmitValues: in sign-in mode they contain email, password, and remember; in sign-up mode they contain name, email, password, and confirmPassword instead.

How it works

  • On mount, the card's contents fade in and slide up one after another with a stagger
  • When the mode switches, the name and confirm-password fields spring their height open and closed, and the sign-in-only "remember me / forgot password" row is swapped in step
  • The heading and the submit button's text change with a morph transition, and a spinning loading icon is shown while submitting

Accessibility

  • The form is associated with the heading through aria-labelledby, and every field has a matching <label> and autoComplete
  • The show-password toggle is a real button with an aria-label and aria-pressed
  • Pressing Enter mid-composition does not accidentally submit the form during IME composition in Chinese and other languages (including the Safari keyCode 229 case)
  • When the user has "reduce motion" enabled, the reveal, the field expansion, and the text morph all switch instantly instead, and the sidebar gradient stops flowing

On this page