@ereo/forms
Signal-based form library for React with per-field reactivity. Each field gets its own signal from @ereo/state, so only the components bound to changed fields re-render. Validation rules derive their trigger behavior automatically -- no manual validateOn configuration needed.
Installation
bash
bun add @ereo/forms @ereo/state reactQuick Example
tsx
import { useForm, useField, useFormStatus, required, email } from '@ereo/forms'
function SignupForm() {
const form = useForm({
defaultValues: { email: '', password: '' },
validators: {
email: [required(), email()],
password: [required(), minLength(8)],
},
onSubmit: async (values) => {
await fetch('/api/signup', { method: 'POST', body: JSON.stringify(values) })
},
})
const emailField = useField(form, 'email')
const passwordField = useField(form, 'password')
const { isSubmitting } = useFormStatus(form)
return (
<form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>
<input {...emailField.inputProps} placeholder="Email" />
{emailField.touched && emailField.errors[0] && (
<span>{emailField.errors[0]}</span>
)}
<input {...passwordField.inputProps} type="password" placeholder="Password" />
{passwordField.touched && passwordField.errors[0] && (
<span>{passwordField.errors[0]}</span>
)}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Sign Up'}
</button>
</form>
)
}Features
- Per-field signals -- only the component using a changed field re-renders
- Validation engine -- 20+ built-in validators with derive-don't-configure trigger behavior
- Field arrays -- dynamic add/remove/swap/move for list-based fields
- Wizard / multi-step forms -- step management with per-step validation
- Schema adapters -- Zod, Valibot, and native
ereoSchemaDSL - Server actions -- progressive enhancement with
ActionFormandcreateFormAction - Accessibility -- focus management, ARIA live announcements, error focus
Exports
Hooks
| Export | Description |
|---|---|
useForm | Create a form instance with default values, validators, and submit handler |
useField | Bind a single field -- returns value, errors, touched, and inputProps |
useFieldArray | Manage dynamic arrays (append, remove, swap, move, insert) |
useWatch | Observe field values reactively without registering the field |
useFormStatus | Subscribe to form-level status (submitting, dirty, valid) |
Core
| Export | Description |
|---|---|
FormStore | The underlying store class holding per-field signals |
createFormStore | Factory function to create a FormStore outside React |
createValuesProxy | ES Proxy for natural property access (form.values.user.email) |
Context
| Export | Description |
|---|---|
FormProvider | React context provider for a form instance |
useFormContext | Consume the nearest FormProvider |
Components
| Export | Description |
|---|---|
Field | Declarative field component with render prop |
TextareaField | Pre-built textarea field |
SelectField | Pre-built select field |
FieldArray | Declarative field array component |
Validation
| Export | Description |
|---|---|
required | Required field validator |
email | Email format validator |
url | URL format validator |
date | Date format validator |
phone | Phone number validator |
minLength / maxLength | String length validators |
min / max | Numeric range validators |
pattern | RegExp pattern validator |
number / integer / positive | Numeric type validators |
custom | Custom sync validator |
async | Custom async validator (auto-validates on change with debounce) |
matches | Cross-field equality check |
oneOf / notOneOf | Inclusion / exclusion validators |
fileSize / fileType | File input validators |
compose | Compose multiple validators into one |
when | Conditional validator |
v | Shorthand namespace re-exporting all validators (e.g. v.required(), v.email()) |
Schema Adapters
| Export | Description |
|---|---|
zodAdapter | Adapt a Zod schema for form-level validation |
valibotAdapter | Adapt a Valibot schema for form-level validation |
isStandardSchema | Type guard for Standard Schema V1 (~standard property) |
standardSchemaAdapter | Explicit adapter for Standard Schema V1-compliant validators |
createSchemaValidator | Generic schema adapter factory |
ereoSchema | Native schema DSL (sync safeParse) |
isEreoSchema | Type guard for ereoSchema instances |
formDataToObject | Convert FormData to a typed object |
Wizard
| Export | Description |
|---|---|
createWizard | Create a multi-step wizard instance |
useWizard | Hook for wizard step navigation and state |
WizardProvider | React context provider for a wizard |
useWizardContext | Consume the nearest WizardProvider |
WizardStep | Render the current step's content |
WizardProgress | Step progress indicator component |
WizardNavigation | Next / back / submit navigation buttons |
Server Actions
| Export | Description |
|---|---|
createFormAction | Create a server action handler with validation |
ActionForm | <form> wrapper for progressive enhancement |
useFormAction | Hook to consume server action results |
parseActionResult | Parse an ActionResult from a server response |
Composition
| Export | Description |
|---|---|
mergeFormConfigs | Deep-merge multiple form configurations |
composeSchemas | Combine schemas for multi-section forms |
Accessibility
| Export | Description |
|---|---|
focusFirstError | Focus the first field with a validation error |
focusField | Programmatically focus a field by path |
announce | Push a message to an ARIA live region |
announceErrors | Announce validation errors to screen readers |
announceSubmitStatus | Announce form submission outcome |
Utilities
| Export | Description |
|---|---|
getPath | Read a nested value by dot-path |
setPath | Immutably set a nested value by dot-path |
deepClone | Structured clone with fallback |
deepEqual | Deep equality check |
parsePath | Parse "a.b[0].c" into path segments |
flattenToPaths | Flatten a nested object to dot-path entries |