Search code examples
javascriptreactjsmaterial-uiformikformik-material-ui

Passing a 'label' prop into Formik <Field /> when using alongside Material UI <TextField />


I'm building a form using Formik and Material UI.

I'm leveraging the Formik component the following way :

My Input component :

const Input = ({ field, form: { errors } }) => {
  const errorMessage = getIn(errors, field.name);
  return <TextField {...field} />;
};

And down into my rendered form, here's how I do it :

<Field
  component={Input}
  name={`patients[${index}].firstName`}
/>

The problem is that Material UI uses a label prop to display label on an input field so the label should be a prop passed to . It works if I "hard-code" it into my "Input" component which defeats the purpose of using a reusable comp.

So that works but inconvenient :

const Input = ({ field, form: { errors } }) => {
  console.log(field.label);
  const errorMessage = getIn(errors, field.name);
  return <TextField {...field} label="first name" />;
};

What I hoped for was using it one level above such as :

<Field
  component={Input}
  name={`patients[${index}].firstName`}
  label="first name"
/>

but the above doesn't work as "label" is not recognised as a prop by Formik (or that's how I understand it but I might be wrong).

Has anyone come across that issue ?

I know I could use my "name" value as a label but it's not great UX as it would leave me with a label such as "patients[0].firstName" aha


Solution

  • Here is a good solution, that is essentially yours, but you can provide anything, not only label. Create the field and put the label on the like so:

    <Field name={`patients[${index}].firstName`} label='First Name' component={MuiTextFieldFormik} />
    

    The trick is to use the spread operator, then the custom component becomes:

    import React from 'react';
    import { TextField } from "@material-ui/core"
    import { get } from "lodash"
    
    export const MuiTextFieldFormik = ({ field, form: { touched, errors }, ...props }) => {
      const error = get(touched, field.name) && !!get(errors, field.name)
      const helperText = get(touched, field.name) && get(errors, field.name)
    
      return (
        <TextField fullWidth variant="outlined" {...field} {...props} error={error} helperText={helperText} />
      )
    }
    

    disappointing that their docs do not have such simple example