Form
is a hook that provides a powerful yet simple way to handle form state, validation, and submission. It offers a declarative approach to form management with built-in validation, error handling, and state management.
Inspired by TanStack Form, it provides a flexible API that can handle complex form scenarios while maintaining simplicity.
accepts a configuration object and returns a that provides:
- Field: Component for managing individual form fields
- Subscribe: Component for subscribing to form state changes
- handleSubmit: Function to handle form submission
- reset: Function to reset the form to initial values
- getFieldState: Function to get the state of a specific field
import { useForm } from "kiru/form"
type ContactFormData = {
name: string
email: string
}
function ContactForm() {
const form = useForm<ContactFormData>({
initialValues: {
name: "",
email: "",
},
onSubmit: ({ state }) => {
console.log("Form submitted:", state)
},
})
return (
<form
onsubmit={(e) => {
e.preventDefault()
form.handleSubmit()
}}
>
<form.Field
name="name"
children={(field) => (
<div>
<label>Name</label>
<input
value={field.state.value}
onchange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
/>
<form.Field
name="email"
children={(field) => (
<div>
<label>Email</label>
<input
type="email"
value={field.state.value}
onchange={(e) => field.handleChange(e.target.value)}
/>
</div>
)}
/>
<button type="submit">Submit</button>
</form>
)
}Form fields can be validated using the validators prop. Validation can be
triggered on different events like onChange, onBlur, or onSubmit. The
validator functions return error messages when validation fails.
import { useForm } from "kiru/form"
type LoginFormData = {
email: string
password: string
}
function LoginForm() {
const form = useForm<LoginFormData>({
initialValues: {
email: "",
password: "",
},
onSubmit: ({ state }) => {
console.log("Login attempt:", state)
},
})
return (
<form
onsubmit={(e) => {
e.preventDefault()
form.handleSubmit()
}}
>
<form.Field
name="email"
validators={{
onChange: ({ value }) => {
if (!value) return "Email is required"
if (!/\S+@\S+\.\S+/.test(value)) return "Invalid email format"
},
}}
children={(field) => (
<div>
<label>Email</label>
<input
type="email"
value={field.state.value}
onchange={(e) => field.handleChange(e.target.value)}
onblur={field.handleBlur}
/>
{field.state.errors.length > 0 && (
<span style={{ color: "red" }}>{field.state.errors[0]}</span>
)}
</div>
)}
/>
<form.Field
name="password"
validators={{
onChange: ({ value }) => {
if (!value) return "Password is required"
if (value.length < 6)
return "Password must be at least 6 characters"
},
}}
children={(field) => (
<div>
<label>Password</label>
<input
type="password"
value={field.state.value}
onchange={(e) => field.handleChange(e.target.value)}
onblur={field.handleBlur}
/>
{field.state.errors.length > 0 && (
<span style={{ color: "red" }}>{field.state.errors[0]}</span>
)}
</div>
)}
/>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting] as const}
children={([canSubmit, isSubmitting]) => (
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? "Logging in..." : "Login"}
</button>
)}
/>
</form>
)
}Field Component
The Field component manages individual form fields. It provides the field state
and handlers to the render function:
- state.value: Current field value
- state.errors: Array of validation errors
- handleChange: Function to update field value
- handleBlur: Function to handle field blur events
Subscribe Component
The Subscribe component allows you to subscribe to specific parts of the form
state using a selector function. This is useful for rendering buttons or other
UI elements based on form state.