DEV Community

Cover image for Building a Complete User Registration System with React and Node.js
WebCraft Notes
WebCraft Notes

Posted on • Originally published at webcraft-notes.com

Building a Complete User Registration System with React and Node.js

There gonna be many JS code sections, and the best way to learn something is to go through this knowledge by yourself but you always can get the whole code here.

The first thing that welcomes a new user is a registration page with its functionality. Registration is the process where users create an account on a website or application. It typically involves providing personal information and creating login credentials, allowing users to access personalized features and services. Today we're going to work on creating the registration page for our React app and implement the entire registration process on the Node.js backend. We have much to cover, so let's outline a brief plan and dive into coding.

1. Create a new empty layout for our Blog CMS
2. Design and build a simple registration page with a registration form
3. Prepare the backend for the registration process: (receive and save user data, generate token)
4. Implement API communication between frontend and backend

Now that we have our roadmap, let's roll up our sleeves and get to work. We'll start by setting up the foundation of our CMS layout, which will serve as the canvas for our registration page and future components.

1. Create a new empty layout for our Blog CMS

We created our "main" layout earlier, and now we need to create a simple empty layout (without header, menu, etc...) with some basic styles for our registration, or login pages.

Let's create an "empty" folder inside "layouts", then we will create two files "EmptyLayout.jsx" and "emptyLayout.styles.scss", first for js code and second for styles.

We will simply need to export our empty layout function with "Outlet" inside, where our components will be rendered.

import React from 'react';
import { Outlet } from 'react-router-dom';
import './emptyLayout.styles.scss';

const EmptyLayout = () => {
  return (
    <div className='emptyLayout'>
      <Outlet />
    </div>
  );
};

export default EmptyLayout;
Enter fullscreen mode Exit fullscreen mode

Also, let's add two simple css rules:

.emptyLayout {
    width: 100%;
    height: 100%;
}
Enter fullscreen mode Exit fullscreen mode

That's it, we can use this layout wherever we need it, and firstly in our registration form that we are going to build in the next part.

2. Design and build a simple registration page with a registration form

To tell you the truth I'm not a good designer, so I found a free registration form design here, and I'll use it in our blog CMS. Okay, we will separate this section into smaller steps to make it easier to implement into your future projects:

  • create a new "registration" folder inside the views folder with two files "Registration.jsx" for our page and "registration.styles.scss" for all the styles;

  • import the "Registration.jsx" file into "App.jsx", and register a new "auth" route for our empty layout with the "registration" route for our "Registration" page;

import Registration from './views/registration/Registration';

<Route path="/auth/*" element={<EmptyLayout />}>
  <Route path='registration' element={<Registration />} />
</Route>
Enter fullscreen mode Exit fullscreen mode
  • prepare our registration form template for work. Also, we will need to import "React", "useState" from react, "useNavigate" and "navLink" from react-router-dom;

  • create a new "Registration" functional component that will use "useState" to store name, email, and password and return our HTML registration form. Each input will update our state data;

import React from 'react';
import { useState } from 'react';
import { NavLink, useNavigate } from 'react-router-dom';
import './registration.styles.scss';

const Registration = () => {
    const navigate = useNavigate();
    const [name, setUsername] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    return (
        <div className="registration">
        <div className='bold-line'></div>
            <div className='container'>
            <div className='window'>
                <div className='overlay'></div>
                <div className='content'>
                <div className='welcome'>Hello There!</div>
                <div className='subtitle'>We're almost done. Before using our services you need to create an account.</div>
                <div className='input-fields'>
                    <input 
                        type='text'
                        placeholder='Username'
                        className='input-line full-width'
                        onChange={(e) => setUsername(e.target.value)}></input>
                    <input
                        type='email'
                        placeholder='Email'
                        className='input-line full-width'
                        onChange={(e) => setEmail(e.target.value)}></input>
                    <input
                        type='password'
                        placeholder='Password'
                        className='input-line full-width'
                        onChange={(e) => setPassword(e.target.value)}></input>
                </div>
                <div className='spacing'>or continue with 
                    <NavLink to="/auth/login" className='highlight'>Login</NavLink>
                </div>
                <div>
                    <button 
                        className='ghost-round full-width loader-styles'
                        onClick={handleSubmit}>
                            'Create Account'
                    </button>
                </div>
                </div>
            </div>
            </div>
        </div>
    );
}

export default Registration;
Enter fullscreen mode Exit fullscreen mode

export default Registration;
It is not the best idea to insert styles, because it will take too much space but you can download the whole app and check styles over there.

And our result will be:

Registration Page image

But it is a simple UI part, we need to add its main functionality.

  • I like to separate some parts of functionality, especially what touches APIs, that is why let's install and use "Axios" and create a folder where we will configure "Axios" and where we will store our API call functions like it was described in one of the previous articles "Mastering Axios in React: From Installation to Project Structure";

  • let's add the "registerNewUser" function inside the "auth.services.js" file in the services folder. This function will use the "post" method to send data to our endpoint;

import { HTTP } from '../http-client';

export const registerNewUser = (data) => 
    HTTP.post('/users/register', data).then(({data}) => data );
Enter fullscreen mode Exit fullscreen mode
  • import this function into the "Registration.jsx" component;

  • send name, email, and password from state to this endpoint, and if we receive "success" status as a response, then redirect from the "registration" page to the "dashboard" page;

const handleSubmit = async (e) => {
    e.preventDefault();
    const result = await registerNewUser({name, email, password});
    if (result.status === 'success') {
        navigate('/');
    }
}
Enter fullscreen mode Exit fullscreen mode

This is great, but if we push the register button nothing will happen (not nothing, we will receive an error) because for now we still do not have any registration routes on our Node.js server side.

3. Prepare the backend for the registration process: (receive and save user data, generate token)

Luckily we did the hardest part of the job in our previous article (we prepared the structure and even started with the user registration process as an example, we have API, model, and controller, but we need to add some updates to our "registerNewUser" controller), so it will be easier for us now. Less words, more code:

  • open a new terminal, go to our "server" folder, and use "npm i bcryptjs jsonwebtoken" command to install 2 new libraries:

BcryptJS is a JavaScript library used for hashing passwords securely. It implements the bcrypt hashing function, allowing us to easily create password hashes that are resistant to brute-force attacks, and for storing user passwords in web applications.

The jsonwebtoken library is a popular Node.js package used for generating and verifying JSON Web Tokens (JWTs). It provides a simple way to create secure tokens for user authentication and authorization in web applications, allowing servers to transmit user information safely between different parts of an application or between separate systems.

  • open the "users.controller.js" file from the routes folder, and import "bcryptjs" and "jsonwebtoken";

  • inside the "registerNewUser" function we need to encrypt the receieved password from the user:

const hashedPassword = await bcrypt.hash(password, 12);
Enter fullscreen mode Exit fullscreen mode

We are using the hash method from the bcrypt library, with a cost factor of 12 (it determines how computationally intensive the hashing process will be);

  • first, we will check if a user with such an email does not exist in our database, if yes we will return an error message;

  • if we do not have a user with such an email we will create a new one with the "createNewUser" function from our users model;

  • if the user was created then we need to return a "success" message, and if not some sort of error message;

async function registerNewUser(req, res) {
    const {name, password, email} = req.body;
    const hashedPassword = await bcrypt.hash(password, 12);
    try {
        const existingUser = await usersModel.findUserByEmail(email);
        if (existingUser) {
            return res.status(400).json({
                status: 'error',
                message: 'Email already in use',
            });
        }
        const user = await usersModel.createNewUser({
            name,
            email,
            password: hashedPassword,
        });
        if (!user) {
            return res.status(500).json({
                status: 'error',
                message: 'Internal server error'
            });
        }
        if (user) {
            res.status(201).json({
                status: 'success',
                message: 'User created successfully',
            })
        }
    } catch (error) {
        console.log(error);
        res.status(500).json({
            status: 'error',
            message: 'Internal server error'
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Nice, but we also should not forget to add "findUserByEmail" function which will look for items by email, into our user model.

async function findUserByEmail(email) {
    try {
        const user = await users.findOne({ email });
        return user;
    } catch (error) {
        console.error('Error finding user by email:', error);
        throw error;
    }
}
Enter fullscreen mode Exit fullscreen mode

Awesome, let's move on.

4. Implement API communication between frontend and backend

We are all most there. Let's check that our API endpoint is the same in the frontend part and in the backend, restart our servers, and try to register.

If you have done everything right, then you should be redirected to our "Dashboard" page, and if you have "compass" from MongoDB installed, then you can check if your "users" db was created and if your newly created user document exists.

MongoDB screenshot with data

Finally, we made it, I think that the "login" functionality is probably the hardest part of the app development process, and we finished with the first half.

We've successfully built a functional user registration system for our Blog CMS. From creating the layout and designing the form in React to implementing the backend logic and establishing API communication, we've covered the essential steps to allow new users to create accounts securely. This registration system serves as a solid foundation for our CMS, paving the way for future enhancements like login functionality and user management. In our next article, we'll tackle the login process to complement this registration system. Thanks for following along, and happy coding!

If you need a source code for this tutorial you can get it here.

Found this post useful? ☕ A coffee-sized contribution goes a long way in keeping me inspired! Thank you)
Buy Me A Coffee

Next step: "Login Logic: Building User Authentication for Your CMS"

Top comments (0)