Login form example

This is a simple login form:

  • If you enter an invalid email address or password you will see inline client-side validation errors.
  • If you enter an email address that doesn't end with @functn.com you will see an error message from the server.

Sign in

login-form.tsx

'use client';
 
import { RiLockPasswordLine, RiUser3Line } from '@remixicon/react';
import { redirect } from 'next/navigation';
import { Button } from '@/ui/components/button/button';
import { Checkbox } from '@/ui/components/forms/checkbox';
import { Form } from '@/ui/components/forms/form';
import { useForm } from '@/ui/components/forms/hooks/use-form';
import { TextField } from '@/ui/components/forms/text-field';
import {
  FormErrors,
  ServerError,
  validators,
} from '@/ui/components/forms/validation';
import { loginAction } from './action';
 
export const LoginForm = () => {
  const form = useForm<{
    email: string;
    password: string;
    rememberMe: string;
  }>({
    onSubmit: async ({ values }) => {
      await loginAction(values);
      redirect('/dashboard');
    },
  });
 
  return (
    <FormRoot
      context={form}
      className="max-w-lg rounded-lg border-2 border-gray-200 p-4 shadow-xl"
    >
      <h2 id="login-form-heading" className="text-3xl font-bold">
        Sign in
      </h2>
      <FormErrors>
        <ServerError title="Could not sign in" />
      </FormErrors>
      <Form
        className="flex flex-col gap-4"
        aria-labelledby="login-form-heading"
      >
        <TextField
          id="email"
          name="email"
          label="Email"
          type="email"
          autoComplete="email"
          icon={<RiUser3Line size={18} />}
          validate={(value) => {
            if (!value) {
              return 'Please enter your email address';
            }
 
            if (!validators.email(value)) {
              return 'Please enter a valid email address';
            }
          }}
          isRequired
        />
        <TextField
          id="password"
          name="password"
          label="Password"
          type="password"
          icon={<RiLockPasswordLine size={18} />}
          autoComplete="current-password"
          validate={(value) => {
            if (!value) {
              return 'Please enter your password';
            }
          }}
          isRequired
        />
        <Checkbox
          id="rememberMe"
          name="rememberMe"
          label="Remember me for 30 days"
        />
        <Button type="submit" loading={form.isPending}>
          Sign in
        </Button>
      </Form>
    </FormRoot>
  );
};