When building our react applications, we more often than not have to validate user generated content before we send it to our api endpoints for processing and storage.
Recently, we started development on a product at my workplace. We decided to go with React. I insisted we use Chakra for our UI components. Partly because I am Nigerian and it absolutely gladdens my heart to see my compatriots doing great work and partly because Chakra is really good. For one, Chakra gives you accessibility by default. I cannot overemphasise how important that was for us.
One stumbling block with using Chakra was the lack of resources on the internet for when you inevitably run into trouble. This is understandable because it is still pretty young. This is my attempt to fill some of that gap.
Validating a login form with react-hooks-form, and yup.
I am assuming the reader has some knowledge setting up react. If you do not, please visit the react website for instructions on how to set up a react app.
Here is what we expect to achieve at the end of this:
After creating a new project with React, we need to install Chakra, React Hook form, and Yup. For that we open our terminal, navigate to our project's folder.
- install chakra
npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion
# or
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
- install yup, react-hook-form, and @hookform/resolvers. We install @hookform/resolvers because react-hook-form supports multiple schema libraries besides yup.
npm i yup react-hook-form @hookform/resolvers
# or
yarn add yup react-hook-form @hookform/resolvers
- next we wrap our app with the Chakra provider so we can use Chakra components in our app. This we will do in our app's entry point,
index.tsx
in my case.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ChakraProvider } from '@chakra-ui/react';
ReactDOM.render(
<React.StrictMode>
<ChakraProvider>
<App />
</ChakraProvider>
</React.StrictMode>,
document.getElementById('root')
);
Now, we have our set up complete, let's now get down to building the login form.
- First, we import the Form components provided by Chakra. These components give us nice, accessible form components. I won't go into explaining what each do because I do not want this to become very long. Please visit the Chakra documentation for more information on these components.
import {
FormControl,
FormLabel,
FormErrorMessage,
FormHelperText,
Input,
Button
} from '@chakra-ui/react';
Then we import yup
, yupResolver
, and useForm
to manage validation for our forms. Again, please visit React Hook Form docs and Yup docs for more information on these imports.
import * as yup from 'yup';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
We create the form schema and the type definitions for our form inputs. The type definitions are for Typescript users.
const schema = yup.object().shape({
email: yup.string().email().required(),
password: yup.string().min(8).required(),
});
type LoginFormInputs = {
email: string;
password: string;
};
Now, let's use all of these to create our login form. We will register our inputs into react hook form using useForm
. The reason is clearly stated in the docs:
One of the key concepts in React Hook Form is to register your uncontrolled component into the hook. This will make its value available for both the form validation and submission.
Note: Each field is required to have a unique name as a key for the registration process.
useForm
returns to us an errors
object containing our validation errors [if any], a handleSubmit
function that takes callback which is executed if validation is successful, and a register
function we use to register our inputs. We set the mode
to onBlur
so our inputs are validated when the user leaves the input field
const { register, handleSubmit, errors } = useForm<LoginFormInputs>({
mode: 'onBlur',
resolver: yupResolver(schema),
});
Now, we register our inputs. We use FormControl
because it:
provides context such as isInvalid, isDisabled, and isRequired to form elements.
We use errortext
to display the errors from our errors object, if any.
<FormControl
isInvalid={!!errors?.email}
errortext={errors?.email?.message}
p='4'
isRequired
>
<FormLabel>Email</FormLabel>
<Input type='email' name='email' placeholder='Email' ref={register} />
<FormErrorMessage>{errors?.email?.message}</FormErrorMessage>
<FormHelperText>
We are obviously giving this straight to Facebook.
</FormHelperText>
</FormControl>
Here's what the full component looks like:
import {
FormControl,
FormLabel,
FormErrorMessage,
FormHelperText,
Input,
Button
} from '@chakra-ui/react';
import * as yup from 'yup';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
const schema = yup.object().shape({
email: yup.string().email().required(),
password: yup.string().min(8).required(),
});
type LoginFormInputs = {
email: string;
password: string;
};
export default function LoginForm() {
const { register, handleSubmit, errors } = useForm<LoginFormInputs>({
mode: 'onBlur',
resolver: yupResolver(schema),
});
const onSubmit = (values: LoginFormInputs) => console.log(values);
return (
<form style={{ width: 350 }}>
<FormControl
isInvalid={!!errors?.email?.message}
errortext={errors?.email?.message}
p='4'
isRequired
>
<FormLabel>Email</FormLabel>
<Input type='email' name='email' placeholder='Email' ref={register} />
<FormErrorMessage>{errors?.email?.message}</FormErrorMessage>
<FormHelperText>
We are obviously giving this straight to Facebook.
</FormHelperText>
</FormControl>
<FormControl
isInvalid={!!errors?.password?.message}
errortext={errors?.password?.message}
px='4'
pb='4'
isRequired
>
<FormLabel>Password</FormLabel>
<Input
ref={register}
type='password'
placeholder='Password'
name='password'
/>
<FormErrorMessage>{errors?.password?.message}</FormErrorMessage>
</FormControl>
<Button
onClick={handleSubmit(onSubmit)}
p='4'
mx='4'
mt='6'
w='90%'
colorScheme='blue'
variant='solid'
disabled={!!errors.email || !!errors.password}
>
Login
</Button>
</form>
);
}
Let's import the LoginForm
component into our App.tsx
file so we can use it.
import { Center, Flex } from '@chakra-ui/react';
import LoginForm from './LoginForm';
function App() {
return (
<Flex justify='center' h='100vh' w='100vw' align='center'>
<Center w='100%'>
<LoginForm />
</Center>
</Flex>
);
}
export default App;
And that's it folks. That is how we validate a form using React Hook Form, Yup, and Chakra.
You can try a live demo on the sandbox below:
Top comments (4)
Which versions are you using here? Doesn't seem to work for me on "react-hook-form": "^7.5.2", "next": "10.2.0".
Keeps giving an error that
TypeError: path.split is not a function
So cool. I love Angular. But I have never worked React. I want to work React.
I'll give it a try, thanks for sharing!
Inputs should re-validate as you type. Trying to figure out how to do it myself.