app/login/login-form.tsx (with react-hook-form)
"use client";
import { useForm } from "react-hook-form";
import { Button } from "@/components/ui/button";
import { Field, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { PasswordInput } from "@/components/ui/password-input";
type Values = { email: string; password: string };
export function LoginForm({ onLogin }: { onLogin: (v: Values) => Promise<void> }) {
const { register, handleSubmit, formState } = useForm<Values>();
const { errors, isSubmitting } = formState;
return (
<form onSubmit={handleSubmit(onLogin)}>
<FieldGroup>
<Field data-invalid={!!errors.email}>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" type="email" autoComplete="email" aria-invalid={!!errors.email}
{...register("email", { required: "Enter your email" })} />
<FieldError errors={[errors.email]} />
</Field>
<Field data-invalid={!!errors.password}>
<FieldLabel htmlFor="password">Password</FieldLabel>
{/* register() passes ref, name, onChange and onBlur straight through */}
<PasswordInput id="password" aria-invalid={!!errors.password}
{...register("password", { required: "Enter your password", minLength: { value: 8, message: "At least 8 characters" } })} />
<FieldError errors={[errors.password]} />
</Field>
<Button type="submit" disabled={isSubmitting}>Log in</Button>
</FieldGroup>
</form>
);
}