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
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
Attached is the screenshot of the process:
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
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.jsxInput.jsxSelectInput.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";
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
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;
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
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;
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;
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"
Next, we define our validation schema object as below
const validationSchema = Yup.object({
FullName: Yup.string("Enter your Name").required("Name is required"),
});
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;
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)
Wow this is amazing