DEV Community

Cover image for How To Create Custom Input Components With Formik, yup and Material-UI
ISIAKA ABDULAHI AKINKUNMI
ISIAKA ABDULAHI AKINKUNMI

Posted on • Updated on

How To Create Custom Input Components With Formik, yup and Material-UI

Components are building blocks for our UIs and with React, reusable, clean and efficient codes are easy. In this tutorial, we will learn how to create custom input field components with React, material-ui and yup. We will use material-ui components in the Formik Field and implement validations using yup.

Introduction

Forms are great features that are common in many web applications, creating a custom component with a popular Javascript library like React and formik is a straightforward process with decent functionalities and error handling.

Create Project

Lets create a new project using vite command-line tool:

 npm create vite@latest

Enter fullscreen mode Exit fullscreen mode

This will prompt us to select the project Name( for this tutorial we used Formik-Input-component), Package name (formik-input-component), framework( React) and variant (JavaScript).
After all, we run the following code:

  cd Formik-Input-component
  npm install
  npm run dev

Enter fullscreen mode Exit fullscreen mode

Attached is the screenshot of the process:

image showing the installation of react

Installation

Next, we will install formik, yup, and material ui packages using npm or yarn:

npm install @mui/material @emotion/react @emotion/styled formik yup

OR

yarn install @mui/material @emotion/react @emotion/styled formik yup

Enter fullscreen mode Exit fullscreen mode

Implementation

Once the packages are installed successfully, we can create a folder called formikControl where all our files will be saved. In the formikControl folder, we will create the following files

  • FormikControl.jsx
  • Input.jsx
  • SelectInput.jsx
  • index.js,
  • TextError.jsx

Component

Let's start with with our Input.jsx file, we will do the following imports into our file



 import { Field, useField } from "formik";
 import PropTypes from "prop-types";
 import { FormLabel, Grid, TextField } from "@mui/material";

Enter fullscreen mode Exit fullscreen mode

After the imports, we create a functional component and the following code:


 const Input = (props) => {
 const { name, label, ...rest } = props;
 const [field] = useField(name);

  return (
    <Grid container direction="column">
      <FormLabel component="legend">{label}</FormLabel>
      <Field {...field} {...rest} as={TextField} />
    </Grid>
  );
};

Input.propTypes = {
  label: PropTypes.string,
  name: PropTypes.string,
};

export default Input

Enter fullscreen mode Exit fullscreen mode

The <Field/> automatically hooks up our TextField to Formik courtesy of the as attribute. We can render our React component and formik will automatically inject onChange, onBlur, name, and value props of the input field attributed to the name prop of the component. We destructed the field object from usefield to perform this.

To be able to handle errors from the validations, we use our already created <TextError/> and render it beneath our . To get the error messages, we destructure ErrorMessage from formik and pass in <TextError/> just like we did for TextField in . Below is the new code that we have:


import { ErrorMessage, Field, useField } from "formik";
import PropTypes from "prop-types";
import { FormLabel, Grid, TextField } from "@mui/material";
import TextError from "./TextError";
const Input = (props) => {
  const { name, label, ...rest } = props;
  const [field] = useField(name);

  return (
    <Grid container direction="column">
      <FormLabel component="legend">{label}</FormLabel>
      <Field {...field} {...rest} as={TextField} />
      <ErrorMessage name={name} as={TextError} />
    </Grid>
  );
};

Input.propTypes = {
  label: PropTypes.string,
  name: PropTypes.string.isRequired,
};

export default Input;

Enter fullscreen mode Exit fullscreen mode

The next thing we do is to define our .

TextError.jsx


import PropTypes from "prop-types";

const TextError = ({ children }) => {
  return <div style={{color:"red"}}>{children}</div>;
};

TextError.propTypes = {
  children: PropTypes.node,
};

export default TextError

Enter fullscreen mode Exit fullscreen mode

The <TextError/> render any error that returns from the ErrorMessage and display it,

Note: we gave it a color red to show it's an error.

Rendering Our Input Field

To display our input fields to the user, we try to make them seamless and easier to use. To do this, we import our Input component into our . Here is the complete code


import Input from "./Input";
import PropTypes from "prop-types";
const FormikControl = ({ control, ...rest }) => {
  switch (control) {
    case "input":
      return <Input {...rest} />;
    // other cases...
     default:
    return null;
  }
};
FormikControl.propTypes = {
  control: PropTypes.string.isRequired,
};
export default FormikControl;

Enter fullscreen mode Exit fullscreen mode

The control prop helps to switch between different types of input field created and the rest prop is used to pass other details passed to the input field component.

yaaah..... our component is ready for use......

To use the Component, we import it into a and render it just like it shown below in <App/>

App.jsx


import {  Grid,Button } from "@mui/material";
import {  Formik,Form } from "formik";
import FormikControl from "./FormikControl";
const App = () => {
  return (
    <Formik
      initialValues={{
        FullName: "",
      }}
      onSubmit={(values)=>console.log(values) }
    >
        <Form style={{ width: "100%" }}>
            <Grid container direction="column">
              <FormikControl
                control="input"
                label="Name"
                id="name"
                name="FullName"
                placeholder="Enter your name"
              />
           <Button type="submit">Submit</Button>
            </Grid>
          </Form>
        );

    </Formik>
  );
};
export default App;

Enter fullscreen mode Exit fullscreen mode

Here, We imported our <FormikControl/> and rendered it inside Formik, we passed our initialValues, and onSubmit Props. We must ensure that the name attribute in our <FormikControl/> matches the one in our intialValues. With that Formik automatically assigned values and other field props.

Yup implementation

After creating our input components, we would like to add some validations to them, yup is a JavaScript schema builder for value parsing and validation. we already installed the package and use it we import it into our and define the validation rules as shown below:

import * as Yup from "yup" 
Enter fullscreen mode Exit fullscreen mode

Next, we define our validation schema object as below


 const validationSchema = Yup.object({
    FullName: Yup.string("Enter your Name").required("Name is required"),
  });

Enter fullscreen mode Exit fullscreen mode

Here, we also use the name attribute that was used in i.e FullName as the key for the validation to work effectively.
Finally, we passed our validationSchema as a prop to the Formik component. Here is the final code in <App/>.

App.jsx


import { Form, Formik } from "formik";
import { Grid, Button } from "@mui/material";
import * as Yup from "yup";
import FormikControl from "./FormikControl";
const App = () => {
  const validationSchema = Yup.object({
    FullName: Yup.string("Enter your Name").required("name is required"),
  });

  return (
    <Formik
      initialValues={{
        FullName: "",
      }}
      validationSchema={validationSchema}
      onSubmit={(values) => console.log(values)}
    >
      <Form style={{ marginTop: "1rem", width: "100%" }}>
        <Grid container direction="column">
          <FormikControl
            control="input"
            label="Name"
            id="name"
            name="FullName"
            placeholder="Enter your FullName"
          />
        </Grid>
        <Button type="submit">Submit</Button>
      </Form>
    </Formik>
  );
};
export default App;

Enter fullscreen mode Exit fullscreen mode

That ends our lesson on implementing a <FormikControl /> with yup, formik and material-ui. The code is available in this repo and I have also implemented input fields like textarea, radio, date, time, select and checkbox.

Thanks for reading

Top comments (1)

Collapse
 
canhamzacode profile image
Hamzat Abdul-muizz

Wow this is amazing