Skip to main content

Form & Validation Hooks

Hooks for managing form state, multi-step form flows, detail forms with validation, and debounced search/input values.

useMultiStepForm

Manages navigation and progress tracking for multi-step form wizards.

useMultiStepForm(options: UseMultiStepFormOptions): UseMultiStepFormReturn

Options

OptionTypeDefaultDescription
totalStepsnumber--Total number of steps in the form (required)
initialStepnumber1Starting step number
onStepChange(step: number) => void--Callback fired when the step changes
onComplete() => void--Callback fired when goToNext is called on the last step

Return Values

PropertyTypeDescription
currentStepnumberThe current step number (1-based)
isFirstStepbooleanWhether the current step is the first
isLastStepbooleanWhether the current step is the last
completedStepsSet<number>Set of step numbers marked complete
progressnumberCompletion percentage (0-100)
goToNext() => booleanAdvance to next step; returns false if on last step
goToPrevious() => booleanGo to previous step; returns false if on first step
goToStep(step: number) => booleanJump to a specific step; returns false if out of range
markStepAsCompleted(step: number) => voidMark a step as completed
markStepAsIncomplete(step: number) => voidRemove a step from the completed set
reset() => voidReset to initialStep and clear all completed steps
import { useMultiStepForm } from '@/hooks/use-multi-step-form';

function Wizard() {
const {
currentStep,
isFirstStep,
isLastStep,
progress,
goToNext,
goToPrevious,
markStepAsCompleted,
reset,
} = useMultiStepForm({
totalSteps: 4,
onComplete: () => console.log('Form completed!'),
});

const handleNext = () => {
markStepAsCompleted(currentStep);
goToNext();
};

return (
<div>
<ProgressBar value={progress} />
<StepContent step={currentStep} />
{!isFirstStep && <button onClick={goToPrevious}>Back</button>}
<button onClick={handleNext}>
{isLastStep ? 'Submit' : 'Next'}
</button>
</div>
);
}

useDetailForm

A comprehensive hook for the item submission/editing form. Manages form state, link management, tag toggling, step validation, and progress calculation.

useDetailForm(
initialData: Partial<FormData>,
onSubmit: (data: FormData) => void
): UseDetailFormReturn

Parameters

ParameterTypeDescription
initialDataPartial<FormData>Pre-populated form values
onSubmit(data: FormData) => voidHandler called on form submission

FormData Shape

FieldTypeDescription
namestringItem name
linkstringMain website URL (synced with links array)
linksProductLink[]Array of links with id, url, label, type, icon
categorystring | nullSelected category
tagsstring[]Selected tags
descriptionstringItem description
introductionstringShort introduction text

Return Values

PropertyTypeDescription
currentStepnumberCurrent form step (1-4)
formDataFormDataCurrent form state
focusedFieldstring | nullCurrently focused field name
completedFieldsSet<string>Set of fields with values
animatingLinkIdstring | nullLink ID currently animating
handleInputChange(e: ChangeEvent) => voidGeneric input change handler
handleLinkChange(id, field, value) => voidUpdate a specific link's URL or label
addLink() => voidAdd a new secondary link
removeLink(id: string) => voidRemove a link (main link cannot be removed)
handleTagToggle(tag: string) => voidToggle a tag selection
handleSubmit(e: FormEvent) => voidForm submission handler
nextStep / prevStep() => voidStep navigation (validates before advancing)
validateStep(step: number) => booleanCheck if a step's required fields are valid
progressPercentagenumberOverall form completion (0-100)
completedRequiredFieldsnumberCount of filled required fields
requiredFieldsCountnumberTotal number of required fields
setFormDataDispatchDirect state setter for programmatic updates
import { useDetailForm } from '@/hooks/use-detail-form';

function ItemForm({ initialData, onSubmit }) {
const {
formData,
handleInputChange,
handleSubmit,
nextStep,
prevStep,
currentStep,
progressPercentage,
validateStep,
} = useDetailForm(initialData, onSubmit);

return (
<form onSubmit={handleSubmit}>
<ProgressBar value={progressPercentage} />
{currentStep === 1 && (
<input name="name" value={formData.name} onChange={handleInputChange} />
)}
<button disabled={!validateStep(currentStep)} onClick={nextStep}>
Next
</button>
</form>
);
}

useDebounceValue

Debounces any value, delaying updates until input has settled. Useful for search inputs, filter controls, and preventing excessive re-renders.

useDebounceValue<T>(value: T, delay?: number): T
ParameterTypeDefaultDescription
valueT--The value to debounce
delaynumber300Debounce delay in milliseconds

Returns: T -- The debounced value (updates after delay ms of no change).

import { useDebounceValue } from '@/hooks/use-debounced-value';

function SearchInput() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounceValue(query, 500);

useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery);
}, [debouncedQuery]);

return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}

useDebounceSearch

A higher-level debounced search hook that wraps useDebounceValue and adds searching state tracking and an onSearch callback.

useDebounceSearch(props: UseDebounceSearchProps): UseDebounceSearchReturn

Props

PropTypeDefaultDescription
searchValuestring--Current raw search input value
delaynumber300Debounce delay in milliseconds
onSearch(value: string) => void | Promise<void>--Callback invoked with the debounced value

Return Values

PropertyTypeDescription
debouncedValuestringThe debounced search string
isSearchingbooleantrue while waiting for debounce or during async onSearch
clearSearch() => voidReset search state
import { useDebounceSearch } from '@/hooks/use-debounced-search';

function SearchableList() {
const [query, setQuery] = useState('');

const { debouncedValue, isSearching } = useDebounceSearch({
searchValue: query,
delay: 400,
onSearch: async (value) => {
await fetchFilteredItems(value);
},
});

return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
{isSearching && <Spinner />}
</div>
);
}

Summary Table

HookPurposeSource File
useMultiStepFormMulti-step wizard navigation and progressuse-multi-step-form.ts
useDetailFormItem detail form with validation, links, tagsuse-detail-form.ts
useDebounceValueGeneric value debouncinguse-debounced-value.ts
useDebounceSearchDebounced search with loading stateuse-debounced-search.ts