Skip to content
    Style: Wireframe

    Blocks · Forms

    Login form

    Email and password with autocomplete hints, a show/hide toggle, keep me signed in, reset and sign-up links. The full auth flow is in Flightdeck.

    40% of the 2,073 sites in The UI Index have a form (821 sites).

    Sign in to Northwind

    Use the email you signed up with.

    Forgot it?

    New here? Create an account

    Both previews render the same markup; the HTML tab below is that markup without a framework.

    Install

    React
    npx shadcn@latest add https://www.themes.dev/ui/r/login-form.json
    Astro
    npx shadcn@latest add https://www.themes.dev/ui/r/astro/login-form.json

    Or copy the files below into your project. They need clsx, tailwind-merge and the components they import, and the themes.dev UI theme. The HTML needs only the compiled CSS and the script.

    login-form.classes.ts
    /** A simple sign-in form (free; the full auth flow with sign-up, reset and two-factor is part of Flightdeck): a
     *  heading, email and password with the right autocomplete hints, a show/hide toggle for the password, "Keep me
     *  signed in", one primary button, and links to reset the password and to sign up. Native validation. */
    export const loginForm = {
      root: 'mx-auto w-full max-w-sm',
      card: 'rounded-xl border bg-card p-6 text-card-foreground shadow-sm sm:p-8',
      title: 'text-xl font-semibold tracking-[-0.02em]',
      description: 'mt-1 text-sm text-pretty text-muted-foreground',
      form: 'mt-6 grid gap-5',
      field: 'grid gap-2',
      labelRow: 'flex items-baseline justify-between gap-3',
      forgot: '-mx-1 inline-flex min-h-6 items-center rounded-sm px-1 text-sm text-muted-foreground underline decoration-transparent underline-offset-4 transition-colors hover:text-foreground hover:decoration-foreground active:bg-accent',
      password: 'relative',
      reveal: 'absolute top-1/2 right-1 inline-flex h-7 -translate-y-1/2 cursor-pointer items-center rounded-md px-2 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed',
      submit: 'w-full',
      footer: 'mt-6 text-center text-sm text-muted-foreground',
      signup: '-mx-1 inline-flex min-h-6 items-center rounded-sm px-1 font-medium text-foreground underline decoration-foreground/30 underline-offset-4 hover:decoration-foreground active:bg-accent',
    };
    export type LoginContent = { title: string; description?: string; action: string; forgotHref: string; signupHref: string; id: string };
    login-form.tsx
    import { useState } from 'react';
    import { Button } from '@/components/ui/button';
    import { Checkbox } from '@/components/ui/checkbox';
    import { Input } from '@/components/ui/input';
    import { Label } from '@/components/ui/label';
    import { cn } from '@/lib/utils';
    import { loginForm as c, type LoginContent } from './login-form.classes';
    
    export type LoginFormProps = LoginContent & { className?: string };
    
    /** A simple sign-in form with autocomplete hints, a show/hide password toggle and native validation. */
    export function LoginForm({ title, description, action, forgotHref, signupHref, id, className }: LoginFormProps) {
      const [shown, setShown] = useState(false);
      return (
        <section aria-labelledby={`${id}-title`} className={cn(c.root, className)}>
          <div className={c.card}>
            <h2 id={`${id}-title`} className={c.title}>{title}</h2>
            {description && <p className={c.description}>{description}</p>}
            <form action={action} method="post" className={c.form}>
              <div className={c.field}>
                <Label htmlFor={`${id}-email`}>Email</Label>
                <Input id={`${id}-email`} name="email" type="email" autoComplete="username" required />
              </div>
              <div className={c.field}>
                <div className={c.labelRow}>
                  <Label htmlFor={`${id}-password`}>Password</Label>
                  <a href={forgotHref} className={c.forgot}>Forgot it?</a>
                </div>
                <div className={c.password}>
                  <Input id={`${id}-password`} name="password" type={shown ? 'text' : 'password'} autoComplete="current-password" required className="pr-16" />
                  <button type="button" data-password-toggle="" aria-controls={`${id}-password`} aria-pressed={shown ? 'true' : 'false'} aria-label="Show password" className={c.reveal} onClick={(e) => { e.preventDefault(); setShown(!shown); }}>{shown ? 'Hide' : 'Show'}</button>
                </div>
              </div>
              <Label><Checkbox name="remember" /> Keep me signed in</Label>
              <Button type="submit" className={c.submit}>Sign in</Button>
            </form>
          </div>
          <p className={c.footer}>New here? <a href={signupHref} className={c.signup}>Create an account</a></p>
        </section>
      );
    }