Piplup
@piplup/rhf-adaptersmui-materialHooks

useMuiOutlinedInputAdapter

Returns adapter props for building custom MUI OutlinedInput fields with React Hook Form, such as a password field with a show/hide toggle.

useMuiOutlinedInputAdapter connects a field to React Hook Form and returns props you spread onto MUI's OutlinedInput. It's what MuiOutlinedInputElement uses internally.

Use it when you build an outlined field from parts, typically because you need adornments with their own state, like a show/hide button on a password, that you want to keep inside one reusable component.

Import

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

Usage

This PasswordField requires 8 to 64 characters and has a button that switches between hidden and visible text. The value and validation state are kept when you toggle.

At least 8 characters

"use client";import { useId, useState } from "react";import Stack from "@mui/material/Stack";import Button from "@mui/material/Button";import FormControl from "@mui/material/FormControl";import FormHelperText from "@mui/material/FormHelperText";import InputAdornment from "@mui/material/InputAdornment";import InputLabel from "@mui/material/InputLabel";import OutlinedInput from "@mui/material/OutlinedInput";import {  MuiButtonElement,  useMuiOutlinedInputAdapter,} from "@piplup/rhf-adapters/mui-material";import { type Control, useForm } from "react-hook-form";type FormValues = { password: string };// A password field with a show/hide toggle. The input type is passed to the// hook, not to OutlinedInput, because the adapter returns its own `type`.function PasswordField({ control }: { control: Control<FormValues> }) {  const id = useId();  const [visible, setVisible] = useState(false);  const { ref, helperText, ...adapter } = useMuiOutlinedInputAdapter({    control,    name: "password",    type: visible ? "text" : "password",    required: true,    minLength: 8,    maxLength: 64,    composeHelperText: true,    helperText: "At least 8 characters",    messages: {      required: "Choose a password",      minLength: (length) => `Use at least ${length} characters`,    },  });  return (    <FormControl variant="outlined" error={adapter.error} required>      <InputLabel htmlFor={id}>Password</InputLabel>      <OutlinedInput        {...adapter}        id={id}        inputRef={ref}        label="Password"        autoComplete="new-password"        endAdornment={          <InputAdornment position="end">            <Button size="small" onClick={() => setVisible((v) => !v)}>              {visible ? "Hide" : "Show"}            </Button>          </InputAdornment>        }      />      <FormHelperText>{helperText}</FormHelperText>    </FormControl>  );}export default function Page() {  const { control, handleSubmit } = useForm<FormValues>({    defaultValues: { password: "" },  });  return (    <form      noValidate      onSubmit={handleSubmit((values) =>        alert(JSON.stringify(values, null, 2)),      )}    >      <Stack direction="column" spacing={2}>        <PasswordField control={control} />        <div>          <MuiButtonElement control={control} variant="contained" type="submit">            Set password          </MuiButtonElement>        </div>      </Stack>    </form>  );}

How it works

The hook has the same logic as useMuiInputAdapterProps: it falls back to a surrounding FormControl for required and disabled, and you should pass the returned ref to inputRef. Value handling, transforms and the validation rules are described on useMuiInputBaseAdapter.

Changing the input type. The result always contains a type key, even when you don't pass one. In <OutlinedInput type="password" {...adapter} />, the spread replaces your type with undefined and the password shows as plain text. Pass the type to the hook instead, as the demo does with type: visible ? "text" : "password". Changing it re-renders the same field, so the value is kept.

Label notch. OutlinedInput only leaves a gap in its border for the floating label when you give it the same text through its label prop. This is MUI behavior, but it's easy to miss when the label comes from an InputLabel next to it.

Length messages. The demo's minLength message is a function, (length) => `Use at least ${length} characters`, so the number in the text follows the rule. maxLength keeps its default, "Please shorten this text to 64 characters or less."

Props

Prop

Type

Return value

Prop

Type

On this page