8
Hooks

useForm

A versatile hook for managing form state, validation, and submission with a clean API.

useForm

The useForm hook provides a comprehensive solution for handling form state, validation, error handling, and submission in React applications. It’s designed to reduce boilerplate while maintaining flexibility.

Login Form

Installation

Install the useForm hook using:

npx axionjs-ui add hook use-form

File Structure

use-form.ts

API

Options

PropTypeDefault
initialValues
T (generic object type with field values)
Required
onSubmit
(values: T) => Promise<void> | void
Required
validate
(values: T) => Partial<Record<keyof T, string>>
undefined

Form State

PropTypeDefault
values
T (generic object type)
-
errors
Partial<Record<keyof T, string>>
-
touched
Partial<Record<keyof T, boolean>>
-
isSubmitting
boolean
-
isValid
boolean
-

Form Actions

PropTypeDefault
setFieldValue
(field: keyof T, value: any) => void
-
setFieldError
(field: keyof T, error: string) => void
-
setFieldTouched
(field: keyof T, isTouched?: boolean) => void
-
setValues
(values: Partial<T>) => void
-
setErrors
(errors: Partial<Record<keyof T, string>>) => void
-
setTouched
(touched: Partial<Record<keyof T, boolean>>) => void
-
resetForm
() => void
-
validateForm
() => boolean
-
submitForm
() => Promise<void>
-

Examples

Contact Form with Validation

A complete contact form with validation and submission handling.

Contact Us

Form with External Validation Library (Zod)

You can integrate the useForm hook with external validation libraries like Zod.

import { z } from "zod";
import { useForm } from "@/hooks/use-form";
 
// Define form schema with zod
const formSchema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.string().email("Please enter a valid email"),
  age: z.number().min(18, "You must be at least 18 years old"),
});
 
type FormValues = z.infer<typeof formSchema>;
 
function ZodForm() {
  const [formState, formActions] = useForm<FormValues>({
    initialValues: {
      name: "",
      email: "",
      age: 0,
    },
    validate: (values) => {
      try {
        formSchema.parse(values);
        return {}; // No errors
      } catch (error) {
        if (error instanceof z.ZodError) {
          return error.errors.reduce(
            (acc, curr) => {
              const path = curr.path[0] as keyof FormValues;
              acc[path] = curr.message;
              return acc;
            },
            {} as Record<keyof FormValues, string>,
          );
        }
        return {};
      }
    },
    onSubmit: async (values) => {
      // Handle submission
    },
  });
 
  // Render form
}

Use Cases

  • Login/Registration Forms: Manage authentication form state with validation
  • Multi-step Forms: Break complex forms into manageable steps
  • Data Collection Forms: Contact forms, surveys, and questionnaires
  • Search & Filter Forms: Manage search parameters and filters
  • CRUD Operations: Create and edit forms for database records
  • Form Wizards: Guide users through complex form processes
  • Dynamic Forms: Forms with fields that change based on user input

Accessibility

For building accessible forms, consider these practices alongside the useForm hook:

  • Use proper HTML form elements (<form>, <label>, <input>, etc.)
  • Connect labels and inputs with matching for and id attributes
  • Provide clear error messages that are announced to screen readers
  • Use aria-invalid to indicate invalid fields
  • Ensure keyboard navigation works correctly

Best Practices

  • Separate form logic from UI components for better maintainability
  • Validate fields on both change and blur for a good UX balance
  • Show validation errors only after fields have been touched
  • Disable the submit button during submission to prevent double-submits
  • Provide clear feedback during and after form submission
  • Reset the form to its initial state after successful submission
  • Use typed generics for type safety when working with TypeScript

On this page