LoginForm
A ready-made email/password sign-in form with 'remember me', a forgot-password link, and client-side validation — emits `submit` with the collected values instead of calling an API itself.
<UiLoginForm />Options
One block per prop — what it does, and what it looks like set.
Loading
loading disables every field and swaps the submit button's label and icon for a spinner, for while the request is in flight.
loading disables every field and the submit button but doesn't touch the two links — a user can still navigate away mid-submit.
Error
error is shown above the fields — where a failed sign-in belongs, since it's about the attempt rather than about any one input.
Purely presentational — it validates the fields client-side and emits submit with the raw values, but never calls an API itself. Wire @submit to your own auth request, and set loading/error from its result.
Color
color applies to the submit button and the links, so the whole form picks up one accent.
Title
title is the heading, for a product that words its sign-in screen its own way. An empty string drops it — right when the page or modal around the form already has a heading of its own. Everything else the form says is translated through a locale file rather than passed in; see the block below.
Description
description is the line under the heading. Same defaulting, and an empty string drops it too — leaving just the heading.
Signup to
signupTo is where 'Sign up' points, /signup by default. Set it to an empty string to drop that whole line — right for an app where accounts are provisioned rather than self-served, and better than linking to a page that doesn't exist.
Forgot password to
forgotPasswordTo does the same for the 'Forgot password?' link — repoint it at your own route, or empty it out until that page exists.
Providers
providers adds social sign-in under the submit button — a labelled rule, then one button per provider, rendered by SocialLogin. Clicking one emits provider with its id and nothing else happens: the OAuth flow is yours to start. Leave it out and the form is email/password only, exactly as before.
The provider buttons sit *below* the submit button, under a labelled rule — email/password stays the primary path. They're rendered by SocialLogin; reach for that component directly if you need its label, divider, variant or per-provider loading on a screen of your own.
loading disables the provider buttons too, but never spins one — the form has no way to know which provider a redirect belongs to. Use SocialLogin directly, with its loading="google", if you want that spinner.
Provider layout
providerLayout picks between the full-width labelled buttons above (stacked) and one icon-only button per provider on a single row (inline) — worth the switch once you're offering four or more.
Examples
The component in real use: compositions that answer "how do I build X with this", rather than one prop at a time.
Translating the rest — a locale, not eighteen props
Every other string the form renders — labels, placeholders, the submit verb, the validation messages — comes from the locale handed to App. The six bundled languages already cover them, so switching the form's language is one prop on one component, not eighteen threaded through every usage. See [Internationalization](/i18n).
Basic — client-side validated, emits `submit` with `{ email, password, remember }`
Validation only runs when the form is actually submitted (pressing Enter or clicking 'Sign in') — there's no live per-keystroke validation like PasswordInput's requirements checklist.
The 'Forgot password?' and 'Sign up' links point to /forgot-password and /signup respectively — plain hardcoded routes, not props, since this is a ready-made form rather than a fully generic building block. Add matching pages/routes in your app, or copy the component and change the links if yours differ.
This form doesn't call an API
LoginForm only validates its fields and emits submit with the collected values — set loading/error yourself from whatever your own request does with them, the same way the basic example above just toasts what was submitted.
API Reference
Generated from the component's source — props, slots and emits as the component actually declares them.
Props
booleanfalse"neutral" | "primary" | "secondary" | "success" | "error" | "warning" | "info" | "pending"'primary'stringstring'/signup'string'/forgot-password'(SocialProviderId | SocialProvider)[][]"stacked" | "inline"'stacked'Emits
submit[payload: { email: string; password: string; remember: boolean }]provider[id: string]Types
type Color = "neutral" | "primary" | "secondary" | "success" | "error" | "warning" | "info" | "pending";interface SocialProvider {
/**
* Identifier emitted by `select` — use it in your handler to pick which OAuth flow to start.
* Matching a `SocialProviderId` also supplies the brand name and logo for free.
*/
id: string;
/**
* The provider's brand name, rendered after the `label` verb ('Continue with **Google**').
* Defaults to the brand name for a built-in id, or the id itself, title-cased.
*/
name?: string;
/**
* URL of the provider's logo, rendered as an `<img>`. Defaults to the bundled brand mark for a
* built-in id.
*/
logo?: string;
/**
* A bundled icon name to render instead of a logo image — for a provider with no brand mark of
* its own (SAML, a magic link, your own SSO).
*/
icon?: string;
}type SocialProviderId = "google" | "apple" | "microsoft" | "github" | "gitlab" | "facebook" | "discord" | "x" | "twitter" | "instagram";