Create basic login forms using react.js hooks and bootstrap

August 15, 2020

  • So today I would like to cover how you can create login forms in react.js using hooks and bootstrap from scratch. In the end, you will have a UI like this one below:

featured image

  • So let’s begin the journey…
  • Some prerequisites for the project are that you need to have node.js and an editor like Vscode installed on your system. Optionally you can also install the yarn package manager. Now the first thing we need to do set up a new react.js project, so open up your cmd/terminal and run the below commands to create a new project using the create-react-app module and start your project:-
  • npx create-react-app loginforms
    cd loginforms
    npm start
  • If everything goes well you should get the below screen:- launch app image
  • Now open the loginforms folder in vscode and you will find mainly two folders viz src and public. Open index.html in the public folder and make the following edits:-
    First, change the text within title and meta tags as per your requirements:-
    <html lang="en">
    <head>
    ...
    <meta
    name="description"
    content="Login forms app created using react.js"
    />
    ...
    <title>Login forms App</title>
    </head>
    </html>
    view raw index.html hosted with ❤ by GitHub
  • We are going to use bootstrap 4 in our project so head over to there official docs page here and add CSS and js links from the page to index.html as follows:-
    <html>
    <head>
    ...
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <link rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
    integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
    crossorigin="anonymous">
    ...
    </head>
    <body>
    ...
    <div id="root"></div>
    ...
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    </body>
    </html>
    view raw index.html hosted with ❤ by GitHub
  • The complete file after all these changes will look like the one below: Github File Link
  • Now let’s create a basic folder structure for our project so that all files are organized properly:- files structure image
  • We are adding a components and constants folders within src folder. The components folder will contain all the related components of the project. First, let us add a header component at the top of the page. For that create a folder named Header in components folder and then create a file called Header.js within that folder
    header structure image
  • We are to going use bootstrap’s navbar component for creating our header. The code for the header component is as follows:-
    import React from 'react';
    function Header() {
    return(
    <nav class="navbar navbar-dark bg-primary">
    <div className="row col-12 d-flex justify-content-center text-white">
    <span className="h3">Register</span>
    </div>
    </nav>
    )
    }
    export default Header;
    view raw Header.js hosted with ❤ by GitHub
  • Now let’s import the Header component in App.js file and modify App.js as follows:
    import React from 'react';
    import Header from './components/Header/Header';
    function App() {
    return (
    <div className="App">
    <Header />
    </div>
    )
    }
    view raw App.js hosted with ❤ by GitHub
  • If the code changes are correct you will be able to see the header in the webpage as shown below:- Register app image
  • Next, let us add the registration form for the users. Create a RegistrationForm folder in components and add the below code to create user inputs with labels:-
    import React, {useState} from 'react';
    function RegistrationForm(props) {
    return(
    <div className="card col-12 col-lg-4 login-card mt-2 hv-center">
    <form>
    <div className="form-group text-left">
    <label htmlFor="exampleInputEmail1">Email address</label>
    <input type="email"
    className="form-control"
    id="email"
    aria-describedby="emailHelp"
    placeholder="Enter email"
    />
    <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
    </div>
    <div className="form-group text-left">
    <label htmlFor="exampleInputPassword1">Password</label>
    <input type="password"
    className="form-control"
    id="password"
    placeholder="Password"
    />
    </div>
    <div className="form-group text-left">
    <label htmlFor="exampleInputPassword1">Confirm Password</label>
    <input type="password"
    className="form-control"
    id="confirmPassword"
    placeholder="Confirm Password"
    />
    </div>
    <button
    type="submit"
    className="btn btn-primary"
    >
    Register
    </button>
    </form>
    </div>
    )
    }
    view raw RegistrationForm.js hosted with ❤ by GitHub
  • We have added input for email on line no 8 and password inputs on 18 and 26. This will show inputs on-screen however we haven’t added a way to manage and store the value of text entered by user or action on submit button click so next let’s see how we can do that…
  • So if you haven’t used hooks before here’s a brief introduction to react.js useState hook:- “State variables are variables whose values can be dynamically updated in react.js context and we can use them to update various UI elements. useState hook provides a way to declare and update state variables in various react.js functional components.”
  • Let’s see an example of how the useState hook can be used to handle values entered by the user in email and password input:-
    import React, {useState} from 'react';
    function RegistrationForm(props) {
    const [state , setState] = useState({
    email : "",
    password : ""
    })
    const handleChange = (e) => {
    const {id , value} = e.target
    setState(prevState => ({
    ...prevState,
    [id] : value
    }))
    }
    return(
    <div className="card col-12 col-lg-4 login-card mt-2 hv-center">
    ...
    <input type="email"
    className="form-control"
    id="email"
    aria-describedby="emailHelp"
    placeholder="Enter email"
    value={state.email}
    onChange={handleChange}
    />
    ...
    <input type="password"
    className="form-control"
    id="password"
    placeholder="Password"
    value={state.password}
    onChange={handleChange}
    />
    ...
    )
    view raw useStateExample.js hosted with ❤ by GitHub
  • useState hook typically returns two parameters viz the state variables and a function to update the state variables
  • In the above example, we initialized email and password state values using useState hook on line no 3. The state object will contain the email and password values while the setState method is responsible for updating these values.
  • We passed this state variable values in the value field of inputs in line no 22 and 30 and the responsibility for updating the values lies with the handleChange function. We can make similar changes to confirmPassword input field
  • Next, we need to send these details to the backend server. This will be handled in on click on submit button so let’s add a click event handler to register button for sending a request to backend:-
    import React, {useState} from 'react';
    function RegistrationForm(props) {
    ...
    const handleSubmitClick = (e) => {
    e.preventDefault();
    if(state.password === state.confirmPassword) {
    sendDetailsToServer()
    } else {
    props.showError('Passwords do not match');
    }
    }
    ...
    return(
    <div className="card col-12 col-lg-4 login-card mt-2 hv-center">
    ...
    <button
    type="submit"
    className="btn btn-primary"
    onClick={handleSubmitClick}
    >
    Register
    </button>
    </div>
    )
    }
    view raw handleClick.js hosted with ❤ by GitHub
  • So we first define handleSubmitClick function where we check if the password inputs match and call sendDetailsToServer function to make a backend API request else we will show an error to user that passwords do not match.
  • axios is an npm module that is used to make API requests to the backend. You can find more information about it here. Let’s add it to our project before proceeding further.
  • First go back to terminal/cmd and make sure in the project directory. Run the below command to add axios npm module:-
  •  npm install --save axios
  • Now let’s go back to defining our sendDetailsToServer function:- You will need to create and set up a backend server for handling API requests. You can check out an excellent article on creating them using Node.js and MongoDB here:- Backend set up article. Just keep one thing in mind that we are not using username field in frontend so if you are following above tutorial then do take care to remove the same. You can find a repo related to these changes here: Github.
    const sendDetailsToServer = () => {
    if(state.email.length && state.password.length) {
    props.showError(null);
    const payload={
    "email":state.email,
    "password":state.password,
    }
    axios.post(API_BASE_URL+'/user/register', payload)
    .then(function (response) {
    if(response.status === 200){
    setState(prevState => ({
    ...prevState,
    'successMessage' : 'Registration successful. Redirecting to home page..'
    }))
    redirectToHome();
    props.showError(null)
    } else{
    props.showError("Some error ocurred");
    }
    })
    .catch(function (error) {
    console.log(error);
    });
    } else {
    props.showError('Please enter valid username and password')
    }
    }
  • We are making a post request to the server where API_BASE_URL is defined in the constants file. The complete code of RegistrationForm.js file can be found here:-
    Github Article
  • Next we will set-up react-router for displaying login and register pages at two different address paths. First, install react-router-dom npm module by entering the below command in cmd/terminal:-
    npm install react-router-dom
  • Next set up react-router in App.js file of the project:-
    import React, {useState} from 'react';
    import RegistrationForm from './components/RegistrationForm/RegistrationForm';
    import {
    BrowserRouter as Router,
    Switch,
    Route
    } from "react-router-dom";
    function App() {
    return (
    <Router>
    <div className="App">
    <Header/>
    <div className="container d-flex align-items-center flex-column">
    <Switch>
    <Route path="/" exact={true}>
    <RegistrationForm />
    </Route>
    </Switch>
    </div>
    </div>
    </Router>
    )
    }
    view raw App.js hosted with ❤ by GitHub
  • Now if we run npm start command, then we should see the registration form in the home page route.
  • Next, we create a login form by following almost a similar procedure and we can add a redirect link below both the forms to switch between login and registration. Finally, on receiving a success response from the server we can redirect the user to the home page.
  • All these different routes need to be added in App.js file. We have also included a small AlertComponent to show errors while handling user inputs:-
    import React, { useState, useEffect } from 'react';
    import './AlertComponent.css';
    function AlertComponent(props) {
    const [modalDisplay, toggleDisplay] = useState('none');
    const openModal = () => {
    toggleDisplay('block');
    }
    const closeModal = () => {
    toggleDisplay('none');
    props.hideError(null);
    }
    useEffect(() => {
    if(props.errorMessage !== null) {
    openModal()
    } else {
    closeModal()
    }
    });
    return(
    <div
    className={"alert alert-danger alert-dismissable mt-4"}
    role="alert"
    id="alertPopUp"
    style={{ display: modalDisplay }}
    >
    <div className="d-flex alertMessage">
    <span>{props.errorMessage}</span>
    <button type="button" className="close" aria-label="Close" onClick={() => closeModal()}>
    <span aria-hidden="true">&times;</span>
    </button>
    </div>
    </div>
    )
    }
    export default AlertComponent
    view raw AlertComponent.js hosted with ❤ by GitHub
  • If you look at line no 3 you will find a props parameter passed in the component. props are values passed from the parent component to the child component. We can make use of props to update the component state based on changes in the parent component.
  • Here we have made use of another react.js hook called useEffect on line no 12. It is useful for updating component level state variables based on changes in props received from the parent component. Basically it listens for changes in prop values and then executes code written within it based on those changes.
  • You can find the complete source code for this project till this point on the Github repo:- Codeclasifiers loginforms
  • Feel free to check it out for reference, fork it and submit your own modifications via PRs anytime.
  • Now I haven’t covered storing session cookie/token from the backend or created private authentication based routes in this tutorial mainly because I wanted to focus on UI and concept of react.js hooks. This means the homepage route is accessible even without login which defeats the purpose of having user authentication.
  • In the second part, we will see how to store access token received from backend APIs locally and how to make the home route private. Check out the article here: Part 2: Creating private routes and handling session in react.js
  • Bonus tips:-
    Input Icons
    You can add icons before input forms to make UI of the forms more appealing to the user.
    Session Management
    You need to manage client level sessions for the user in case of showing private routes. Do research about it a bit on the react-router site and try to create some of your own or check out the second article in this series to learn more: Article on the private routes.
  • Parting Notes
    Thank you for reading. I hope I could teach you something new today. You can follow me on Twitter or LinkedIn in case you need any guidance or have any doubts related to frontend development.

Saurabh Mhatre

Blog by Saurabh Mhatre
Follow me on Twitter