Table of Contents
- Introduction
- React Project Setup with Appwrite
- Building the User Interface(UI)
- Set up React Context for User State Management
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.
Appwrite can be categorized as a Backend as a Service (BaaS) platform. It offers a range of backend functionalities such as authentication, database, storage, and server-side functions, which developers can utilize to build applications without needing to manage the backend infrastructure themselves.
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 with AppWrite
After registering, create a new project in appwrite by clicking on the Create a new project
button. You would then see a User Interface (UI) like this:
To get started, you'll need to give your project a name. We'll use react-appwrite-auth
for this example. Don't worry about the Project ID, it will be automatically generated for you when you click Next
On click of the Next
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 the Create
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 the Web
Platform
On Click of the Web Platform
we are presented with a screen in the screenshot below.
These two fields are required. Set the name as React Appwrite Auth
and the hostname as localhost
. Alternatively, you can use *
as your hostname.
We would then see two optional steps that guide us on how to Install the appwrite SDK into our app
and Initialize 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.
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 named react-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.
# npm
npm create vite@latest
# Yarn
yarn create vite
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
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
npm install appwrite
Next, create a .env.local
file in the project root directory and paste the following
VITE_APPWRITE_PROJECT_ID="PROJECT_ID"
To obtain your PROJECT_ID
, navigate to your project created on appwrite, in our case, navigate to the react-appwrite-auth
project, you will be able to see it here.
You can also navigate to the react-appwrite-auth
project dashboard > settings
, you will be able to see your PROJECT_ID similar to the screenshot below
Then create a appwriteConfig.js
file in the src
folder of your React app and paste the following code
import { Account, Client } from "appwrite"; // Import Client from "appwrite"
const client = new Client(); // Create a new Client instance
client
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID)
export const account = new Account(client); // Export an Account instance initialized with the client
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:
- Login Page: Initial access point for users to input credentials and log in
- Register Page: Allows new users to create accounts by providing essential details.
- Home Page (Private Route): Authenticated users' exclusive space, displaying user email and ID from Appwrite.
- The Navbar Component
Login Page
import { useState } from "react";
import { Link } from "react-router-dom";
import "./Login.css";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
try {
if (password === "" || email === "") {
alert("Please fill in the field required")
} else {
alert("Data Validated 🚀");
}
// Call Appwrite function to handle login with email and password
} catch (err) {
alert(err.message)
}
};
return (
<div className="loginPage">
<h2>Login</h2>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
required
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
type="password"
required
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button type="submit">Login</button>
<div>
Dont have an account? <Link to="/register">Register</Link>
</div>
</form>
</div>
);
};
export default Login;
.loginPage > form > *:not(:last-child) {
margin-bottom: 1rem;
}
Register Page
import { useState } from "react";
import { Link } from "react-router-dom";
import "./Register.css";
const Register = () => {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
try {
// Call Appwrite function to handle user registration
if (password !== confirmPassword) {
alert("Passwords do not match");
return;
}
if (
username === "" ||
email === "" ||
password === "" ||
confirmPassword === ""
) {
alert("Please fill in all fields");
return;
}
alert("Data is validated 🚀");
} catch (err) {
alert(err.message);
}
};
return (
<div className="registerPage">
<h2>Register</h2>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">Username:</label>
<input
type="text"
required
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
required
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
type="password"
required
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div>
<label htmlFor="confirmPassword">Confirm Password:</label>
<input
type="password"
required
id="confirmPassword"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<button type="submit">Register</button>
<div>
Have an account? <Link to="/login">Login</Link>
</div>
</form>
</div>
);
};
export default Register;
.registerPage > form > *:not(last-child) {
margin-bottom: 1rem;
}
Home Page
import "./Home.css";
const Home = () => {
return (
<div>
<h1>Home Page</h1>
<p>This page is a Protected Page and should only be seen by Authenticated Users</p>
</div>
);
};
export default Home;
Navbar Component
import { Link } from "react-router-dom";
import "./Navbar.css";
import { useNavigate } from "react-router-dom";
const Navbar = () => {
const navigate = useNavigate();
const handleLogin = () => {
navigate("/login")
}
return (
<nav>
<div className="navLogo">Logo</div>
<Link to="/" className="navHomeLink">Home</Link>
<button onClick={handleLogin} className="navLoginButton">Login</button>
</nav>
);
};
export default Navbar;
nav {
/* max-width: 768px; */
margin-inline: auto;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 3px solid black;
}
nav > .navLogo {
font-size: 2.5rem;
}
nav > .navHomeLink {
font-size: 1.4rem;
}
nav > button {
background-color: transparent;
border: 1px solid gray;
font-size: 1.2rem;
cursor: pointer;
}
If you've followed the UI code snippets, you'll have a basic app that looks like this:
Set up React Context for User State Management
In your root directory, create a context
folder. Inside it, add a file named UserAuthContext.jsx
. Paste the provided code into this file to handle user authentication efficiently.
import { createContext, useEffect, useState } from "react";
import { account } from "../appwriteConfig";
export const UserAuthContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true); // Track loading state
useEffect(() => {
const fetchUserData = async () => {
try {
const response = await account.get(); // Fetch user data
setUser(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.Provider value={{ user, setUser, isLoading }}>
{children}
</UserAuthContext.Provider>
);
};
Top comments (0)