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.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";
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