Skip to content
stroid
Docsapiuse form store
Docs for v0.0.2 - current npm release. Reduce your code, reduce your stress - never your functionality.

useFormStore

A specialized React hook for form state management with built-in validation, dirty tracking, and async submission handling.

Signature

function useFormStore<T extends Record<string, any>>(
  initialValues: T,
  options?: FormStoreOptions<T>
): FormStore<T>

Parameters

ParameterTypeDescription
initialValuesTObject with initial form field values.
options.validatePartial<Record<keyof T, (value) => string | null>>Per-field validation functions. Return null for valid, string for error message.
options.validateOn'blur' | 'change' | 'submit'When to run validation. Default: 'blur'.

Return Value: FormStore<T>

Property / MethodTypeDescription
valuesTCurrent form values.
field(name)(name: keyof T) => InputPropsReturns { value, onChange, onBlur, name } for binding to inputs.
error(name)(name: keyof T) => string | nullReturns the validation error for a field, or null.
errorsPartial<Record<keyof T, string>>All current validation errors.
isValidbooleanTrue when all fields pass validation.
isDirty(name?)(name?: keyof T) => booleanWhether a specific field (or any field) has been modified.
isSubmittingbooleanTrue while an async submit handler is in progress.
handleSubmit(fn)(fn: (values: T) => void | Promise<void>) => FormEventHandlerReturns a form onSubmit handler that validates and calls fn.
reset()() => voidReset all fields to initial values.
resetField(name)(name: keyof T) => voidReset a single field to its initial value.
setFieldValue(name, value)(name: keyof T, value: T[keyof T]) => voidProgrammatically set a field value.

Complete Example

ContactForm.tsx
import { useFormStore } from 'stroid'

function ContactForm() {
  const form = useFormStore({
    name: '',
    email: '',
    message: '',
  }, {
    validate: {
      name: (v) => v.trim().length < 2 ? 'Name is required' : null,
      email: (v) => !/^[^\s@]+@[^\s@]+$/.test(v) ? 'Invalid email' : null,
      message: (v) => v.trim().length < 10 ? 'Message too short' : null,
    },
    validateOn: 'blur',
  })

  const onSubmit = async (values: typeof form.values) => {
    await fetch('/api/contact', {
      method: 'POST',
      body: JSON.stringify(values),
    })
    form.reset()
  }

  return (
    <form onSubmit={form.handleSubmit(onSubmit)}>
      <div>
        <input {...form.field('name')} placeholder="Name" />
        {form.error('name') && <span>{form.error('name')}</span>}
      </div>
      
      <div>
        <input {...form.field('email')} placeholder="Email" />
        {form.error('email') && <span>{form.error('email')}</span>}
      </div>
      
      <div>
        <textarea {...form.field('message')} placeholder="Message" />
        {form.error('message') && <span>{form.error('message')}</span>}
      </div>
      
      <button type="submit" disabled={!form.isValid || form.isSubmitting}>
        {form.isSubmitting ? 'Sending...' : 'Send Message'}
      </button>
    </form>
  )
}