Components
Loading preview...
HeroUI v3 Form — accessible form wrapper with built-in validation, submit/reset handling and a custom render-function API.
npx shadcn@latest add https://21st.dev/r/hero_ui/heroui-form"use client"
import { Check } from "lucide-react"
import {
Button,
Description,
FieldError,
Input,
Label,
TextField,
} from "@heroui/react"
import { Form } from "@/components/ui/heroui-form"
export function Basic() {
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
const data: Record<string, string> = {}
// Convert FormData to plain object
formData.forEach((value, key) => {
data[key] = value.toString()
})
alert(`Form submitted with: ${JSON.stringify(data, null, 2)}`)
}
return (
<Form className="flex w-96 flex-col gap-4" onSubmit={onSubmit}>
<TextField
isRequired
name="email"
type="email"
validate={(value) => {
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "Please enter a valid email address"
}
return null
}}
>
<Label>Email</Label>
<Input placeholder="john@example.com" />
<FieldError />
</TextField>
<TextField
isRequired
minLength={8}
name="password"
type="password"
validate={(value) => {
if (value.length < 8) {
return "Password must be at least 8 characters"
}
if (!/[A-Z]/.test(value)) {
return "Password must contain at least one uppercase letter"
}
if (!/[0-9]/.test(value)) {
return "Password must contain at least one number"
}
return null
}}
>
<Label>Password</Label>
<Input placeholder="Enter your password" />
<Description>
Must be at least 8 characters with 1 uppercase and 1 number
</Description>
<FieldError />
</TextField>
<div className="flex gap-2">
<Button type="submit">
<Check />
Submit
</Button>
<Button type="reset" variant="secondary">
Reset
</Button>
</div>
</Form>
)
}
export default Basic