Skip to content

Form Components

Form components provide React Hook Form-integrated input wrappers with built-in validation, error display, and consistent styling across the application.

  • Building forms with validation (group creation, wishlist editing)
  • Need automatic error handling and display
  • Require controlled input components with React Hook Form

Text input with label and error message.

interface RHFTextFieldProps {
name: string; // Form field name (required)
label?: string; // Input label
placeholder?: string; // Placeholder text
type?: 'text' | 'email' | 'url' | 'number' | 'password';
multiline?: boolean; // Textarea mode
rows?: number; // Rows for multiline
disabled?: boolean;
helperText?: string; // Additional hint text
}

Dropdown select input.

interface RHFSelectProps {
name: string;
label?: string;
options: Array<{ value: string | number; label: string }>;
disabled?: boolean;
}

Checkbox with label.

interface RHFCheckboxProps {
name: string;
label: string;
disabled?: boolean;
}

Date picker using MUI DatePicker.

interface RHFDatePickerProps {
name: string;
label?: string;
minDate?: Date;
maxDate?: Date;
disabled?: boolean;
}

File upload with preview.

interface RHFUploadProps {
name: string;
accept?: string; // e.g., 'image/*'
maxSize?: number; // Max file size in bytes
onDrop?: (files: File[]) => void;
}
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { RHFTextField, RHFDatePicker, RHFCheckbox } from 'src/components/hook-form';
const schema = yup.object({
name: yup.string().required('Name is required'),
email: yup.string().email('Invalid email').required('Email is required'),
date: yup.date().min(new Date(), 'Date must be in future').required(),
agree: yup.boolean().oneOf([true], 'You must agree'),
});
function MyForm() {
const { control, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(schema),
defaultValues: {
name: '',
email: '',
date: null,
agree: false,
},
});
const onSubmit = async (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<RHFTextField name="name" label="Name" control={control} />
<RHFTextField name="email" label="Email" type="email" control={control} />
<RHFDatePicker name="date" label="Event Date" control={control} />
<RHFCheckbox name="agree" label="I agree to terms" control={control} />
<button type="submit">Submit</button>
</form>
);
}
<RHFTextField
name="budget"
label="Budget"
type="number"
control={control}
rules={{
required: 'Budget is required',
min: { value: 1, message: 'Must be at least $1' },
max: { value: 1000, message: 'Must be under $1000' },
}}
/>
  • Automatic error display: Errors from formState.errors shown below input
  • Material-UI styling: Uses MUI TextField with theme integration
  • Accessibility: Proper labels, aria-attributes, and error associations
  • Validation timing: On blur and submit by default
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
it('should display error on invalid input', async () => {
render(<MyForm />);
const input = screen.getByLabelText(/email/i);
await userEvent.type(input, 'invalid-email');
await userEvent.tab(); // Trigger blur
expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
});