DEV Community

Eke Enyinnaya Diala
Eke Enyinnaya Diala

Posted on

Using Chakra UI, React Hook Form, and Yup for React Form Validation.

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:
Animated login form

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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')
);
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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),
  });
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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:

Oldest comments (4)

Collapse
 
alcnturkmen profile image
Ali Can Turkmen

So cool. I love Angular. But I have never worked React. I want to work React.

Collapse
 
dico_monecchi profile image
Adriano Monecchi

I'll give it a try, thanks for sharing!

Collapse
 
ahjashish profile image
Ashish Ahuja

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

Collapse
 
dufia profile image
Konrad Moskal

Inputs should re-validate as you type. Trying to figure out how to do it myself.