# LoginForm

> Sign-in form that works alone and, inside an `AuthLayout`, drives its illustration: focusing the identifier or the password, typing, failing and signing in all update the layout's…

Source: https://ui.jcsoftdev.com/docs/components/login-form

Sign-in form that works alone and, inside an `AuthLayout`, drives its illustration: focusing the
identifier or the password, typing, failing and signing in all update the layout's state and
progress, and the selected portal becomes the illustration's active slab.

## Import

```ts
import { LoginForm } from "@jcsoftdev/ui/auth";
```

Single-component entry points tree-shake to just this component: `@jcsoftdev/ui/auth/login-form`.

## Usage

```tsx
<LoginForm portals={[]} />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `onSubmit` | `((values: LoginValues) => void \| Promise<void>)` |  | Called with the values when the form is submitted with both fields filled. A returned promise keeps the form loading; if it resolves the form reads as signed in, if it rejects the message of the error (or the localized default) is shown. Leave it out and driv… |
| `portals` | `readonly LoginPortal[]` | `[]` | Portals the person can sign in through. With two or more, `LoginForm.Portals` renders a picker. |
| `portal` | `string` |  | Selected portal value. Controlled; omit it for an uncontrolled picker. |
| `defaultPortal` | `string` |  |  |
| `onPortalChange` | `((value: string) => void)` |  |  |
| `appearance` | `"card" \| "open"` |  | `card` is the form inside the panel card, labels hidden visually. `open` has no card chrome and shows the labels above the fields with a pill button. Defaults to `open` inside an `AuthLayout panel="solid"` and `card` otherwise. |
| `loading` | `boolean` |  | The form is waiting for the server. Overrides the state `onSubmit` keeps. |
| `error` | `string \| null` |  | Sign-in error to show. `null` clears it. Overrides the state `onSubmit` keeps. |
| `success` | `boolean` |  | The sign-in worked. Overrides the state `onSubmit` keeps. |
| `children` | `ReactNode` |  | Parts to place yourself: `LoginForm.Header`, `.Portals`, `.Error`, `.Identifier`, `.Password`, `.Options`, `.Submit`, `.Providers` and `.Footer`. Without children the form renders them in that order (no `.Options`, `.Providers` or `.Footer`). A field left emp… |
| `classNames` | `Partial<Record<LoginFormPart, string>>` |  | Per-part classes merged with `cn`; consumer classes win. `className` still targets the root and is merged last. |

`*` marks a required prop.

It also accepts the native props of the element or primitive it renders (for example `className`, `id`, `aria-*` and event handlers).

## Storybook stories

- [Standalone](/storybook/?path=/story/auth-loginform--standalone)
- [With Portals](/storybook/?path=/story/auth-loginform--with-portals)
- [Portal Tiles Focus And Check](/storybook/?path=/story/auth-loginform--portal-tiles-focus-and-check)
- [Complete](/storybook/?path=/story/auth-loginform--complete)
- [Missing Identifier](/storybook/?path=/story/auth-loginform--missing-identifier)
- [Wrong Password](/storybook/?path=/story/auth-loginform--wrong-password)
- [Loading](/storybook/?path=/story/auth-loginform--loading)
- [Controlled Error](/storybook/?path=/story/auth-loginform--controlled-error)
- [Inside Auth Layout](/storybook/?path=/story/auth-loginform--inside-auth-layout)
- [Repeated Error](/storybook/?path=/story/auth-loginform--repeated-error)
- [Controlled Outcome Reset](/storybook/?path=/story/auth-loginform--controlled-outcome-reset)
- [Keeps Default Active Index](/storybook/?path=/story/auth-loginform--keeps-default-active-index)
- [Submit Keeps Focus While Loading](/storybook/?path=/story/auth-loginform--submit-keeps-focus-while-loading)

## Source

`packages/ui/src/auth/login-form/login-form.tsx`
