Technical Writing

How to Add Sign-in Functionality to React with Userbase

2021-03-29

I always thought that to have user accounts in your website, you need to run your own server, but Userbase proved me wrong. Userbase provides a way of logging in users and accessing user data in static sites(which includes static .html files), which fits in well with the Jamstack idea.

Introduction

While building a website, you may need to add some user-specific functionality such as allowing users to create and access their playlists across devices. Userbase is a backend provider that allows you to add user accounts and store data for your site. With Userbase, you sign up, sign in and sign out users from your site. With Userbase, you can also store data for your users. Userbase offers a JavaScript SDK that makes it very easy to work with Userbase.

Userbase offers a free tier that limits you to a single web app, with at most 100 users and at most 100MB of stored data. They also offer a Production tier which is an $89 annual subscription. This plan allows you to have an unlimited number of apps, an unlimited number of users, and 1GB of storage. Extra storage space can be purchased as an add-on.

In this tutorial, you will build a React app that has sign-in functionality with Userbase.

Prerequisites

  • As you will be building this page with JavaScript, you will need to have Node.js and npm installed on your machine. You can follow the steps described on the Downloading and Installing Node.js and npm page in the npm docs.

  • You will also need a Userbase account. You can create a free Userbase account if you do not already have one from the signup page.

Step 1 — Creating the React Application

We will bootstrap this project with create-react-app. create-react-app is a utility that allows developers to quickly get started with a pre-configured react app.

Open a terminal in the folder where you want the app to be located.

Install create-react-app if you don't already have it installed

npm install -g create-react-app

This installs the create-react-app package from npm. The -g switch installs the package globally so you can use create-react-app from any directory.

Bootstrap the react app with the following command:

npx create-react-app my-userbase-app

Navigate into the newly created my-userbase-app directory:

cd my-userbase-app

Start the development server:

npm start

When the development server starts, visit localhost:3000 in your browser.

Open src/App.js in your text editor.

Clear the contents and replace them with the following.

import React from 'react';

function App() {
    return(<div>
        <h2>Hello Guest</h2>
    </div>
    )
}

export default App;

When you save, Hello Guest will be displayed in the browser.

You have now created the React application that you will be building on. In the next step, you will install the Userbase SDK

Step 2 — Installing and Initializing the Userbase SDK

Before you install the SDK, stop the running development server by pressing CTRL+C in the terminal.

Run the following command to install userbase-js from npm.

npm install userbase-js

Now you have successfully installed the userbase-js library, you will move on to initialize the SDK.

This initialization will connect the React Website to a Userbase app via the SDK. You need to initialize the app before you can create users and store data.

Restart the development server

npm start

Import the SDK at the top of src/App.js

import React from 'react';
import userbase from 'userbase-js';
...

You will now initialize the userbase object. All you have to do is to run userbase.init({appId: "app-id"}. Replacing app-id with your app's id.

To proceed, you need to have a Userbase app id. When you created a free Userbase account, a starter app is automatically created for you.

Log into your Userbase account. On the dashboard, you will see the starter app and the app id by the side. Copy this app id, you will be using it in subsequent steps.

Edit src/index.js to look like below:

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    useEffect(() => {
            userbase.init({appId: "app-id"}).then((session) => {
            setCurrentUser(session.user)
        }).catch((e) => console.error(e))
    }, [])

    return(<div>
        <h2>Hello { currentUser ? currentUser.username : "Guest" }</h2>
    </div>
    )
}

export default App;

Replace app-id with the app id you copied from the dashboard.

Initialization is supposed to happen only once. That is why we carried out the initialization with a useEffect hook. The empty dependency array passed to the hook makes the script in the effect to be called only once.

When you initialize with userbase.init(), the SDK checks if there is an active session in the browser. If there is a session, userbase.init automatically resumes the session and it returns a session object with a user property containing the user's username, account creation date, user id, etc. If it checks and there is no previous session in the browser, userbase.init returns null instead.

To store the current user's data, we created a currentUser variable with the useState hook. In the callback of the initialization, we store the value contained in session.user in currentUser.

The value of currentUser can tell us whether there is an active session or not. We use this to determine what name to display on the page. When currentUser is not null, i.e. when a user is signed in, we render the currently signed in user's username in the ternary operator. When no user is signed in, when currentUser is null, we return 'Guest' instead.

You have successfully connected the SDK to your Userbase account. In the next step, you will create a way for users to create accounts.

Step 3 — Creating the Sign-Up form

In this step, you will be creating the elements and necessary scripts that will enable users to create accounts.

You will use two controlled inputs to handle the signup forms.

Create the newUsername and newPassword states in the App function.

function App(){
    ...
    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")
    ...
    return ...
}

You will store the values inputted in the signup forms in these two variables.

Create a signUp function in the App function:

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")

    const signUp = () => {
        userbase.signUp({
            username: newUsername,
            password: newPassword
        }).then((user) => {
            setCurrentUser(user)
        }).catch((e) => console.error(e))
    }

    return ...
}

export default App;

The userbase.signUp function is used to create new Userbase accounts. This function accepts an object with the username and password properties. When the user is created, it returns a user object that has the same properties as the session.user object you encountered during initialization.

In signUp, we call userbase.signup() with the username and password parameters. In the callback, we then set the state of currentUser to have the data of the newly created user.

You will now add the input areas where users who want to sign up will type a username and password.

Edit the JSX returned in App

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")

    ...

    return(<div>
        <h2>Hello { currentUser ? currentUser.username : "Guest" }</h2>

        {
            currentUser ? "logged in" : <div>
                Sign Up
                <input type="text" placeholder="Username" value={newUsername}
                    onChange={(event)=>setNewUsername(event.target.value)} required />
                <input type="password" placeholder="Password" value={newPassword}
                    onChange={(event)=>setNewPassword(event.target.value)} required />
                <button onClick={signUp}>Submit</button>
            </div>
        }
    </div>
    )
}

export default App;

We want the signup form to only be shown if no user is logged in. That is why we use the ternary operator on currentUser to customize the visibility of the form.

Now that you have added the signup form, you can create a new account with the form. Once the account is created, you will be automatically be logged in with that account. Since there is now an active session, "Hello Username" will be displayed instead of "Hello Guest", where Username is the username you used to create the account.

When you are signed into the newly created account. You will notice that there is no way to sign out. You will build this in the next step.

Step 4 — Adding a Sign Out Button

You will add a button that signs a user out when clicked.

Create this signOut function in App.

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    ...
    const signOut = () => {
        userbase.signOut().then(() => {
            setCurrentUser(null)
        }).catch((e) => console.error(e))
    }
    return ...
}

export default App;

userbase.signOut ends the session of the currently logged-in user. Once a user is signed out, we will update the state of currentUser to null since no user is logged in. We added this function call to the callback of userbase.signOut.

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")

    ...

    return(<div>
        <h2>Hello { currentUser ? currentUser.username : "Guest" }</h2>

        {
            currentUser ? <button onClick={signOut}>Sign Out</button> : <div>
                ...
        }
    </div>
    )
}

export default App;

The sign-out button is to be displayed only when a user is logged in. That is why we added the button in place of "logged in" in the ternary operator used to conditionally render the signup section.

Click the button to sign out of your account.

In the next step, you will add a login section so returning users can sign into their accounts.

Step 5 — Adding a login form

Create two additional states loginUsername and loginPassword in the App function with the useState hook.

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")

    const [loginUsername, setLoginUsername] = useState("")
    const [loginPassword, setLoginPassword] = useState("")

    ...
}

export default App;

These two variables will be used to handle input states for the login section.

Create a signIn function in the App function:

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")

    const [loginUsername, setLoginUsername] = useState("")
    const [loginPassword, setLoginPassword] = useState("")

    const signIn = () => {
        userbase.signIn({
            username: loginUsername,
            password: loginPassword
        }).then((user) => {
            setCurrentUser(user)
        }).catch((e) => console.error(e))
    }

    return ...
}

export default App;

The userbase.signIn function is used to sign into a Userbase account. This function accepts an object with the username and password properties. It returns a user object. We set the state of currentUser to the returned user object in the callback of userbase.signIn

Edit the JSX returned in App to show the login inputs:

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    ...

    return(<div>
        <h2>Hello { currentUser ? currentUser.username : "Guest" }</h2>

        {
            currentUser ? <button onClick={signOut}>Sign Out</button> : <div>
                Sign Up
                <input type="text" placeholder="Username" value={newUsername}
                    onChange={(event)=>setNewUsername(event.target.value)} required />
                <input type="password" placeholder="Password" value={newPassword}
                    onChange={(event)=>setNewPassword(event.target.value)} required />
                <button onClick={signUp}>Submit</button>
                <br />

                Sign In
                <input type="text" placeholder="Username" value={loginUsername}
                    onChange={(event)=>setLoginUsername(event.target.value)} required />
                <input type="password" placeholder="Password" value={loginPassword}
                    onChange={(event)=>setLoginPassword(event.target.value)} required />
                <button onClick={signIn}>Sign in</button>
            </div>
        }
    </div>
    )
}

export default App;

This renders both the sign-un form and the sign-up form when there is no active session.

You can now log back into the account that you created.

Your final src/App.js file should look like this now.

import React, { useState, useEffect } from 'react';
import userbase from 'userbase-js';

function App() {
    const [currentUser, setCurrentUser] = useState(null)

    const [newUsername, setNewUsername] = useState("")
    const [newPassword, setNewPassword] = useState("")

    const [loginUsername, setLoginUsername] = useState("")
    const [loginPassword, setLoginPassword] = useState("")

    useEffect(() => {
            userbase.init({appId: "app-id"}).then((session) => {
            setCurrentUser(session.user)
        }).catch((e) => console.error(e))
    }, [])

    const backgroudChange = (event) => {
        setCurrentColor(event.target.value)
    }

    const signUp = () => {
        userbase.signUp({
            username: newUsername,
            password: newPassword
        }).then((user) => {
            setCurrentUser(user)
        }).catch((e) => console.error(e))
    }

    const signOut = () => {
        userbase.signOut().then(() => {
            setCurrentUser(null)
        }).catch((e) => console.error(e))
    }

    const signIn = () => {
        userbase.signIn({
            username: loginUsername,
            password: loginPassword
        }).then((user) => {
            setCurrentUser(user)
        }).catch((e) => console.error(e))
    }

    return(<div >
        <h2>Hello { currentUser ? currentUser.username : "Guest" }</h2>

        <p>Current color: {currentColor}</p>
        <p>Select a background color</p>
        <div onChange={backgroudChange}>
            <input type="radio" name="color" value="white" />White
            <input type="radio" name="color" value="red" />Red
            <input type="radio" name="color" value="blue" />Blue
            <input type="radio" name="color" value="green" />Green
        </div>

        {
            currentUser ? <button onClick={signOut}>Sign Out</button> : <div>
                Sign Up
                <input type="text" placeholder="Username" value={newUsername}
                    onChange={(event)=>setNewUsername(event.target.value)} required />
                <input type="password" placeholder="Password" value={newPassword}
                    onChange={(event)=>setNewPassword(event.target.value)} required />
                <button onClick={signUp}>Submit</button>
                <br />
                Sign In
                <input type="text" placeholder="Username" value={loginUsername}
                    onChange={(event)=>setLoginUsername(event.target.value)} required />
                <input type="password" placeholder="Password" value={loginPassword}
                    onChange={(event)=>setLoginPassword(event.target.value)} required />
                <button onClick={signIn}>Submit</button>
            </div>
        }
    </div>
    )
}

export default App;

Conclusion

You have now built a simple React app with sign-in and sign-up functionality with Userbase.

As a follow-up from this tutorial, you may be interested in learning how Userbase allows you to create databases for individual users. You can store a user's data in these databases and this data will be available wherever a user is signed in. You can learn more about how to use databases in the SDK documentation.

Userbase also allows you to upload files and permit specific users to read data from another user's database. You can learn more about uploading files and sharing databases in the documentation.