Skip to content
LuoForge/Tungsten
← All recipes

Login form

Minimal email + password sign-in form built on FormProvider + FormField. Email is required and format-checked; the submit handler only fires onValid when both fields pass.

Preview

* required

Source

apps/docs/app/recipes/login/recipe.tsx
'use client';

import { Button, FormField, FormProvider, Input, useForm } from '@hey-mike/tungsten';

const isEmail = (v: unknown) =>
  typeof v === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) ? undefined : 'Enter a valid email';

function LoginFields() {
  const form = useForm();
  return (
    <form onSubmit={form.handleSubmit} className="flex w-full max-w-sm flex-col gap-4" noValidate>
      <p className="text-ink-3 text-2xs font-mono uppercase tracking-label">
        <span className="text-error-text">*</span> required
      </p>
      <FormField name="email" label="Email" required validate={isEmail}>
        <Input type="email" placeholder="you@example.com" autoComplete="email" />
      </FormField>
      <FormField name="password" label="Password" required validate={(v) => (v ? undefined : 'Required')}>
        <Input type="password" autoComplete="current-password" />
      </FormField>
      <Button type="submit" variant="brand" size="md">
        Sign in
      </Button>
    </form>
  );
}

export default function LoginRecipe() {
  return (
    <FormProvider>
      <LoginFields />
    </FormProvider>
  );
}
'use client';

import { Button, FormField, FormProvider, Input, useForm } from '@hey-mike/tungsten';

const isEmail = (v: unknown) =>
  typeof v === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) ? undefined : 'Enter a valid email';

function LoginFields() {
  const form = useForm();
  return (
    <form onSubmit={form.handleSubmit} className="flex w-full max-w-sm flex-col gap-4" noValidate>
      <p className="text-ink-3 text-2xs font-mono uppercase tracking-label">
        <span className="text-error-text">*</span> required
      </p>
      <FormField name="email" label="Email" required validate={isEmail}>
        <Input type="email" placeholder="you@example.com" autoComplete="email" />
      </FormField>
      <FormField name="password" label="Password" required validate={(v) => (v ? undefined : 'Required')}>
        <Input type="password" autoComplete="current-password" />
      </FormField>
      <Button type="submit" variant="brand" size="md">
        Sign in
      </Button>
    </form>
  );
}

export default function LoginRecipe() {
  return (
    <FormProvider>
      <LoginFields />
    </FormProvider>
  );
}