Piplup
@piplup/rhf-adaptersmui-materialHooks

useMuiButtonAdapter

Returns props for an MUI Button that disables itself from React Hook Form state and resets the form when its type is "reset".

useMuiButtonAdapter reads React Hook Form's form state and returns props you spread onto MUI's Button: a computed disabled, a click handler that resets the form for type="reset", and a className and style that can react to the form state. It's what MuiButtonElement uses internally.

Use the element for ordinary submit and reset buttons. Reach for the hook when the button's content depends on that state (a "Saving…" label), when you want classes or style driven by the form state (the element turns class composition off), or when you build a shared button for a design system. useMuiFabAdapter and useMuiIconButtonAdapter work the same way.

Import

import { useMuiButtonAdapter } from "@piplup/rhf-adapters/mui-material";

Usage

SaveButton is disabled and reads "Saving…" while the submit handler runs. DiscardButton is a reset button that asks for confirmation first, and cancels the reset if you say no.

"use client";import Button from "@mui/material/Button";import Stack from "@mui/material/Stack";import {  MuiTextFieldElement,  useMuiButtonAdapter,} from "@piplup/rhf-adapters/mui-material";import { type Control, useForm } from "react-hook-form";type FormValues = { displayName: string; bio: string };// Disabled while the submit handler runs. Because disableOnIsSubmitting is// the only rule that can disable it, `disabled` also tells us we're saving.function SaveButton({ control }: { control: Control<FormValues> }) {  // Button has no error or helperText props, so keep them off the DOM.  const {    error: _error,    helperText: _helperText,    ...adapter  } = useMuiButtonAdapter<FormValues, HTMLButtonElement>({    control,    type: "submit",    disableOnIsSubmitting: true,  });  return (    <Button variant="contained" {...adapter}>      {adapter.disabled ? "Saving…" : "Save"}    </Button>  );}// Resets the form to its default values, but only after the user confirms.// Calling preventDefault() in onClick stops the reset.function DiscardButton({ control }: { control: Control<FormValues> }) {  const {    error: _error,    helperText: _helperText,    ...adapter  } = useMuiButtonAdapter<FormValues, HTMLButtonElement>({    control,    type: "reset",    disableOnIsSubmitting: true,    onClick(event) {      if (!window.confirm("Discard your changes?")) {        event.preventDefault();      }    },  });  return (    <Button variant="outlined" {...adapter}>      Discard changes    </Button>  );}export default function Page() {  const { control, handleSubmit } = useForm<FormValues>({    defaultValues: { displayName: "Ada", bio: "" },  });  return (    <form      noValidate      onSubmit={handleSubmit(async (values) => {        // Simulate a slow request so the disabled state is visible.        await new Promise((resolve) => setTimeout(resolve, 1500));        alert(JSON.stringify(values, null, 2));      })}    >      <Stack direction="column" spacing={2}>        <MuiTextFieldElement          control={control}          name="displayName"          label="Display name"          required        />        <MuiTextFieldElement          control={control}          name="bio"          label="Bio"          multiline          minRows={2}        />        <Stack direction="row" spacing={1}>          <SaveButton control={control} />          <DiscardButton control={control} />        </Stack>      </Stack>    </form>  );}

Pass control, or render the button inside a FormProvider or FormContainer so the hook can find the form.

How it works

The button isn't a field. It doesn't register anything with React Hook Form; it subscribes to the form state (errors, isSubmitting, isValid) and derives its props from it.

Disabling. The returned disabled is true when any of these apply:

PropDisables the button when
disabledAlways.
disableOnIsSubmittingThe form is submitting, which includes waiting for an async submit handler.
disableOnErrorThe button is in the error state (see below).

Error state. The returned error is your error prop if you pass one. Otherwise it's true whenever the form's isValid is false. That's the whole form, not one field: name and exact narrow which updates re-render the button, but isValid still covers every registered field.

Click and reset. The returned onClick calls your onClick first. Then, if type is "reset" and your handler didn't call event.preventDefault(), it calls React Hook Form's reset() with no arguments. That restores the form's default values, using any resetOptions you gave useForm. The demo uses preventDefault() to cancel the reset when the user doesn't confirm.

type and name. type is passed through. name is returned for the DOM only when it's a string. An array of field names is only used to scope the form state subscription.

Class names and styles. The hook builds className from classes.root, plus classes.disabled and classes.error while those states are active, plus your className. Set composeClassName: false to get your className back unchanged, as the element does. style can be a function that receives { disabled, error } (see the Fab demo for an example). The hook also returns your classes object as-is, so it reaches the component's own classes prop too.

Helper text. The hook also returns helperText. With composeHelperText: true, the error parser receives the form's errors object rather than one field's error (filtered to the listed keys when name is an array). The default parser only reads a top-level message, so pass your own errorParser if you want to show anything from it. A parser set with FormErrorProvider takes priority over the errorParser prop.

MUI's Button has no error or helperText props. Remove them from the returned object before spreading it onto the button, as the element and the demo do, so they don't end up on the DOM element.

isValid is checked as soon as the form mounts and includes fields the user hasn't touched. With disableOnError, a form that starts with an empty required field has a disabled submit button, so the user can't submit to see which fields need attention. Use a validation mode such as "onChange" or "onTouched" so errors show as the user types, or skip disableOnError and signal the state another way.

Props

Prop

Type

Return value

Prop

Type

On this page