Authentication Form
Login and registration form with validation and optional social callbacks.
Install
One command copies the item's source into your project, where you own and edit it.
npx manteen add @house/authentication-formDependencies
@mantine/core@^9npm@mantine/form@^9npm@mantine/hooks@^9npm@tabler/icons-react@^3npm
Installing this item also installs @house/mantine-ui-license from the registry.
Preview
Usage
A copy-ready example published by the registry author. Imports use the aliases Manteen configures at init.
import { AuthenticationForm } from "@/components/ui/authentication-form";
export function AccountSignIn() { return ( <AuthenticationForm heading="Welcome back" onSubmit={(values, mode) => console.log("submit", values, mode)} onGoogle={() => console.log("continue with google")} onGithub={() => console.log("continue with github")} /> );}Props
Author-documented props, carried verbatim from the registry item.
| Prop | Type | Default | Description |
|---|---|---|---|
initialMode | "login" | "register" | "login" | Sets which mode the form starts in; the internal toggle then switches between login and register. |
heading | string | "Welcome" | Heading text rendered as "{heading}, {mode} with" above the form, e.g. "Welcome, login with". |
initialTerms | boolean | false | Initial checked state of the terms-and-conditions checkbox shown in register mode. Upstream Mantine UI initializes it true, which renders a pre-checked consent box the user never touched; this registry defaults it false. Set it true only if your flow genuinely warrants a pre-checked consent. |
onSubmit | (values: AuthenticationValues, mode: AuthenticationMode) => void | — | Called with the validated form values and current mode when the form is submitted. |
onGoogle | () => void | — | Renders the Google social button and handles its click; the social row and divider are hidden entirely when neither social handler is set. |
onGithub | () => void | — | Renders the GitHub social button and handles its click; the social row and divider are hidden entirely when neither social handler is set. |
...others | PaperProps | — | All other props are forwarded to the underlying Paper. AuthenticationFormProps extends PaperProps, and the spread is applied after the component's own radius="md", p="lg", withBorder defaults, so any of those three can be overridden by the caller. |
* required
Styling
No public Styles API is declared. Installed CSS remains editable because your project owns the source; internal class names are implementation details, not a customization contract.
Source
3 installable files ship with this item.
registry/mantine-ui/authentication-form/authentication-form.tsxcomponent · 4.7 kB · tsx
/** * Adapted from Mantine UI's AuthenticationForm at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. * * Curated deviation from upstream: the terms-and-conditions checkbox defaults to * unchecked here (`initialTerms` prop, default `false`). Upstream initializes it * `true`, which renders a pre-checked consent checkbox with no user action — a * dark pattern this registry does not ship by default. */import { Anchor, Button, Checkbox, Divider, Group, Paper, type PaperProps, PasswordInput, Stack, Text, TextInput,} from "@mantine/core";import { useForm } from "@mantine/form";import { upperFirst, useToggle } from "@mantine/hooks";
import { GithubButton } from "./github-button";import { GoogleButton } from "./google-button";
export type AuthenticationMode = "login" | "register";
export interface AuthenticationValues { email: string; name: string; password: string; terms: boolean;}
export interface AuthenticationFormProps extends PaperProps { initialMode?: AuthenticationMode; heading?: string; /** Initial checked state of the terms-and-conditions checkbox. Defaults to `false` — the box * must never render pre-checked without the consumer opting in explicitly. */ initialTerms?: boolean; onSubmit?: (values: AuthenticationValues, mode: AuthenticationMode) => void; onGoogle?: () => void; onGithub?: () => void;}
export function AuthenticationForm({ initialMode = "login", heading = "Welcome", initialTerms = false, onSubmit, onGoogle, onGithub, ...paperProps}: AuthenticationFormProps) { const alternateMode: AuthenticationMode = initialMode === "login" ? "register" : "login"; const [mode, toggle] = useToggle<AuthenticationMode>([initialMode, alternateMode]); const hasSocialLogin = Boolean(onGoogle || onGithub); const form = useForm<AuthenticationValues>({ initialValues: { email: "", name: "", password: "", terms: initialTerms }, validate: { email: (value) => (/^\S+@\S+$/.test(value) ? null : "Enter a valid email address"), password: (value) => value.length >= 7 ? null : "Password should include at least 7 characters", }, });
return ( <Paper radius="md" p="lg" withBorder {...paperProps}> <Text size="lg" fw={500} c="bright"> {heading}, {mode} with </Text>
{hasSocialLogin && ( <> <Group grow mb="md" mt="md"> {onGoogle && ( <GoogleButton radius="xl" onClick={onGoogle}> Google </GoogleButton> )} {onGithub && ( <GithubButton radius="xl" onClick={onGithub}> GitHub </GithubButton> )} </Group>
<Divider label="Or continue with email" labelPosition="center" my="lg" styles={{ label: { color: "var(--mantine-color-bright)", opacity: 0.85 } }} /> </> )}
<form onSubmit={form.onSubmit((values) => onSubmit?.(values, mode))}> <Stack> {mode === "register" && ( <TextInput label="Name" placeholder="Enter your name" key={form.key("name")} {...form.getInputProps("name")} radius="md" /> )}
<TextInput required label="Email" placeholder="Enter your email" key={form.key("email")} {...form.getInputProps("email")} radius="md" />
<PasswordInput required label="Password" placeholder="Enter your password" key={form.key("password")} {...form.getInputProps("password")} radius="md" />
{mode === "register" && ( <Checkbox label="I accept the terms and conditions" key={form.key("terms")} {...form.getInputProps("terms", { type: "checkbox" })} /> )} </Stack>
<Group justify="space-between" mt="xl"> <Anchor component="button" type="button" c="bright" opacity={0.85} onClick={() => toggle()} size="xs" > {mode === "register" ? "Already have an account? Login" : "Don't have an account? Register"} </Anchor> <Button type="submit" radius="xl" color="blue.9"> {upperFirst(mode)} </Button> </Group> </form> </Paper> );}registry/mantine-ui/authentication-form/google-button.tsxcomponent · 1.8 kB · tsx
/** * Adapted from Mantine UI's AuthenticationForm at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Button, type ButtonProps } from "@mantine/core";import type { ComponentPropsWithoutRef } from "react";
function GoogleIcon(props: ComponentPropsWithoutRef<"svg">) { return ( <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" viewBox="0 0 256 262" style={{ width: 14, height: 14 }} aria-hidden="true" {...props} > <path fill="#4285F4" d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" /> <path fill="#34A853" d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" /> <path fill="#FBBC05" d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782" /> <path fill="#EB4335" d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" /> </svg> );}
export function GoogleButton(props: ButtonProps & ComponentPropsWithoutRef<"button">) { return ( <Button leftSection={<GoogleIcon />} variant="default" styles={{ section: { marginInlineEnd: 8 } }} {...props} /> );}registry/mantine-ui/authentication-form/github-button.tsxcomponent · 620 B · tsx
/** * Adapted from Mantine UI's AuthenticationForm social button at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Button, type ButtonProps } from "@mantine/core";import { IconBrandGithub } from "@tabler/icons-react";import type { ComponentPropsWithoutRef } from "react";
export function GithubButton(props: ButtonProps & ComponentPropsWithoutRef<"button">) { return ( <Button leftSection={<IconBrandGithub size={16} />} variant="default" styles={{ section: { marginInlineEnd: 8 } }} {...props} /> );}Author notes
Adapted from Mantine UI AuthenticationForm at ffbf61c559…; submission and social actions are exposed as callbacks.