How to seamlessly Roll Auth into your React App using Firebase Authentication

Lets Comprehensively walk-through Firebase SDK setup, user registration, and storing data in Firestore.We'll Enhance your app's security and user experience with highlights on best practices.

October 23, 202410 min read

Introduction

Wrestling with authentication complexity? Want to ship your app in a weekend?
Worry no more because Firebase BaaS (Backend as a Service) has you covered.

Think about it: no more worrying about password hashing, token management, or setting up authentication servers. Instead of spending time building the main app functionality, you can leverage Firebase Authentication for a production-ready solution in minutes.

Why Firebase Authentication?

  • Security Out of the Box: Firebase handles sensitive operations like password hashing, token management, and secure session handling.
  • Simple Integration: With just a few lines of code, you get a production-ready authentication system.
  • Scalability: Built on Google’s infrastructure, it scales automatically with your user base.

Prerequisites

  • Basic React knowledge
  • Node.js installed
  • A Firebase account (free)
  • Familiarity with async/await and promises
  • Basic understanding of authentication concepts

What We'll Build

We'll create a complete authentication system featuring:

  • Email/password sign-up and sign-in
  • Password reset functionality
  • User authentication credentials stored in Firestore
  • Persistent login sessions and Authentication State

Try Kodaschool for free

Click below to sign up and get access to free web, android and iOs challenges.

Sign Up

Initial Setup and Configuration

  1. Create a Firebase Project
    • Navigate to the Firebase page and create a project with a name of your choice.
    • For this project, let’s disable Google Analytics.

Image
  • Allow a few minutes for the project to be created.
Image
  1. Configure Dashboard Services

  • On the side panel, under the Build dropdown, select:
    • Authentication
    • Firestore Database
Image
  1. Configure Authentication

Click on the Get Started button and enable the providers you need for authentication.

For our setup, we’ll only use the Email Provider.After selecting your provider, you’ll be navigated to the Sign-in Method page, which shows a list of your active authentication providers.

Image

After selecting your provider, you’ll be navigated to the Sign-in Method page, which shows a list of your active authentication providers.

Image
  1. Configure Firestore Database

  • Select Firestore Database from the side panel.
  • Click on the Create Database button.
Image
  • Let’s create a collection of users to store user information with an auto-generated ID.
Image

Ensure the database is in Test-mode (configures basic firestore rules):

Image

This Should be the Final Output:

Image

  1. Firestore Security Rules

// security rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read: if request.auth != null && request.auth.uid == userId;
      allow write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

service cloud.firestore { ... }

Defines service being as => Firestore.
match /databases/{database}/documents { ... }

Matches all documents in Firestore db.
match /users/{userId} { ... }

Specifically matches documents within the users collection, where {userId} its representing any user ID.

allow read: if request.auth != null && request.auth.uid == userId;

Allows read access to a user document only if the request is authenticated (request.auth != null) and the authenticated user's ID matches the document ID (request.auth.uid == userId).

allow write: if request.auth != null && request.auth.uid == userId;

Allows write access to a user document under the same conditions as read access:

  1. Register our Web-app in Firebase Project

Provide a name of your choice to your project.

Image

Copy the code in a code/text editor of your choice:

Image

Key Concepts in Authentication

Firebase provides a variety of functions to enable a custom and simplified authentication flow:

  • Creating New Users
    Functionality for registering users with an email and password.
  • Signing In
    Enables user login to access restricted features.
  • Password Reset
    Allows users to reset their password securely if forgotten.
  • Signing Out
    Lets users log out from their account and end the session.
  • Auth State Changes
    A way to listen to real-time authentication state changes, like login or logout, across the app.

Step 1. Creating New users

import { createUserWithEmailAndPassword } from 'firebase/auth';

const handleSignup=()=>{
  try {
    // Create auth account
    const userCredential = await createUserWithEmailAndPassword(auth, email, password);
    const user = userCredential.user;

    // Store additional user data in Firestore

    return user;
  } catch (error) {
    console.error("Error in signup process:", error);
    throw error; // Re-throw to handle in UI
  }

}
// Common Error Codes:
// auth/email-already-in-use
// auth/invalid-email
// auth/operation-not-allowed
// auth/weak-password


The createUserWithEmailAndPassword async function handles user registration in Firebase Auth by taking user credentials (auth, email, password) as arguments.

It creates a new user account in your Firebase project, sets up their initial password, and returns a Promise with the user's credentials. The auth object represents your initialized Firebase auth instance from the 'firebase/auth' , while email and password come from user input fields

step 2. Signing in Users

import { signInWithEmailAndPassword } from 'firebase/auth';

const handlesignin=()=>{
    try {
    // Create auth account
    const userCredential = awaitsignInWithEmailAndPassword(auth, email, password);
    const user = userCredential.user;

    // Store additional user data in Firestore

    return user;
  } catch (error) {
    console.error("Error in signin process:", error);
    throw error; // Re-throw to handle in UI
  }
}

// Common Error Codes:
// auth/invalid-email
// auth/user-disabled
// auth/user-not-found
// auth/wrong-password


The signInWithEmailAndPassword async function handles user authentication in Firebase by validating provided client side credentials (auth, email, password). It checks these against existing Firebase user accounts, and if matched, returns a Promise containing the user's credential object.

3.Password Reset

import { sendPasswordResetEmail } from 'firebase/auth';

const handleResetPassword = async () => {
    try {
      await sendPasswordResetEmail(auth, email);
     //email successfully sent to your inbox
    } catch (error) {
       const errorCode = error.code;
       const errorMessage = error.message;
    }
  };

The sendPasswordResetEmail async function manages Firebase's password recovery process. When triggered with your auth instance and the user's email, it automatically sends a reset link to that email address. This link gives users a secure way to create a new password. The function returns a Promise - if successful, the user gets a recovery email; if it fails (like with an invalid email), it throws an error that your code can catch and handle appropriately.

4.sign out

import { signOut } from 'firebase/auth';

const logout = () => {
      if (window.confirm("Are you sure you want to log out?")) {
        signOut(auth)
          .then(() => {
            console.log("User signed out");
        //navigate to get started page
          })
          .catch((error) => {
            console.error("Logout error:", error.message);
          });
      }
    };


The signOut function terminates the user's current session, clearing all authentication state. When invoked, it removes the user's tokens and returns a Promise that resolves once they're successfully logged out.

5.AuthState Changes(users state change)

import { onAuthStateChanged } from 'firebase/auth';
// Set up listener onAuthStateChanged(auth, (user) => { if (user) { // User object !== null } else { // User is signed out } });


The onAuthStateChanged real-time listener keeps track of user authentication status . It watches for any changes to the user's sign-in state (logging in, logging out, or token expiry) and triggers a callback function that lets you update your app accordingly.This helps maintain proper UI state -eg showing different content for logged-in versus logged-out users.

Firestore Concepts

Database Structure

  • Collections: Groups of documents, e.g., a 'users' collection organized like folders.
  • Documents: Individual data records that contain user data with unique IDs. Documents can also have subcollections.

Core Database Operations

  • Reading Data
    • getDoc: Reads a single document.
    • getDocs: Reads multiple documents.
    • Real-time listeners: Listens to changes in real-time to reflect updates immediately.
  • Writing Data
    • setDoc: Creates or overwrites documents.
    • addDoc: Adds new documents.
    • updateDoc: Updates existing documents.

Other Operations

  • DocumentSnapshot: Represents the state of a single document.
  • QuerySnapshot: Represents the state of a collection or query result, often as an array of DocumentSnapshot objects.
  • DocumentReference: A reference to a specific document in Firestore, used to perform operations like reading, writing, or deleting data.

Lets Ship something :-)

Lets initialize our project using vite

npm init vite@latest

Select React and Javascript option

Image

Ensure your src folder resembles this;

frontend/
├── src/
   ├── auth/                    # Auth components
   │   ├── resetpassword.jsx
   │   ├── signin.jsx
   │   ├── signinbtn.jsx
   │   └── signup.jsx
   │
   ├── context/                
   │   └── authctx.jsx
   │
   ├── navbar/                 
   │   ├── index.css
   │   ├── index.jsx
   │   └── RightMenu.jsx
   │
   ├── styles/                 
   │   ├── input.css
   │   └── output.css
   │
   ├── App.css                
   ├── App.jsx                 
   ├── error.jsx  # Fallback ui for non-existent routes  
   ├── errorboundary.jsx # Error boundary for React errors
   ├── firebase.js           
   ├── home.jsx              
   ├── index.css            
   └── main.jsx             

Global Files

Create a src/firebase.js file

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore, collection, addDoc,doc,getDoc } from 'firebase/firestore';

const firebaseConfig = {
  apiKey: "AIzaSy..........A",
  authDomain: "ivanp....o-..............",
  projectId: "ivan........,
  storageBucket: "iva.......com",
  messagingSenderId: "789..........3710",
  appId: "1:78977............9df"
};

export const app = initializeApp(firebaseConfig);
export const  auth =getAuth(app);
export const db =getFirestore(app);
export { collection, addDoc ,doc,getDoc};

Create a context for global statemanagement

authctx.jsx

import React, { createContext, useContext, useState, useEffect } from 'react';
import { auth } from '../firebase';

const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [isAuthLoading, setIsAuthLoading] = useState(true);

  useEffect(() => {
    // Add an observer for changes to the user's sign-in state
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setUser(user);
      setIsAuthLoading(false);
    });
    return unsubscribe;
  }, []);

  return (
    <AuthContext.Provider value={{ user, setUser, isAuthLoading }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

Parse the provider to our root file main.jsx

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import  './styles/output.css'
import { AuthProvider } from './context/authctx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
      <AuthProvider>
      <App />
      </AuthProvider>
  </StrictMode>,
)

ErrorBounary.jsx file fallback-ui for our react-errors

import  { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {

    console.log(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1 className='p-10 m-3 flex justify-center align-baseline'>Something went wrong.</h1>;
    }
    // eslint-disable-next-line react/prop-types
    return this.props.children;
  }
}

export default ErrorBoundary;

Error.jsx file for unmatched routes

import React from 'react'

const Error = () => {
  return (
   <>
   <div className="text-center py-12 mb-12 px-6">
	<h1 className="text-3xl lg:text-4xl mb-6 font-display text-black leading-tight">Sorry, this page doesn't exist</h1>
	<p className="max-w-lg mx-auto">We are sorry, but the page you are looking for cannot be found.</p>
   </div></>
  )
}

export default Error

Create an App.jsx for our Routes configuration:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Navbar from './navbar/index';
import Error from './error';
import { Home } from './home';
import { SignUpForm } from './auth/signup';
import { SignIn } from './auth/signin';
import { ResetPassword } from './auth/resetpassword';
import ErrorBoundary from './errorboundary';

// Create a new component for the container
const Container = ({ children }) => (
  <div className="container mx-auto px-4 py-8">{children}</div>
);

// Create a new component for the Navbar container
const NavbarContainer = () => (
  <div className="sticky top-0 z-50">
    <Navbar />
  </div>
);

function App() {

  return (
  <ErrorBoundary>
    <Router>
      <NavbarContainer />
      <Container>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/signup" element={<SignUpForm />} />
          <Route path="/signin" element={<SignIn />} />
          <Route path="/resetpassword" element={<ResetPassword />} />
          <Route path="*" element={<Error />} />
        </Routes>
      </Container>
    </Router>
  </ErrorBoundary>
  );
}

export default App;

Lets create home.jsx

import { useAuth } from './context//authctx';


export const Home = () => {
  const { user } = useAuth();
 
  return (
    <>
      <section className="bg-gray-100 py-24">
        <div className="grid max-w-7xl mx-auto grid-cols-1 md:grid-cols-2 items-center">
        {user ?  <p> `Welsome {user.email} to our Homepage ` </p>: <p>Loggin to view this page</p>}
        </div>
      </section>
    </>
  );
};

When the user is authenticated, this is the homepage one will see

Image

When the user is not logged in below will be his ui;

Image

Auth components in the Auth folder

Lets create the resetpassword.jsx

The resetPassword component
1. uses Firebase authentication for handling the password reset process
2. manages three pieces of state(email sent, messages state , error )

handleResetPassword:
- Calls Firebase's sendPasswordResetEmail function thus shows error or 200

messsage

import React, { useState } from 'react';
import { Typography, Button } from '@material-tailwind/react';
import { sendPasswordResetEmail } from 'firebase/auth';
import { auth } from '../firebase';

export const ResetPassword = () => {
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');
  const [error, setError] = useState('');

  const handleEmailChange = (e) => {
    setEmail(e.target.value);
  };

  const handleResetPassword = async () => {
    try {
      await sendPasswordResetEmail(auth, email);
      setMessage('Password reset email sent. Check your inbox.');
      setError('');
    } catch (error) {
      setError(`Error sending password reset email: ${error.message}`);
      setMessage('');
    }
  };

  return (
    <div className="fixed z-10 inset-0 flex items-center justify-center overflow-y-auto" role="dialog" aria-modal="true">
      <div className="inline-block bg-white rounded-lg px-4 pt-5 pb-2 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
        <div>
          <h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
            Reset Password
          </h3>
          <div className="mt-2">
            <input
              id="email"
              type="email"
              className="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
              placeholder="Email address"
              value={email}
              onChange={handleEmailChange}
            />
          </div>
        </div>
        <div className="mt-5 sm:mt-6">
          <Button
            variant="gradient"
            fullWidth
            onClick={handleResetPassword}
            className="bg-indigo-600 text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
          >
            Send reset link
          </Button>
        </div>
        {message && (
          <Typography variant="small" color="green" className="mt-4">
            {message}
          </Typography>
        )}
        {error && (
          <Typography variant="small" color="red" className="mt-4">
            {error}
          </Typography>
        )}
      </div>
    </div>
  );
};
Image

Lets create a signin.jsx

Signin component

State Management.:
- Tracks email, password, errors, loading state
- Shows/hides password visibility

Validation:
- Email validation using regex
- Password minimum length (8 characters)

Authentication:
- Uses Firebase's signInWithEmailAndPassword
- Shows loading spinner during authentication
- Handles success/error states

import React, { useState } from "react";
import {
  Card,
  CardHeader,
  CardBody,
  CardFooter,
  Typography,
  Input,
  Checkbox,
  Button,
} from "@material-tailwind/react";
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase";
import { useNavigate,Link } from "react-router-dom";
import { useAuth } from "../context/authctx";
import { FaEye, FaEyeSlash } from "react-icons/fa";
import { PuffLoader } from "react-spinners";

export const SignIn = () => {

  const [password, setPassword] = useState("");
  const [email, setEmail] = useState('');
  const [emailError, setEmailError] = useState("");
  const [passwordError, setPasswordError] = useState("");
  const [errorMessage, setErrorMessage] = useState("");
  const [showPassword, setShowPassword] = useState(false);
  const [isLoading, setIsLoading] = useState(false)

  const navigate = useNavigate();
  const { setUser } = useAuth();
  const MIN_PASSWORD_LENGTH = 8;


  const validateEmail = (email) => {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
  };


  const togglePasswordVisibility = () => {
    setShowPassword(!showPassword);
  };


  const handleForgotPassword = () => {
    // Add your forgot password logic here
    console.log("Forgot password clicked");
    navigate("/resetpassword");
  };


  const handlePasswordChange = (e) => {
    const value = e.target.value.trim();
    setPassword(value);
    setPasswordError(
      value.length >= MIN_PASSWORD_LENGTH
        ? ""
        : `Password must be at least ${MIN_PASSWORD_LENGTH} characters long`
    );
  };



  const handleEmailChange = (e) => {
    const value = e.target.value;
    setEmail(value);
    setEmailError(validateEmail(value) ? "" : "Invalid email format");
  };
  const handleLogin = () => {
    if (!validateEmail(email) || password.length < MIN_PASSWORD_LENGTH) {
      setErrorMessage(
        "Please enter a valid email and ensure the password is at least 8 characters long"
      );
      return;
    }
    setIsLoading(true);
    signInWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        const user = userCredential.user;
        console.log("User logged in:", user);
        alert("You have  successfully loggined in as ", user);
        setUser(user);
        navigate("/");
      })
      .catch((error) => {
        console.log("Login error:", error.message);
        setErrorMessage("Login failed: invalid credentials ");
      }).
      finally(() => {
        setIsLoading(false);
      });
  

  };

  return (
    <>
    <Card className="mx-auto w-full max-w-[24rem]">
      <CardHeader
        variant="gradient"
        color="gray"
        className="mb-4 grid h-28 place-items-center"
      >
        <Typography variant="h3" color="white">
          Sign In
        </Typography>
      </CardHeader>
      <CardBody className="flex flex-col gap-4">
      <Typography className="-mb-2" variant="h6">
          Email
        </Typography>
      <Input
          label="Email"
          size="lg"
          value={email}
          onChange={handleEmailChange}
          error={emailError}
        />
        <Typography className="-mb-2" variant="h6">
          Password
        </Typography>
        <Input
          label="Password"
          size="lg"
          type={showPassword ? "text" : "password"}
          value={password}
          onChange={handlePasswordChange}
          icon={
            showPassword ? (
              <FaEyeSlash onClick={togglePasswordVisibility} />
            ) : (
              <FaEye onClick={togglePasswordVisibility} />
            )
          }
        />
          {errorMessage && (
          <Typography variant="small" color="red">
            {errorMessage}
          </Typography>
        )}
        <div className="-ml-2.5">
          <Checkbox label="Remember Me" />
        </div>
      </CardBody>
      <CardFooter className="pt-0">
        <Button variant="gradient" onClick={handleLogin}  fullWidth>
          {isLoading ? (
            <PuffLoader color="#EBF0EF"  />
          ) : (
            'Sign In'
          )}
        </Button>
        <Typography
          variant="small"
          color="blue-gray"
          className="mt-2 cursor-pointer"
          onClick={handleForgotPassword}
        >
          Forgot password?
        </Typography>
        <Typography variant="small" className="mt-6 flex justify-center">
          Don&apos;t have an account?
          <Link
            to="/signup"
            variant="small"
            color="blue-gray"
            className="ml-1 font-bold"
          >
            Sign up
          </Link>
        </Typography>
      </CardFooter>
    </Card>
    </>
  );
}
Image

Here are Signup.jsx

Signup component

State Management:
- Tracks name, email, password, confirm password
- Similar password visibility toggle
- Loading state for form submission

Enhanced Validation:
- Email validation
- Password matching confirmation and length check
- Real-time error messages

Authentication + Database:
- Creates user with Firebase createUserWithEmailAndPassword
- Stores additional user data in Firestore database
- Handles success/error states

import React, { useState } from "react";
import {
  Card,
  CardHeader,
  CardBody,
  CardFooter,
  Typography,
  Input,
  Checkbox,
  Button,
} from "@material-tailwind/react";
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase";
import { useNavigate,Link } from "react-router-dom";
import { useAuth } from "../context/authctx";
import { FaEye, FaEyeSlash } from "react-icons/fa";
import { PuffLoader } from "react-spinners";

export const SignIn = () => {

  const [password, setPassword] = useState("");
  const [email, setEmail] = useState('');
  const [emailError, setEmailError] = useState("");
  const [passwordError, setPasswordError] = useState("");
  const [errorMessage, setErrorMessage] = useState("");
  const [showPassword, setShowPassword] = useState(false);
  const [isLoading, setIsLoading] = useState(false)

  const navigate = useNavigate();
  const { setUser } = useAuth();
  const MIN_PASSWORD_LENGTH = 8;


  const validateEmail = (email) => {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
  };


  const togglePasswordVisibility = () => {
    setShowPassword(!showPassword);
  };


  const handleForgotPassword = () => {
    // Add your forgot password logic here
    console.log("Forgot password clicked");
    navigate("/resetpassword");
  };


  const handlePasswordChange = (e) => {
    const value = e.target.value.trim();
    setPassword(value);
    setPasswordError(
      value.length >= MIN_PASSWORD_LENGTH
        ? ""
        : `Password must be at least ${MIN_PASSWORD_LENGTH} characters long`
    );
  };



  const handleEmailChange = (e) => {
    const value = e.target.value;
    setEmail(value);
    setEmailError(validateEmail(value) ? "" : "Invalid email format");
  };
  const handleLogin = () => {
    if (!validateEmail(email) || password.length < MIN_PASSWORD_LENGTH) {
      setErrorMessage(
        "Please enter a valid email and ensure the password is at least 8 characters long"
      );
      return;
    }
    setIsLoading(true);
    signInWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        const user = userCredential.user;
        console.log("User logged in:", user);
        alert("You have  successfully loggined in as ", user);
        setUser(user);
        navigate("/");
      })
      .catch((error) => {
        console.log("Login error:", error.message);
        setErrorMessage("Login failed: invalid credentials ");
      }).
      finally(() => {
        setIsLoading(false);
      });
  

  };

  return (
    <>
    <Card className="mx-auto w-full max-w-[24rem]">
      <CardHeader
        variant="gradient"
        color="gray"
        className="mb-4 grid h-28 place-items-center"
      >
        <Typography variant="h3" color="white">
          Sign In
        </Typography>
      </CardHeader>
      <CardBody className="flex flex-col gap-4">
      <Typography className="-mb-2" variant="h6">
          Email
        </Typography>
      <Input
          label="Email"
          size="lg"
          value={email}
          onChange={handleEmailChange}
          error={emailError}
        />
        <Typography className="-mb-2" variant="h6">
          Password
        </Typography>
        <Input
          label="Password"
          size="lg"
          type={showPassword ? "text" : "password"}
          value={password}
          onChange={handlePasswordChange}
          icon={
            showPassword ? (
              <FaEyeSlash onClick={togglePasswordVisibility} />
            ) : (
              <FaEye onClick={togglePasswordVisibility} />
            )
          }
        />
          {errorMessage && (
          <Typography variant="small" color="red">
            {errorMessage}
          </Typography>
        )}
        <div className="-ml-2.5">
          <Checkbox label="Remember Me" />
        </div>
      </CardBody>
      <CardFooter className="pt-0">
        <Button variant="gradient" onClick={handleLogin}  fullWidth>
          {isLoading ? (
            <PuffLoader color="#EBF0EF"  />
          ) : (
            'Sign In'
          )}
        </Button>
        <Typography
          variant="small"
          color="blue-gray"
          className="mt-2 cursor-pointer"
          onClick={handleForgotPassword}
        >
          Forgot password?
        </Typography>
        <Typography variant="small" className="mt-6 flex justify-center">
          Don&apos;t have an account?
          <Link
            to="/signup"
            variant="small"
            color="blue-gray"
            className="ml-1 font-bold"
          >
            Sign up
          </Link>
        </Typography>
      </CardFooter>
    </Card>
    </>
  );
}

Image

Lets create th reusable button

import React from 'react'
import { useNavigate } from "react-router-dom";
import { Button } from "@material-tailwind/react";

export const SignInBtn = () => {

    const navigate = useNavigate();

    const handleSignIn = () => {
      navigate("/signin");
    };
  
    
    return <Button onClick={handleSignIn}>Sign In</Button>;
}

Lets create our navbar components rom this repo

Ensure your tailwind config.js resembles this

import withMT from '@material-tailwind/react/utils/withMT'

export default withMT({
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      fontFamily: {
        play: ["Play", "sans-serif"],
      },
    },
  },
  daisyui: {
    themes: ["light"],
  },
  plugins: [require("daisyui")],
})

Ensure that your package.json resemble this :

{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview",
    "watch:tailwind": "tailwindcss -i ./src/styles/input.css  -o  ./src/styles/output.css --watch"
  },
  "dependencies": {
    "@ant-design/icons": "^5.2.6",
    "@material-tailwind/react": "^2.1.9",
    "ant": "^0.2.0",
    "antd": "^5.9.2",
    "axios": "^1.6.2",
    "firebase": "^9.23.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-icons": "^4.12.0",
    "react-router-dom": "^6.22.2",
    "react-spinners": "^0.13.8"
  },
  "devDependencies": {
    "@eslint/js": "^9.11.1",
    "@types/react": "^18.3.10",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.2",
    "autoprefixer": "^10.4.20",
    "daisyui": "^4.12.13",
    "eslint": "^9.11.1",
    "eslint-plugin-react": "^7.37.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.12",
    "globals": "^15.9.0",
    "postcss": "^8.4.47",
    "tailwindcss": "^3.4.14",
    "vite": "^5.4.8"
  }
}

Now lets coss our fingers and pray

npm run watch:tailwind

Lets start our server

npm run dev  

And it works feel free to share any contributions and tweaking with the ui here. Happy coding :- )

Barack Ouma

About Barack Ouma

Software Engineer | AWS Enthusiast | Tech Writer
Building and scaling cloud infrastructures.
Expert in AWS architecture, cost optimization, and security.
Passionate about simplifying tech through writing and hands-on solutions.
Driven to solve real-world problems with innovative and Quality Software.