Skip to main content

Hook Form API Reference

Complete API documentation for all Hook Form functions and methods.

createForm<T>(params)

Creates a type-safe form instance with validation and state management.

Type Parameters:

  • T - Type of form values (must extend Record<string, any>)

Parameters:

interface HookFormParams<T extends Record<string, any>> {
// Initial field values with optional validation rules
defaultValues: {
[K in keyof T]: Field<T[K]> | T[K];
};

// Optional custom validation resolver (Zod, Yup, etc)
resolver?: Resolver<T>;
}

Returns: A function that returns form methods and Controller component

Example:

import { createForm, zodResolver } from 'zustic/hook-form';
import { z } from 'zod';

const schema = z.object({
email: z.string().email(),
password: z.string().min(8)
});

const form = createForm<{ email: string; password: string }>({
defaultValues: {
email: { value: '', required: true },
password: { value: '', required: true }
},
resolver: zodResolver(schema)
});

Form Hook

Call the form function returned by createForm in your component:

const { handleSubmit, Controller, watch, setValue, ... } = form();

Returned Methods

handleSubmit(callback)

Submit handler that validates all fields before calling callback.

const handleSubmit: (
cb: (data: T) => void | Promise<void>
) => (e: React.FormEvent<HTMLFormElement>) => Promise<void>;

Example:

const { handleSubmit } = form();

const onSubmit = (data: LoginForm) => {
console.log('Valid data:', data);
// Make API call, redirect, etc
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* form fields */}
</form>
);

Controller

Component for controlled form inputs with automatic validation.

interface ControllerProps<T extends Record<string, any>> {
// Field name to control
field: keyof T;

// Render function receiving field props
render: (field: {
value: any;
error: string;
onChange: (value: any) => void;
}) => React.ReactNode;
}

Example:

const { Controller } = form();

<Controller
field="email"
render={({ value, error, onChange }) => (
<div>
<input
value={value}
onChange={(e) => onChange(e.target.value)}
type="email"
/>
{error && <span className="error">{error}</span>}
</div>
)}
/>

watch(field)

Get current field value with automatic re-renders.

const watch: (field: keyof T) => T[keyof T];

Example:

const { watch } = form();

const email = watch('email');
const password = watch('password');

return (
<p>
Email: {email}
{password.length > 0 && ' ✓'}
</p>
);

setValue(field, value)

Update field value programmatically.

const setValue: (field: keyof T, value: T[keyof T]) => void;

Example:

const { setValue } = form();

// Pre-fill form
useEffect(() => {
setValue('email', 'user@example.com');
setValue('name', 'John Doe');
}, []);

setFieldValue(field, value)

Update field value with type conversion (string to number, etc).

const setFieldValue: (field: keyof T, value: any) => void;

Example:

const { setFieldValue } = form();

// Auto-converts string to number
setFieldValue('age', '25'); // Converts to number 25

getErrors(field?)

Get all form errors or specific field error.

const getErrors: (field?: keyof T) => 
| Partial<Record<keyof T, string>>
| string;

Example:

const { getErrors } = form();

// Get all errors
const errors = getErrors();
// { email: "Invalid email", password: "Too short" }

// Get specific field error
const emailError = getErrors('email');
// "Invalid email"

setError(field, error)

Manually set error for a field (useful for server-side errors).

const setError: (field: keyof T, error: string) => void;

Example:

const { setError } = form();

try {
await api.login(data);
} catch (error) {
setError('email', 'Email already registered');
}

clearFieldError(field)

Clear error for a specific field.

const clearFieldError: (field: keyof T) => void;

Example:

const { clearFieldError } = form();

clearFieldError('email'); // Removes error

clearAllErrors()

Clear all field errors at once.

const clearAllErrors: () => void;

Example:

const { clearAllErrors } = form();

clearAllErrors(); // Clears all errors

isDirty(field?)

Check if form or specific field has been modified.

const isDirty: (field?: keyof T) => boolean;

Example:

const { isDirty } = form();

// Check if any field changed
if (isDirty()) {
console.log('Form has unsaved changes');
}

// Check specific field
if (isDirty('email')) {
console.log('Email was modified');
}

isTouched(field)

Check if field has been focused/interacted with.

const isTouched: (field: keyof T) => boolean;

Example:

const { isTouched, getErrors } = form();

<Controller
field="email"
render={({ value, error, onChange }) => (
<div>
<input value={value} onChange={(e) => onChange(e.target.value)} />
{/* Only show error if field was touched */}
{isTouched('email') && error && (
<span className="error">{error}</span>
)}
</div>
)}
/>

setTouched(field, touched)

Manually set field touched state.

const setTouched: (field: keyof T, touched: boolean) => void;

Example:

const { setTouched } = form();

// Mark as touched to show errors
setTouched('email', true);

// Mark as not touched to hide errors
setTouched('email', false);

getValues(field?)

Get all form values or specific field value.

const getValues: (field?: keyof T) => T | T[keyof T];

Example:

const { getValues } = form();

// Get all values
const formData = getValues();
// { email: "user@example.com", password: "secret123" }

// Get specific value
const email = getValues('email');
// "user@example.com"

reset()

Reset form to initial values and clear all errors.

const reset: () => void;

Example:

const { reset, handleSubmit } = form();

const onSubmit = async (data: T) => {
await api.submit(data);
reset(); // Clear form after successful submit
};

Validation Rules

Built-In Rules

interface Field<T> {
value: T;
error?: string;
touched?: boolean;
isDirty?: boolean;

// Required validation
required?: boolean | {
value: boolean;
message: string;
};

// Pattern validation (strings only)
pattern?: {
value: RegExp;
message: string;
};

// Minimum value or length
min?: number | {
value: number;
message: string;
};

// Maximum value or length
max?: number | {
value: number;
message: string;
};
}

Example:

const form = createForm({
defaultValues: {
email: {
value: '',
required: { value: true, message: 'Email is required' },
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: 'Invalid email format'
}
},
age: {
value: 0,
min: { value: 18, message: 'Must be 18+' },
max: { value: 120, message: 'Invalid age' }
},
password: {
value: '',
min: { value: 8, message: 'Min 8 characters' }
}
}
});

Custom Resolvers (Zod/Yup)

zodResolver(schema)

Create validation resolver from Zod schema.

import { z } from 'zod';
import { zodResolver } from 'zustic/hook-form';

const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Min 8 characters'),
age: z.number().min(18, 'Must be 18+')
});

const form = createForm({
defaultValues: {
email: '',
password: '',
age: 0
},
resolver: zodResolver(schema)
});

yupResolver(schema)

Create validation resolver from Yup schema.

import * as yup from 'yup';
import { yupResolver } from 'zustic/hook-form';

const schema = yup.object().shape({
email: yup.string().email('Invalid email').required(),
password: yup.string().min(8, 'Min 8 characters').required(),
age: yup.number().min(18, 'Must be 18+').required()
});

const form = createForm({
defaultValues: {
email: '',
password: '',
age: 0
},
resolver: yupResolver(schema)
});

Type Definitions

HookFormParams<T>

Configuration for form creation.

interface HookFormParams<T extends Record<string, any>> {
defaultValues: {
[K in keyof T]: Field<T[K]> | T[K];
};
resolver?: Resolver<T>;
}

Field<T>

Individual field state and configuration.

type Field<T> = {
value: T;
error?: string;
touched?: boolean;
isDirty?: boolean;
required?: RequiredRule;
pattern?: { value: RegExp; message: string };
min?: NumberRule;
max?: NumberRule;
};

FormState<T>

Complete form state with all methods.

type FormState<T> = Record<keyof T, Field<T[keyof T]>> & {
setFieldValue: (field: keyof T, value: any) => void;
setValue: (field: keyof T, value: T[keyof T]) => void;
getErrors: (field?: keyof T) => Record<keyof T, string> | string;
setError: (field: keyof T, error: string) => void;
clearFieldError: (field: keyof T) => void;
clearAllErrors: () => void;
isDirty: (field?: keyof T) => boolean;
isTouched: (field: keyof T) => boolean;
setTouched: (field: keyof T, touched: boolean) => void;
getValues: (field?: keyof T) => T | T[keyof T];
handleSubmit: (cb: (data: T) => void) => (e: React.FormEvent) => Promise<void>;
reset: () => void;
};

Common Patterns

Simple Form

const form = createForm<{ email: string }>({
defaultValues: {
email: { value: '', required: true }
}
});

const { handleSubmit, Controller } = form();

With Zod Schema

const schema = z.object({ email: z.string().email() });

const form = createForm({
defaultValues: { email: '' },
resolver: zodResolver(schema)
});

With Default Values

const form = createForm({
defaultValues: {
email: 'user@example.com',
password: ''
}
});

With Validation Rules

const form = createForm({
defaultValues: {
email: {
value: '',
required: { value: true, message: 'Required' },
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: 'Invalid email'
}
}
}
});

Next Steps