MUI v9 · React Hook Form · TypeScript

mui-rhf-library

Forms that feel like Material UI — controlled by React Hook Form

A focused set of controllers and a FormFields helper so you ship validated, accessible inputs faster — without re-wiring RHF on every project.

Why use it

Less boilerplate between MUI and React Hook Form

Ready-made controllers

Text fields, selects, autocomplete, checkboxes, switches, radio groups, and date pickers — each aligned with MUI props and RHF’s control.

🧩

Declarative FormFields

Describe fields as data — names, types, grid layout, and props — and render a whole form region from one component.

Works with your stack

Pair with Yup or Zod resolvers, Emotion, and MUI’s layout primitives. MIT licensed and built for real apps.

Installation

Install from npm alongside your MUI, Emotion, and React Hook Form peer dependencies.

pnpm

pnpm add mui-rhf-library

npm

npm install mui-rhf-library

yarn

yarn add mui-rhf-library

What’s included

Controllers and FormFields types

Everything maps to familiar MUI components and React Hook Form’s control object.

Controllers

Import and place next to your form logic.

TextFieldController
MUI TextField bound to RHF—text, email, password, multiline, and standard TextField props.
SelectController
MUI Select with option lists, configurable value/label keys, loading indicator, and helper text.
AutocompleteController
Autocomplete with options, plus textFieldProps for the inner TextField.
CheckboxController
Checkbox with label, default checked state, and optional blur handler.
SwitchController
MUI Switch with label and optional custom change handling.
RadioGroupController
Radio group from an options array with label and helper text.
DatePickerController
MUI X DatePicker integrated with RHF for date values.
FormFields
Renders multiple inputs from one fields array (see fieldType column).

fieldType values

Used in each entry of the FormFields fields array.

textField
Renders a TextFieldController row.
select
Renders a SelectController row.
autocomplete
Renders an AutocompleteController row.
checkbox
Renders a CheckboxController row.
radioGroup
Renders a RadioGroupController row.
switch
Renders a SwitchController row.
datePicker
Renders a DatePickerController row.
custom
Supply your own component for layouts or inputs the built-ins don’t cover.

vs wiring MUI + React Hook Form yourself

Doing it by hand means repeating Controller wrappers, default values, error messages, and ref forwarding for every MUI input variant. mui-rhf-library centralizes those patterns so you keep using MUI’s documented props and RHF’s control—without rewriting the same glue for each new form.

Examples

Controllers or data-driven FormFields

Drop-in MUI controllers, or describe fields in an array and render them with FormFields.

Individual controllers

App.tsx
import { TextFieldController, SelectController } from 'mui-rhf-library';
import { useForm } from 'react-hook-form';

type ExampleFormValues = {
  name: string;
  role: string;
};

export function Example() {
  const { control } = useForm<ExampleFormValues>({
    defaultValues: { name: '', role: '' },
  });

  return (
    <>
      <TextFieldController
        control={control}
        name="name"
        defaultValue=""
        label="Full name"
      />
      <SelectController
        name="role"
        label="Role"
        control={control}
        options={[
          { label: 'Admin', value: 'admin' },
          { label: 'Editor', value: 'editor' },
        ]}
      />
    </>
  );
}

FormFields from a config array

SignupForm.tsx
import { FormFields, type FieldProps } from 'mui-rhf-library';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import Grid from '@mui/material/Grid';
import * as yup from 'yup';

type SignupFormValues = {
  firstName: string;
  lastName: string;
  acceptTerms: boolean;
};

const signupSchema = yup
  .object({
    firstName: yup.string().required('First name is required'),
    lastName: yup.string().required('Last name is required'),
    acceptTerms: yup
      .boolean()
      .oneOf([true], 'You must accept the terms'),
  })
  .required();

export function SignupForm() {
  const { control, handleSubmit } = useForm<SignupFormValues>({
    defaultValues: { firstName: '', lastName: '', acceptTerms: false },
    resolver: yupResolver(signupSchema),
  });

  const fields: FieldProps[] = [
    {
      fieldType: 'textField',
      name: 'firstName',
      label: 'First name',
      props: { fullWidth: true },
      gridProps: { size: { xs: 12, sm: 6 } },
    },
    {
      fieldType: 'textField',
      name: 'lastName',
      label: 'Last name',
      props: { fullWidth: true },
      gridProps: { size: { xs: 12, sm: 6 } },
    },
    {
      fieldType: 'checkbox',
      name: 'acceptTerms',
      label: 'I accept the terms',
      gridProps: { size: { xs: 12 } },
    },
  ];

  return (
    <form
      onSubmit={handleSubmit((data: SignupFormValues) => {
        console.log(data);
      })}
    >
      <Grid container spacing={2}>
        <FormFields fields={fields} control={control} />
      </Grid>
      <button type="submit">Submit</button>
    </form>
  );
}

Full API tables and migration notes live in the repository README .