This repository contains a custom form component built using React Hook Form (RHF) and Material-UI (MUI).
It allows for form-controlled selection of options via React Hook Form's Controller
component.
To install, you can use npm or yarn or pnpm:
npm install @paratco/rhf-mui-form
yarn add @paratco/rhf-mui-form
pnpm add @paratco/rhf-mui-form
RHFTextField
is a wrapper around MIUI's TextField
component that integrates with React Hook Form.
<RHFTextField
name="firstName"
label="First Name"
control={control} // Optional, if useFormContext is not used
inputDir="ltr"
isReadOnly={true}
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
inputDir | (ltr or rtl ) |
If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field | |
isReadOnly | boolean | False | The isReadOnly prop to make the input read-only |
props | TextFieldProps | Additional props passed to the underlying MUI TextField
|
RHFTextMasked
is a React Hook Form integrated TextField
component that supports input masking via react-imask
.
<RHFTextMasked
name="phoneNumber"
label="Phone Number"
maskOptions={{ mask: "(000) 000-0000" }}
control={control} // Optional, if useFormContext is not used
inputDir="ltr"
isReadOnly={true}
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
maskOptions* | ReactMaskOpts | The options for input masking, as defined by `react-imask | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
inputDir | (ltr or rtl ) |
If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field | |
isReadOnly | boolean | False | The isReadOnly prop to make the input read-only |
props | TextFieldProps | Additional props passed to the underlying MUI TextField
|
RHFAutoComplete
is a reusable autocomplete component integrated with React Hook Form.
- Supports single or multiple option selections based on the
Autocomplete
component.
<RHFAutoComplete
name="category"
label="Category"
options={categoryOptions}
control={control} // Optional, if useFormContext is not used
multiple // Optional, if you want
/>
Types:
export interface SelectOptionBase {
label: string;
value: unknown;
disabled?: boolean;
}
interface OptionItem extends SelectOptionBase {
value: string;
}
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
label* | string | The name of the input | |
options* | OptionItem[ ] | The options to be displayed in the autocomplete dropdown. | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
renderInputProps | TextFieldProps | Additional props passed to the underlying MUI TextField
|
|
props | AutocompleteProps<Value, Multiple, DisableClearable, FreeSolo> |
Additional props passed to the Autocomplete component. |
RHFCheckBox
is a wrapper around MIUI's Checkbox
component that integrates with React Hook Form.
<RHFCheckBox
name="termsAndConditions"
label="I agree to the terms and conditions"
control={control} // Optional, if useFormContext is not used
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
label* | string | The label for the checkbox | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
props | CheckboxProps | Additional props passed to the underlying MUI Checkbox
|
RHFRadioGroup
is a wrapper around MIUI's RadioGroup
component that integrates with React Hook Form.
- It renders a group of radio buttons based on the provided options and manages the form state.
- The
formLabel
prop allows for an optional MUIFormLabel
component to be displayed above the radio buttons.
<RHFRadioGroup
name="gender"
options={[
{ label: "Male", value: "male" },
{ label: "Female", value: "female" },
]}
formLabel={<FormLabel>Gender</FormLabel>} // Optional FormLabel
control={control} // Optional, if useFormContext is not used
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
options* | OptionItem[ ] | An array of options for the radio buttons, each having a label and value. | |
formLabel | ReactElement<typeof FormLabel> |
A FormLabel component to be displayed above the radio buttons | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
props | RadioGroupProps | Additional props passed to the underlying MUI RadioGroup
|
RHFSelect
is a wrapper around MIUI's Select
component that integrates with React Hook Form.
- It supports both single and multiple selections and handles validation and error messages.
- The
options
prop allows for dynamic option generation, including support for disabling options.
<RHFSelect
name="favoriteFruits"
options={[
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Cherry", value: "cherry", disabled: true },
]}
control={control} // Optional, if useFormContext is not used
multiple // if you want
/>
Types:
export interface SelectOptionBase {
label: string;
value: unknown;
disabled?: boolean;
}
interface OptionItem extends SelectOptionBase {
/** The value of the option, which is used as the key */
value: string; // Different between RHFSelect and RHFSelectPro
}
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
options* | OptionItem[ ] | An array of option items to be displayed in the select dropdown | |
inputDir | (ltr or rtl ) |
If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
maxHeight | number | The maximum height of the select input | |
dropDownMaxHeight | number | The maximum height of the dropdown menu | |
props | SelectProps | Additional props passed to the underlying MUI Select
|
RHFSelectPro
is a wrapper around MIUI's Select
component that integrates with React Hook Form.
<RHFSelectPro
name="favoriteFruits"
options={[
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
{ label: "Cherry", value: "cherry", disabled: true },
]}
control={control} // Optional, if useFormContext is not used
multiple // if you want
/>
Types:
export type NotUndefined = object | string | number | boolean | null | NotUndefined[];
export interface SelectOptionBase {
label: string;
value: unknown;
disabled?: boolean;
}
interface OptionItem extends SelectOptionBase {
/** The value of the option, which can be any type */
value: NotUndefined; // Different between RHFSelect and RHFSelectPro
}
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
options* | OptionItem[ ] | An array of option items to be displayed in the select dropdown | |
inputDir | (ltr or rtl ) |
If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
maxHeight | number | The maximum height of the select input | |
dropDownMaxHeight | number | The maximum height of the dropdown menu | |
props | SelectProps | Additional props passed to the underlying MUI Select
|
RHFSwitch
is a wrapper around MIUI's Switch
component that integrates with React Hook Form.
<RHFSwitch
name="notifications"
label="Enable Notifications"
control={control} // Optional, if useFormContext is not used
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
label* | string | The label that will be displayed alongside the switch | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
props | SwitchProps | Additional props to pass down to the Switch component |
RHFDatePickerJalali
is a date picker component integrated with React Hook Form
<RHFDatePickerJalali
name="birthDate"
label="Birth Date"
control={control} // Optional if useFormContext is used
isReadOnly={false}
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
isReadOnly | boolean | False | The isReadOnly prop to make the input read-only |
props | DatePickerProps<Date> |
Additional props passed to the underlying MUI DatePicker
|
RHFDateTimePickerJalali
is a date and time picker component integrated with React Hook Form.
<RHFDateTimePickerJalali
name="appointmentTime"
label="Appointment Time"
control={control} // Optional if useFormContext is used
isReadOnly={false}
/>
Prop | Type | Default | Definition |
---|---|---|---|
name* | Path<T> |
The name of the input | |
control | Control |
The control object from React Hook Form, optional if useFormContext is used | |
isReadOnly | boolean | False | The isReadOnly prop to make the input read-only |
props | DateTimePickerProps<Date> |
Additional props passed to the underlying MUI DateTimePicker
|
- It allows for customization of the time view using the
renderTimeViewClock
. - It uses
AdapterDateFnsJalali
for Jalali (Persian) calendar support.
Here is an example that demonstrates how to use all components in a controlled manner. In this example, MUI is used throughout.
Additionally, Zod is used for validation.
import { Stack, Button, FormLabel } from "@mui/material";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import type { SubmitHandler } from "react-hook-form";
import { useForm } from "react-hook-form";
import {
RHFAutoComplete,
RHFCheckBox,
RHFDatePickerJalali,
RHFDateTimePickerJalali,
RHFRadioGroup,
RHFSelect,
RHFSelectPro,
RHFSwitch,
RHFTextField,
RHFTextMasked
} from "@paratco/rhf-mui-form";
const schema = z.object({
movies: z.string().nullable(),
isMan: z.boolean(),
// If you don't want to give a specific default value for the date, you should use nullable here and handle it inside refine with superRefine
birthDate: z.date().nullable(),
startDate: z.date().nullable(),
fav: z.string(),
uni: z.string(),
selects: z.array(z.boolean()),
turn: z.boolean(),
name: z.string(),
phoneNumber: z.string()
});
type SchemaType = z.infer<typeof schema>;
export default function Home() {
const { control, handleSubmit } = useForm<SchemaType>({
resolver: zodResolver(schema),
defaultValues: {
movies: null, // Autocomplete
isMan: false, // checkbox
birthDate: null, // date picker
startDate: null, // date time
fav: "", // radio group
uni: "", // select
selects: [], // multi select
turn: false, // switch
name: "", // RHFText
phoneNumber: "" // Mask
}
});
const onSubmit: SubmitHandler<SchemaType> = (data: SchemaType) => {
console.log(data);
};
return (
<Stack sx={{ flexGrow: 1, width: 1 }}>
<Stack margin={2} gap={2} component="form" onSubmit={(event) => void handleSubmit(onSubmit)(event)}>
<RHFAutoComplete<SchemaType>
control={control}
name="movies"
key="movies"
label="Movies"
options={[
{ label: "Home Alone", value: "homeAlone" },
{ label: "Inception", value: "inception" }
]}
/>
<RHFCheckBox<SchemaType> name="isMan" control={control} label="Are you man?" key="isMan" />
<RHFDatePickerJalali<SchemaType>
name="birthDate"
control={control}
key="birthDate"
label="Birth Date"
// maxDate={new Date()}
// minDate={new Date()}
/>
<RHFDateTimePickerJalali<SchemaType> name="startDate" key="startDate" control={control} label="Start Date" />
<RHFRadioGroup<SchemaType>
name="fav"
key="fav"
control={control}
formLabel={<FormLabel sx={{ fontSize: "14px" }}>Favorite</FormLabel>}
options={[
{ label: "spring", value: "spring" },
{ label: "fall", value: "fall" }
]}
/>
<RHFSelect<SchemaType>
name="uni"
key="uni"
control={control}
label="University"
options={[
{ label: "UCL", value: "ucl" },
{ label: "milan", value: "milan" }
]}
/>
<RHFSelectPro<SchemaType>
name="selects"
multiple={true}
control={control}
label="Selects"
key="selects"
options={[
{ label: "No1", value: true },
{ label: "No2", value: false }
]}
/>
<RHFSwitch<SchemaType> name="turn" key="turn" control={control} label="Turn on?" />
<RHFTextField<SchemaType> name="name" control={control} label="Name" key="name" />
<RHFTextMasked<SchemaType>
name="phoneNumber"
control={control}
label="phoneNumber"
key="phoneNumber"
maskOptions={{ mask: "(000) 000-0000" }}
/>
<Button type="submit">submit</Button>
</Stack>
</Stack>
);
}