You can find a live demo here and the source code here
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
npx create-react-app client-side-validations
Then we will also want to add bootstrap. For that we execute following in our console:
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:
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:
function App() {
return (
<div className='form-wrapper'>
<Form>
<Container fluid>
</Container>
</Form>
</div>
);
}
And then we style it:
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. Next we add our 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;
}
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:
const [validated, setValidated] = useState(false);
and pass that as the form validated
attribute. We also disable the default html validators with noValidate
:
<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:
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:
<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:
<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:
^[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:
.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:
const [password, setPassword] = useState('')
const [confirmation, setConfirmation] = useState('')
Now in our input fields we add an onChange
to update the password and confirmation:
<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
:
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'
:
<p
style={{ color: 'red', display: 'none' }}
ref={confirmationError}
>
Password and confirmation are not the same
</p>
Next we initialise our ref:
const confirmationError = useRef(null);
And then we also change our if statement:
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:
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:
<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:
<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:
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:
Top comments (0)