Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Authentication in React with Appwrite
Emore Ogheneyoma Lawrence
Emore Ogheneyoma Lawrence

Posted on • Edited on

     

Authentication in React with Appwrite

Table of Contents

Introduction

User Authentication as we know it today has become very important in Web applications. Authentication is simply the process of identifying a users identity. For so long now, things like user Authentication has been handled by custom backend in applications which then sends the data to the frontend and all the magic happens, it's usually time consuming, complex and hard to grasp especially if you're not a backend developer. During the course of this article, we will implement user authentication in React web applications using Appwrite.

Appwrite is a versatile open-source backend server that offers various functionalities like authentication, database, storage, and server-side functions. It enables developers to build full-stack applications without requiring a separate backend. With Appwrite, React developers can create real-time applications effortlessly, leveraging its comprehensive set of APIs and services.

This article will guide you through implementing User Authentication using the AppWrite Auth functionality.

For this article, it's helpful to have a basic understanding of handling forms in React and familiarity with React Router. Don't worry if you're not an expert yet, you can still follow along and learn as you go!

React Project Setup with Appwrite

To initiate our project setup, the first step involves registering and creating an account withAppWrite

After registering, create a new project in appwrite by clicking on theCreate a new project button. You would then see a User Interface (UI) like this:

Create Appwrite Project

To get started, you'll need to give your project a name. We'll usereact-appwrite-auth for this example. Don't worry about the Project ID, it will be automatically generated for you when you clickNext

On click of theNext button you'll be routed to a page where you'll be asked to choose your region. Choose the nearest available region for optimal performance.
Next click on theCreate button, this will redirect you to your project dashboard where you'll be able to Add a platform. Since we're using React, we will go with theWeb Platform

Web Platform

On Click of theWeb Platform we are presented with a screen in the screenshot below.

Register Hostname

These two fields are required. Set the name asReact Appwrite Auth and the hostname aslocalhost. Alternatively, you can use* as your hostname.

We would then see two optional steps that guide us on how toInstall the appwrite SDK into our app andInitialize and configure the API Key. We would come back to use them after we've successfully created our React application locally.

After these we would see the screen in the screenshot below.

Project setup complete

Hooray! We have successfully setup our project on the Appwrite Platform.

Now, Let us create a React Project using vite. Run the following commands to create a React project.

Create a project directory namedreact-appwrite-auth and open it in your favorite code editor. Then run the following command that helps to scaffold(find a more simple word) a project for you.

# npmnpm create vite@latest# Yarnyarn create vite
Enter fullscreen modeExit fullscreen mode

You will then be prompted and asked to configure how you want your project to be configured. The screenshot below should direct you on what you should pick

Vite-Project-Scaffolding

Entering./ as the project name in Vite scaffolds the React project in your current terminal directory. ChooseReact from the framework options, and select theJavascript variant.
After creating the project,npm install fetches the necessary tools andnpm run dev starts a live development server for quick updates.

Now let us install Appwrite into our created React Project and configure the API keys

To install Appwrite, run the following command in the projects directory

  npminstallappwrite
Enter fullscreen modeExit fullscreen mode

Next, create a.env.local file in the project root directory and paste the following

VITE_APPWRITE_PROJECT_ID="PROJECT_ID"
Enter fullscreen modeExit fullscreen mode

To obtain yourPROJECT_ID, navigate to your project created on appwrite, in our case, navigate to thereact-appwrite-auth project, you will be able to see it here.
You can also navigate to thereact-appwrite-auth project dashboard >settings, you will be able to see your PROJECT_ID similar to the screenshot below

project-id

Then create aappwriteConfig.js file in thesrc folder of your React app and paste the following code

import{Account,Client}from"appwrite";//  Import Client from "appwrite"constclient=newClient();// Create a new Client instanceclient.setEndpoint("https://cloud.appwrite.io/v1").setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID)exportconstaccount=newAccount(client);// Export an Account instance initialized with the client
Enter fullscreen modeExit fullscreen mode

We have successfully created the Appwrite client by passing in the Project EndPoint and Project ID and then exporting the account instance that will be used later on in the project.

Building the User Interface(UI)

This section focuses on crafting UI components for our React app, prioritizing simplicity and functionality due to our authentication focus. We'll build four key components:

  1. Login Page: Initial access point for users to input credentials and log in
  2. Register Page: Allows new users to create accounts by providing essential details.
  3. Home Page (Private Route): Authenticated users' exclusive space, displaying user email and ID from Appwrite.
  4. The Navbar Component

Login Page

import{useState}from"react";import{Link}from"react-router-dom";import"./Login.css";constLogin=()=>{const[email,setEmail]=useState("");const[password,setPassword]=useState("");const[buttonLoading,setButtonLoading]=useState(false);consthandleLogin=async(e)=>{setButtonLoading(true)e.preventDefault();if(password===""||email===""){alert("Please fill in the field required")setButtonLoading(false)return}// appwrite Login functionality 👇return(<divclassName="loginPage"><h2>Login</h2><formonSubmit={handleLogin}><div><labelhtmlFor="email">Email:</label><inputtype="email"requiredid="email"value={email}onChange={(e)=>setEmail(e.target.value)}/></div><div><labelhtmlFor="password">Password:</label><inputtype="password"requiredid="password"value={password}onChange={(e)=>setPassword(e.target.value)}/></div><buttontype="submit">{buttonLoading?"Loading...":"Login"}</button><div>Donthaveanaccount?<Linkto="/register">Register</Link></div></form></div>);};exportdefaultLogin;
Enter fullscreen modeExit fullscreen mode
.loginPage>form>*:not(:last-child){margin-bottom:1rem;}
Enter fullscreen modeExit fullscreen mode

Register Page

import{useState}from"react";import{Link}from"react-router-dom";import"./Register.css";constRegister=()=>{const[username,setUsername]=useState("");const[email,setEmail]=useState("");const[password,setPassword]=useState("");const[confirmPassword,setConfirmPassword]=useState("");const[loadingStatus,setLoadingStatus]=useState(false);consthandleRegister=async(e)=>{e.preventDefault();try{// Call Appwrite function to handle user registrationif(password!==confirmPassword){alert("Passwords do not match");setLoadingStatus(false)return;}if(username===""||email===""||password===""||confirmPassword===""){alert("Please fill in all fields");setLoadingStatus(false)return;}// appwrite Register functionality 👇}catch(err){alert(err.message);}};return(<divclassName="registerPage"><h2>Register</h2><formonSubmit={handleRegister}><div><labelhtmlFor="username">Username:</label><inputtype="text"requiredid="username"value={username}onChange={(e)=>setUsername(e.target.value)}/></div><div><labelhtmlFor="email">Email:</label><inputtype="email"requiredid="email"value={email}onChange={(e)=>setEmail(e.target.value)}/></div><div><labelhtmlFor="password">Password:</label><inputtype="password"requiredid="password"value={password}onChange={(e)=>setPassword(e.target.value)}/></div><div><labelhtmlFor="confirmPassword">ConfirmPassword:</label><inputtype="password"requiredid="confirmPassword"value={confirmPassword}onChange={(e)=>setConfirmPassword(e.target.value)}/></div><buttontype="submit">{<buttontype="submit">{loadingStatus?"Loading...":"Register"}</button>}</button><div>Haveanaccount?<Linkto="/login">Login</Link></div></form></div>);};exportdefaultRegister;
Enter fullscreen modeExit fullscreen mode
.registerPage>form>*:not(last-child){margin-bottom:1rem;}
Enter fullscreen modeExit fullscreen mode

Home Page

import"./Home.css";constHome=()=>{return(<div><h1>HomePage</h1><p>ThispageisaProtectedPageandshouldonlybeseenbyAuthenticatedUsers</p></div>);};exportdefaultHome;
Enter fullscreen modeExit fullscreen mode

Navbar Component

import{Link}from"react-router-dom";import"./Navbar.css";import{useNavigate}from"react-router-dom";constNavbar=()=>{constnavigate=useNavigate();consthandleLogin=()=>{navigate("/login")}return(<nav><divclassName="navLogo">Logo</div><Linkto="/"className="navHomeLink">Home</Link><buttononClick={handleLogin}className="navLoginButton">Login</button></nav>);};exportdefaultNavbar;
Enter fullscreen modeExit fullscreen mode
nav{/* max-width: 768px; */margin-inline:auto;display:flex;align-items:center;justify-content:space-between;border-bottom:3pxsolidblack;}nav>.navLogo{font-size:2.5rem;}nav>.navHomeLink{font-size:1.4rem;}nav>button{background-color:transparent;border:1pxsolidgray;font-size:1.2rem;cursor:pointer;}
Enter fullscreen modeExit fullscreen mode

If you've followed the UI code snippets, you'll have a basic app that looks like this:

Gif UI

Set up React Context for User State Management

In your root directory, create acontext folder. Inside it, add a file namedUserAuthContext.jsx. Paste the provided code into this file to handle user authentication efficiently.

import{createContext,useEffect,useState}from"react";import{account}from"../appwriteConfig";exportconstUserAuthContext=createContext();exportconstUserProvider=({children})=>{const[user,setUser]=useState(null);const[isLoading,setIsLoading]=useState(true);// Track loading stateuseEffect(()=>{constfetchUserData=async()=>{try{constresponse=awaitaccount.get();// Fetch user datasetUser(response);// Set user data}catch(error){console.error("Error fetching user data:",error);}finally{setIsLoading(false);// Always set loading state to false after fetching}};fetchUserData();},[]);return(<UserAuthContext.Providervalue={{user,setUser,isLoading}}>{children}</UserAuthContext.Provider>);};
Enter fullscreen modeExit fullscreen mode

TheUserAuthContext component does the following:

  • Context Setup: Create a context namedUserAuthContext to hold user data and related functions.
  • UserProvider Component: This component provides user data and loading state to the children components.
  • Fetching User Data:useEffect fetches user data on component mount and updates state.
  • Providing Context:UserProvider exposes user data, update function, and loading state.
  • Wrapping Your Application: Wrap your app withUserProvider to make context accessible everywhere.

User Registration functionality

Let's build the user registration functionality by adding code to our existing Register ComponenthandleRegister function

consthandleRegister=async(e)=>{setLoadingStatus(true)e.preventDefault();try{// Call Appwrite function to handle user registrationif(password!==confirmPassword){alert("Passwords do not match");setLoadingStatus(false)return;}if(username===""||email===""||password===""||confirmPassword===""){alert("Please fill in all fields");setLoadingStatus(false);return;}// appwrite Register functionality 👇if(password.length<8){alert("Password must contain 8 characters");setLoadingStatus(false);return;}constpromise=account.create(ID.unique(),email,password,username);promise.then(function(response){console.log(response);// Successalert("Account Created Successfully 🚀");navigate("/login");},function(error){console.log(error);// Failurealert(error);});}catch(err){alert(err.message);}};
Enter fullscreen modeExit fullscreen mode

The updatedhandleSubmit function code above handles user registration with Appwrite. Here's a breakdown of its functionality

  • Password Validation: Checks if password length meets the minimum requirement (8 characters) and displays an error message if not.
  • Appwrite integration: Calls Appwrite'saccount.create function to register the user with provided credentials
  • Success Handling: Logs the successful registration response, displays a success message(alert), and redirects the user to the login page
  • Error Handling: Logs any errors encountered during registration and displays an error message to the user

User Login functionality

Let's build the user login functionality by adding code to our existing Login ComponenthandleLogin function

consthandleLogin=async(e)=>{setButtonLoading(true)e.preventDefault();if(password===""||email===""){alert("Please fill in the field required");setButtonLoading(false)return;}// appwrite Login functionality 👇// Call Appwrite function to handle login with email and passwordconstpromise=account.createEmailPasswordSession(email,password);promise.then(function(response){console.log(response);// SuccesssetUser(response);navigate("/")setButtonLoading(false)},function(error){console.log(error);// Failurealert(error.message)setButtonLoading(false);});};
Enter fullscreen modeExit fullscreen mode

ThehandleLogin updated function does the following:

  • Appwrite Login: Usesaccount.createEmailPasswordSession to attempt login with email and password
  • Success: Logs the user data, updates the state using thesetUser setter function, redirects the user to the homepage, and disables the loading indicator (setButtonLoading(false)).
  • Failure: In the case of a failure, it logs the errors, displays the error message as an alert, and disables the loading indicator

We have now finished the Authentication section. Let's proceed to make the Private routes

Private Routes

Go ahead to create a file namedPrivateRoute.jsx and then put the following code inside it

import{useContext}from"react";import{Outlet,Navigate}from"react-router-dom";import{UserAuthContext}from"../context/UserAuthContext";constPrivateRoute=()=>{const{user}=useContext(UserAuthContext);returnuser?<Outlet/>:<Navigateto={"/login"}/>;};exportdefaultPrivateRoute;
Enter fullscreen modeExit fullscreen mode

ThePrivateRoute component is responsible for the following:

  • The component utilizes theUserAuthContext created earlier to check if a user is currently logged in.
  • It retrieves theuser data from the context using theuseContext hook
  • If theuser data exists(logged in), it renders the child component wrapped by . This allows you to define protected routes within your application and the route is only accessible by logged-in users.
  • Ifuser isnull (not logged in), it redirects the user to the login page(/login) using theNavigate fromreact-router-dom.

Now let's us use thePrivateRoute component in theApp component
This is how the code will look like in our App component:

<Routeelement={<PrivateRoute/>}><Routepath="/"element={<Home/>}/></Route>
Enter fullscreen modeExit fullscreen mode

This allows us to simply usePrivateRoute component we've just created.
TheHome component which is a child component of thePrivateRoute component appears if and only if theuserobject is not null

User Persistence

User persistence refers to holding user data even when the page is refreshed. This ensures that the user remains logged in or their preferences remembered even after a browser refresh or session termination.

Appwrite by default usesLocal Storage for session management on Registering and Logging in users into our React applications

On refresh of theHome Page, the application is routed to theLogin page as the data from theUserAuthContext hasn't been fetched yet.
To prevent this, let us add this piece of code to theLogin page above thehandleLogin function that checks and redirects the users back to theHome page if the user object is not null or empty

import{useState,useContext,useEffect}from"react";import{Link}from"react-router-dom";import"./Login.css";import{account}from"../appwriteConfig";import{useNavigate}from"react-router-dom";import{UserAuthContext}from"../context/UserAuthContext";constLogin=()=>{const{setUser,user}=useContext(UserAuthContext)// console.log(user);constnavigate=useNavigate();const[email,setEmail]=useState("");const[password,setPassword]=useState("");const[buttonLoading,setButtonLoading]=useState(false);useEffect(()=>{if(user!==null){navigate("/");}},[user,navigate])consthandleLogin=async(e)=>{// Login logic. See above sections};
Enter fullscreen modeExit fullscreen mode

TheuseEFfect() hook simply checks if the user isnot null. If this evaluates to true, then the user is routed to the/ orHome page.

Handling User Logout

We would handle the User Logout in theNavbar component already created.

constNavbar=()=>{const{user,setUser}=useContext(UserAuthContext);constnavigate=useNavigate();consthandleLogin=()=>{navigate("/login");};consthandleLogout=async()=>{try{awaitaccount.deleteSession("current");setUser(null);navigate("/login");}catch(error){console.error(error);}};return(<nav><divclassName="navLogo">Logo</div><Linkto="/"className="navHomeLink">Home</Link>{user?(<buttononClick={handleLogout}className="navLoginButton">Logout</button>):(<buttononClick={handleLogin}className="navLoginButton">Login</button>)}</nav>);
Enter fullscreen modeExit fullscreen mode

In this UpdatedNavbar component, we are doing the following:

  • Conditional Rendering: We are conditionally rendering the Login and Logout buttons based on the currentuser value
  • Login: On click of the Login button, the user is routed to the/login page
  • Logout: On click of the Logout button, thehandleLogout() is triggered. The current user session is then deleted and the user is changed to null using the setter functionsetUser(null). The user is then routed to the/login page.

Here's a demo of how the app looks like:

App-UI-demo

Conclusion

In this article, we've explored and seen how to Authenticate users in our React apps using Appwrite. I'm delighted that you've followed along to this point.
Here is the github repo for this article incase you need to check it out,here

Lastly if you've found value in this article, please consider sharing it with your peers who may also benefit from it.

What are your thoughts on the topic of Authentication in React with Appwrite? Feel free to share your thoughts in the comments section below.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

FrontEnd React JS Developer || Open Source Lover || Building Products
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Developer
  • Joined

More fromEmore Ogheneyoma Lawrence

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp