Simple and responsive react forms with validation
Tags: javascript, webdev, react, css
Published: 2022-09-22 13:21:41

You can find a live demo [here](https://aneshodza.github.io/client-side-validations/) and the source code [here](https://github.com/aneshodza/client-side-validations)
## What is this about?
When making a website, every software developer runs into this request earlier or later: "Yeah so here we need a login form. Please make it responsive and pretty". Now you are trying to use your best knowledge of media queries to try and make it response. What if I told you that it was a lot easier than you thought? In this tutorial we will be going over how to make a pretty, response and validated form in reactJS.
### What will we be going over
To make our form we will use reactJS, bootstrap and regular css. The first part will mainly focus on creating and designing the form, then we will implement client-sided validations and at the end we will add a live updating password strength-meter to show the user how strong his password is.
What we won't be going over is how to validate the incoming data server-sided.
## Getting started
First we initialise our react-app by typing 
```bash
npx create-react-app client-side-validations
```
Then we will also want to add bootstrap. For that we execute following in our console:
```bash
npm install react-bootstrap bootstrap
```
We will all do it on the homepage, so we wont need `rails router` or anything like that. That also means,that me need to clear out our `function App() { /*...*/ }` that it looks like this:
```jsx
import './App.css';

function App() {
  return ();
}

export default App;
```
## Basic form design
For our form design we will make use of bootstrap react components. In our case, we will use the `<Form>` component. For responsiveness we will also use `<Container>` inside the form and wrap all of that in a `form-wrapper` to center the form in the end:
```jsx
function App() {
  return (
    <div className='form-wrapper'>
      <Form>
        <Container fluid>

        </Container>
      </Form>
    </div>
  );
}
```
And then we style it:
```css
div.form-wrapper {
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
}

form {
  border-radius: 10px;
  box-shadow: 0 0 5px 0 hsl(0, 0%, 74%);
  padding: 20px;
}
```
Then we can add our rows and their inputs. For our case we will use Bootstraps `<FloatingLabel>` components. For the name we want to have both inputs on the same height, as long as the form is big enough for that, so we wrap both of those in a `<Col sm={6}>` tag. The `sm={6}` makes the Columns take up 50% of the width as long as we are over a certain threshold which Bootstrap calls `sm`. When we go under that our input boxes take up full width. The code I used looks like [this](https://github.com/aneshodza/client-side-validations/blob/8378127bf9cf42d787d12146b1390663b7b8f4b4/src/App.js). Next we add our css:
```css
button.btn.btn-primary {
  background-color: white;
  color: black;
  border: none;
  transition: all 0.3s ease;
  box-shadow: 0 0 5px 0 hsl(0, 0%, 74%);
}

button.btn.btn-primary:hover {
  color: black;
  background-color: white;
  transform: scale(1.03);
  box-shadow: 0 0 10px 0 hsl(0, 0%, 74%);
}

.form-control {
  border: 1px solid hsl(0, 0%, 74%);
  border-radius: 5px;
  box-shadow: none;
  transition: all 0.3s ease;
}

.form-control:focus {
  box-shadow: 0 0 10px 0 hsl(0, 0%, 74%);
  border: none;
}

div.form-floating > .form-control {
  height: 55px;
}
```
Here you have a screenshot:
![Screenshot of form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5diyh8o87dcrdrv2vgmr.png)
## Validations
Next, we will implement our validations for the form. Bootstrap also offers us validations and error messages out of the box, which we will use. First we need to initialise a new state variable:
```jsx
const [validated, setValidated] = useState(false);
```
and pass that as the form `validated` attribute. We also disable the default html validators with `noValidate`:
```jsx
<Form noValidate validated={validated}>
```
Next we create a function that is called upon submission. That function should check if our form is valid. If it isn't it should abort the submission:
```jsx
const handleSubmit = (event) => {
  const form = event.currentTarget;
  if (form.checkValidity() === false) {
    event.preventDefault();
    event.stopPropagation();
  }
   setValidated(true);
};
```
Now we can actually just add our validators in a very similar way to what we would do if it was regular html:
```jsx
<FloatingLabel controlId='firstnamLabel' label='First name'>
  <Form.Control 
    type='text'
    placeholder='First name'
    required
  />
</FloatingLabel> 
```
There is also the possibility for RegEx, which we will use for our email and password:
```jsx
<Form.Group className='mb-3' controlId='formBasicEmail'>
              <FloatingLabel controlId='emailLabel' label='Enter email'>
  <Form.Control
    type='email'
    placeholder='Enter email'
    required
    pattern='^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
  />
</FloatingLabel>
  <Form.Text className='text-muted' focus>
    We'll (hopefully) never share your email with anyone else.
  </Form.Text>
</Form.Group>
```
The regex password looks like following:
```regex
^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$
```
Maybe you already realised that the icons 'travel' before they actually arrive where they should be. That is because we set a transition time before. We can overwrite that when the field has been validated:
```css
.was-validated .form-control {
  transition: none;
}
```
### Check if password and confirmation match
Next we want to check if our password and confirmation are the same. This is going to be a bit more complicated. What we can do is create a new state variable, that holds the password and its confirmation:
```jsx
const [password, setPassword] = useState('')
const [confirmation, setConfirmation] = useState('')
```
Now in our input fields we add an `onChange` to update the password and confirmation:
```jsx
<Form.Control
  type='password'
  placeholder='Password'
  required
  pattern='^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$'
  onChange={(e) => setPassword(e.target.value)}
/>
```
Don't forget to do the same for the confirmation field. Then we need to compare them in our `handleSubmit`:
```jsx
const handleSubmit = (event) => {
  //...
  if (password !== confirmation) {
    event.preventDefault();
    event.stopPropagation();
  }
//...
}
```
Now we need to also display this somehow. What we can do is an error message that we conditionally render. For that we just create the message, but we also give it a `ref`. You can style it how you want, but it is important that you give it an inline style of `display: 'none'`:
```jsx
<p
  style={{ color: 'red', display: 'none' }}
  ref={confirmationError}
>
  Password and confirmation are not the same
</p>
```
Next we initialise our ref:
```jsx
const confirmationError = useRef(null);
```
And then we also change our if statement:
```jsx
if (password !== confirmation) {
  event.preventDefault();
  event.stopPropagation();
  confirmationError.current.style.display = null;
} else {
  confirmationError.current.style.display = 'none';
}
```
If we now try entering two different passwords it will display our message as it should:
![Screenshot of confirmation being different](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svrfr3qdrqwqhayswo5k.png)
## Live updating password strength meter
Now we can finally create the live updating password strength meter. We just want to have a progress bar that fills up the more secure a users password becomes. Step one is changing our `onChange` in our password field to call a function:
```jsx
<Form.Control
  type='password'
  placeholder='Password'
  required
  pattern='^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$'
  onChange={(e) => handlPasswordChange(e.target.value)}
/>
```
Then we need to add a progress bar below the `FloatingLabel`, where the bar itself has a ref:
```jsx
<div
  style={{
    height: '24px',
    marginTop: '5px',
    backgroundColor: 'hsl(0, 0%, 74%)',
    borderRadius: '5px',
  }}
>
  <div
    ref={progressBar}
    style={{
      height: '100%',
      borderRadius: '5px',
    }}
   ></div>
</div>
```
Now we can finally create our function to dynamically display the password strength. That goes through a few criteria to give points: a normal letter is one point, a number is 2 points and a special character is 3 points. With that we can say, that 33 points are very secure. That we can do like this:
```jsx
const handlePasswordChange = (password) => {
    setPassword(password);
    const letterMatch = (password.match(/[a-z, A-Z]/g) || []).length;
    const numberMatch = (password.match(/[0-9]/g) || []).length;
    const specialMatch = (password.match(/[#?!@$%^&*-]/g) || []).length;

    const strength = letterMatch + numberMatch * 2 + specialMatch * 3;
    progressBar.current.style.width = `${strength * 3}%`;
    let color = 'red';
    if (strength > 10) {
      color = 'orange';
    }
    if (strength > 26) {
      color = 'green';
    }
    progressBar.current.style.backgroundColor = color;
  };
```
What exactly is happening up there? Let us go though it slowly. First we set the state variable to the new password. Then we count the amount of letters, numbers and special characters with regex. The strength we calculate next by taking the amount of matches multiplied with our weights. Next we multiply the points by 3 to set the width of our progress bar. The if statements just set a threshold at how much points what colour our progress bar gets.
If we look at the final product, we can see that it looks quite good:
![Screenshot of the form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcf0df7m8n8pbm31x3f9.png)