Piplup
@piplup/rhf-adaptersreact-number-formatHooks

usePatternFormatAdapter

Returns adapter props for wiring react-number-format's PatternFormat masks (phone numbers, card numbers, codes) to React Hook Form.

usePatternFormatAdapter connects a field to React Hook Form and returns props you spread onto PatternFormat from react-number-format. It's what PatternFormatElement uses internally. It calls useNumberFormatBaseAdapter with the same props; this page covers what matters for masked input.

Use the element for one-off fields. Reach for the hook when the same mask and its completeness rules appear in several forms, for example a phone or card number field you want to define once.

Import

import { usePatternFormatAdapter } from "@piplup/rhf-adapters/react-number-format";

Usage

This UsPhoneField shows +1 (###) ###-#### and only accepts a complete number with a valid area code. Submit a partial number, or one starting with 0, to see the errors. The alert shows that only the digits are stored.

"use client";import Stack from "@mui/material/Stack";import TextField from "@mui/material/TextField";import { MuiButtonElement } from "@piplup/rhf-adapters/mui-material";import { usePatternFormatAdapter } from "@piplup/rhf-adapters/react-number-format";import { type Control, useForm } from "react-hook-form";import { PatternFormat } from "react-number-format";type FormValues = { phone: string };// The form stores only the 10 typed digits, so the rules below// run against "2125550100", not "+1 (212) 555-0100".function UsPhoneField({ control }: { control: Control<FormValues> }) {  const adapter = usePatternFormatAdapter<string, FormValues, "phone">({    control,    name: "phone",    required: true,    composeHelperText: true,    pattern: /^\d{10}$/,    messages: {      required: "Enter a phone number",      pattern: "Enter all 10 digits",    },    rules: {      validate: (value) => !/^[01]/.test(value) || "Area codes can't start with 0 or 1",    },  });  return (    <PatternFormat      customInput={TextField}      label="Mobile number"      format="+1 (###) ###-####"      mask="_"      {...adapter}    />  );}export default function Page() {  const { control, handleSubmit } = useForm<FormValues>({    defaultValues: { phone: "" },  });  return (    <form      noValidate      onSubmit={handleSubmit((values) => alert(JSON.stringify(values, null, 2)))}    >      <Stack direction="column" spacing={2}>        <UsPhoneField control={control} />        <div>          <MuiButtonElement control={control} variant="contained" type="submit">            Submit          </MuiButtonElement>        </div>      </Stack>    </form>  );}

How it works

Stored value is the raw input. The form receives values.value: only the characters typed into # slots. Literal characters from format (including the +1 above) and mask placeholders are dropped. With format="(###) ###-####", the display (212) 555-0100 is stored as "2125550100", and a half-typed (212) 55_-____ as "21255".

To keep the formatted text instead, pass transform.output: (values) => values.formattedValue. Note that a partly filled field then still contains the mask characters.

Default values. Use the same raw form for defaultValues. PatternFormat formats a digits-only string like "4085551212" into (408) 555-1212 on first render.

Checking for a complete value. A partly filled mask is not empty, so required alone accepts it. Because the stored string holds only the typed characters, its length tells you how much is filled in:

  • pattern: /^\d{10}$/ (as in the demo) or minLength: 10 rejects incomplete numbers.
  • rules.validate can check parts of the value, such as the area code.
KeyDefault message
requiredPlease fill out this field.
patternPlease match the requested format.
minLengthPlease lengthen this text to n characters or more
maxLengthPlease shorten this text to n characters or less.

The default length message talks about "text", which reads oddly for a phone or card number, so set messages for these.

Errors and helper text. Set composeHelperText: true when rendering through customInput={TextField} to receive error and a helperText that switches to the error message. Without it, neither prop is returned. With customInput, the field ref is attached to the TextField root, so focus-on-error does not reach the input.

Props

Prop

Type

Return value

Prop

Type

On this page