How to create a React Firebase Project ⚛️ 🔥
Published: 2023-06-16 14:11:13

This post is a walkthrough on how to configure and develop a React application written in TypeScript, using a Firebase backend service.

We will touch upon some basic setup procedures for integrating Firebase into a React App, focusing on features such as registration, login, and authorization.

There is a link to the repository in the TL;DR section. This guide is intended as a setup for a meetup on how to use React and Firebase in a sample app. Essentially, it's a call to action to inspire you to create your own apps using React and Firebase, or even to explore other frameworks besides React (please note that we will not be discussing other frameworks in this blog post).

I hope you find building this application enjoyable and that it serves as a stepping stone for you to create your own apps using Firebase in the future.


What We Will Need:

- Node version of at least [LTS](https://en.wikipedia.org/wiki/Long-term_support#:~:text=September%202022) which is currently v16.18.0
- Your favorite text editor (I will be using [VSCode](https://code.visualstudio.com/))
- A Firebase account - we will set this up later, so relax and don't worry about it for now.
- Also make sure [Java](https://www.java.com/en/download/help/download_options.html) is installed on your machine; we need it to make the firebase emulator work.
- The latest version of [Vite](https://vitejs.dev/), which we will use to create a React project. Installing this will be covered later.

What Basic Knowledge This Blog Expects You to Have:

- Some basic command-line knowledge, including creating directories, changing directories, and running script commands.
- Some basic React and JS/TS knowledge is welcome, but we will cover some of the basics and provide explanations. Minimal knowledge is acceptable too. As long as you know what a frontend framework is and have some JavaScript knowledge, you will be fine.
- We will be using TypeScript but won't be explaining the typing, so some knowledge is a nice-to-have but not necessary to follow along.
- Some basic knowledge of Git will come in handy because some concepts will be mentioned but not explained.

Don't worry, this blog will explain how to set up Vite and create a Firebase project. Along the way, more dependencies will be added, and we will explain why we need them.

## TL;DR

For those of you who just want to check out the code:
The project can be found [here](https://github.com/kevinegstorf/firebase_basics_with_react).
And if you want to go step by step of creating the project just stick around and keep reading.

So lets get cracking!! 🐙


## Setting up Firebase

Go to this [link]('https://firebase.google.com/') and register for a Firebase account.
Once you've completed the registration, head to the Firebase console and click on the Add Project tile. You'll go through some steps to create a project.

In the first step, give your project a unique name. Please note that a project name is different from an app name. Later in the process, you'll provide a name for your app.

Next, you'll be prompted to decide if you want to use Google Analytics. We won't be discussing the Google Analytics feature in this blog, but feel free to select it and explore on your own. Firebase recommends its use, so that might be a good choice.

In the following step, select the Default Account for Firebase option and press Create Project. Once your project is created, you'll be redirected to your project console overview page.

For now, we're done with Firebase. We'll return to it after setting up a React project in the Configure Firebase in the React Project part of this blog.

## Creating a Fresh React Project with Vite

This blog won't delve deeply into [Vite](https://vitejs.dev/), but we will touch on how to create a boilerplate React project. This will save us a ton of time, allowing us to focus on building the good stuff. 

A little bit about Vite: as you can probably guess, it's a tool that quickly generates boilerplate projects for most popular frontend frameworks. It's pretty cool, to say the least. If you're interested in learning more, check out the [Vite webpage](https://vitejs.dev/).

First, let's install Vite globally, which will make creating boilerplate projects a breeze. Run the following command in your terminal to install Vite:


```bash 
npm i -g vite
```
The `-g` flag ensures that Vite is installed globally (making it usable by all users on your computer).

Next, navigate to the folder where you'd like to store the project and run the following command. Make sure to replace `<project-name>` with the name you've chosen for your project.

I've chosen to name my project __firebase_basics_with_react__, but remember to replace `<project-name>` in the command with your own project name.

`npm init vite@latest firebase_basics_with_react -- --template react-ts`

Once this process is completed, Vite will provide a prompt in the command line interface (CLI) that looks something like this:

```bash
Done. Now run:

  cd firebase_basics_with_react
  npm install
  npm run dev
```

Copy the provided command and paste it into your command line.

Now, navigate to `http://localhost:5173/` in your browser, and your Vite React project in TypeScript should be all set up. 

It's time to open your favorite text editor and start coding!


## Clean Up the React Project

Open the project in your text editor. We're going to remove some of the boilerplate to make the app feel more like our own. Navigate to the root folder of the project and open the `src` folder.

The contents should look a little like this:

```bash
src
> assets
App.css
App.tsx
index.css
main.tsx
vite-env.d.ts
```

First, clear all content in the `App.tsx` file and leave the rest for now. Everything will be updated in due time.

Next, create a `firebase` folder inside the `src` folder and add an `index.ts` file inside it.

## Configure Firebase in the React Project

Now that you have a Firebase Project and a fresh new React boilerplate project, it's time to combine the two and start our app-building adventure. Return to the Firebase console for your brand new project.

Select the button marked with closing brackets `</>`. This will take us to the section where we can create a _Web Application_.

Next, register your app and choose a name for it. For now, leave the hosting checkbox unchecked. We'll discuss this option later. Click the "Register App" button to proceed.

The next step is to add the Firebase SDK to our project. We'll use the `index.ts` file in the Firebase folder we created earlier for this purpose. 

We'll be using NPM for this, so we can follow the steps presented on the Firebase webpage. Head over to the command line, navigate to the root folder of your React app, and run the npm install command:


```bash
npm install firebase
```

Next, create a file called `.env` at the root of your project and define the following environment variables:


```txt
VITE_API_KEY=
VITE_AUTH_DOMAIN=
VITE_PROJECT_ID=
VITE_STORAGE_BUCKET=
VITE_MESSAGE_SENDER_ID=
VITE_APP_ID=
VITE_MEASUREMENT_ID=
```

If you look at the Firebase console in your browser, beneath the `npm install firebase` script, you'll find an example of how to initialize the Firebase app inside the React app.

The code snippet should look something like this:


```javascript
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "YOUR KEY",
  authDomain: "YOUR DOMAIN",
  projectId: "YOUR PROJECT ID",
  storageBucket: "YOUR STORAGE BUCKET",
  messagingSenderId: "YOUR MESSAGING SENDER ID",
  appId: "YOUR APP ID",
  measurementId: "YOUR MEASUREMENT ID"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
```

The values in the `firebaseConfig` object can be pasted into the corresponding variables in the `.env` file. Please note that the `.env` file should be added to the `.gitignore` file of your project if you plan to use a Git repository for your project. __Never push keys to an open source repo! In fact, never push keys to any repo for the sake of security!__

Now, let's insert the initialization code into our project. This code belongs in the `index.ts` file in the Firebase folder.


```typescript
import { initializeApp } from 'firebase/app';

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: import.meta.env.VITE_API_KEY,
  authDomain: import.meta.env.VITE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_MESSAGE_SENDER_ID,
  appId: import.meta.env.VITE_APP_ID,
  measurementId: import.meta.env.VITE_MEASUREMENT_ID,
};

const FireBaseApp = initializeApp(firebaseConfig);

export default FireBaseApp;
```

## Adding Routing

In this section, we'll focus on setting up routing, allowing us to navigate through different pages. The end result of this blog/app will include a sign-up and login page with a password reset email feature. When the user logs in, they'll be redirected to a homepage. That's the goal we're working towards, with some stylish embellishments provided by Tailwind.

Let's start by adding React Router to the project.

If you don't already have a terminal session open, start one now, navigate to your React project folder, and install the React Router package as a dependency by running this npm install command:

```bash
npm install react-router-dom
```

Once the installation is complete, let's head over to the `main.tsx` file where we'll modify the code. Currently, we have only one page with some Vite content running, but we want more than one page in our app. React Router can be configured in two ways:

1. We use `createBrowserRouter` inside the React render function, which takes an array of objects as an argument. These objects hold different paths and the corresponding components that render content when the route is requested by the user. The `createBrowserRouter` will then be passed to the `RouterProvider` component inside the React render function.

2. The alternative method is using `createRoutesFromElements` and passing this as an argument to the `createBrowserRouter` function. In this approach, you don't pass routes as objects that represent all routes in your app; instead, you can use React components.

Enough talking, here is the code snippet for the routing. Just paste it into the `main.tsx` file:


```typescript
import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import "./index.css";
import App from "./App";

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
]);

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

```

Let's proceed by creating two additional folders in our project: `pages` and `components`. Create both folders inside the `src` folder and include an `index.ts` file in each. We'll utilize [_Barrels_](https://basarat.gitbook.io/typescript/main-1/barrel) for importing components into another file. This approach will consolidate our imports into a single file from where we'll import pages and components into other parts of the application.

Your project folder structure should now resemble something like this:


```bash
src
> assets
> components
    index.ts
> pages
    index.ts
App.css
App.tsx
index.css
main.tsx
vite-env.d.ts
```

Our next step involves moving the `App.tsx` file into the `pages` folder and updating the `index.tsx` file inside the `pages` folder as follows:


```typescript
export { App } from "./App";
```

Don't forget to update the import of the `App` component inside the `main.tsx` file. Change it to `import App from "./pages/App";`.

We'll add a very basic component inside the `App.tsx` file to verify that everything works up to this point:


```typescript
const App = () => <div>Hello APP!</div>;

export default App;
```

The setup should now render `Hello APP!` in the browser. It might appear somewhat awkwardly positioned midway in the browser, but we'll address that in the next section.


## Adding Styling with Tailwind

Now it's time to set up the basics for styling. For this project, we'll be using Tailwind. Tailwind offers fast and easy styling options, which allows us to bypass the need for explaining a lot of CSS details that are not within the scope of this blog post. Tailwind provides classes that carry the styling for us, so by adding these classes to the HTML elements, we can update the styling without directly using CSS, as Tailwind has already taken care of that for us.

Let's get it set up and make this app shine! 💎✨🪩


[Tailwind docs](https://tailwindui.com/documentation) provide a comprehensive guide on setting up Tailwind, but we'll need to adjust the procedure a bit in our case.

This is due to Tailwind's dependency on PostCSS. PostCSS serves as a build system in Tailwind to generate CSS from its unique custom syntax. Additionally, we'll need Autoprefixer to ensure the CSS it generates is compatible with as many browsers as possible.

Armed with this knowledge, we need to do a bit more work, which involves installing a few extra packages and applying some configuration. So let's get to it!


```bash
npm install -D tailwindcss postcss autoprefixer
```

Next run:

```bash
npx tailwindcss init
```

It will add a `tailwind.config.js` file.

Update the file so it looks like this:

```javascript
/** @type {import('tailwindcss').Config} */
export default {
  content: [ "./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Also create a `postcss.config.js` file and add this:

```javascript
export default {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
}
```

Next step is updating the `index.css` file by pasting this code:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
background-color: #f0fdfa;
}
```

When we update the `App.tsx` file to this:

```typescript
const App = () => (
  <div className="min-h-screen flex justify-center items-center">
    <h1 className="text-3xl font-bold text-blue-600">
      Install & Setup Vite + React + Typescript + Tailwind CSS 3
    </h1>
  </div>
);

export default App;
```

The background should be slightly green, with text centered in the middle of the screen and displayed in a bold, sans-serif font, as shown below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l6f5cwe056w8k3m9axny.png)


Now this is done lets continue on the app itself.

## Setting up Firebase Authentication

Here's a brief overview of the user experience we're aiming for in our app once it's complete.

When a user visits the app, they will be presented with a signup form that includes three input fields: one for email, one for username, and one for password. This form will also feature a signup button and a link for logging in. Clicking this link will change the signup form to a login form, which will only require email and password fields as it's assumed that the user already has an account, thus eliminating the need for a username.

Once a user signs up or logs in with a valid username and password, they'll be redirected to a user homepage, where they'll be greeted with their username. Sounds super awesome, right? 🤩

First, let's enable authentication in Firebase for the app.

Go to the project overview page of your Firebase project on the web and select "Authentication". Press the "Get Started" button.

For now, we will only use email and password authentication, so select the "Email/Password" option. Enable both "Email/Password" and "Email Link" options, then click "Save".

Now, let's get coding!

Create an `AuthForm` folder component inside the `components` folder. Within that `AuthForm` folder, create an `index.tsx` file and paste the following code into it:


```typescript

import {
  getAuth,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  sendPasswordResetEmail,
  updateProfile,
} from "firebase/auth";
import { FormEvent, MouseEvent, useState } from "react";
import { useNavigate } from "react-router-dom";
import FireBaseApp from "../../firebase";
import { FirebaseError } from "firebase/app";

export const AuthForm = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [displayName, setDisplayName] = useState("");
  const [signUp, setSignUp] = useState(true);
  const [error, setError] = useState<string | undefined>();
  const auth = getAuth(FireBaseApp);
  const navigate = useNavigate();

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();
    if (signUp) {
      try {
        await createUserWithEmailAndPassword(auth, email, password);
        const user = auth.currentUser;
        if (user) {
          updateProfile(user, {
            displayName,
          });
        }

        navigate("/dashboard");
      } catch (error) {
        console.log(error);
      }
    }

    try {
      await signInWithEmailAndPassword(auth, email, password);
      navigate("/dashboard");
    } catch (error: unknown) {
      if (error instanceof FirebaseError) {
        setError(error.code);
      } else {
        setError("An error occurred");
      }
    }
  };

  const toggleSignUp = (event: MouseEvent<HTMLButtonElement>) => {
    event.preventDefault();
    setSignUp(!signUp);
  };

  const resetPassword = async () => {
    try {
      sendPasswordResetEmail(auth, email);
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <form
      onSubmit={(event) => handleSubmit(event)}
      className="bg-white p-6 rounded-lg shadow-xl"
    >
      <h1 className="text-center">{signUp ? "Sign Up" : "Sign In"}</h1>
      {signUp && (
        <>
          <label
            className="block text-gray-700 font-medium mb-2 text-left"
            htmlFor="username"
          >
            Username
          </label>
          <input
            type="username"
            name="username"
            value={displayName}
            onChange={(event) => setDisplayName(event.target.value)}
            className="w-full p-3 border border-gray-400 rounded-lg outline-teal-500"
            id="username"
            required
          />
        </>
      )}
      <div className="mb-4">
        <label
          className="block text-gray-700 font-medium mb-2 text-left"
          htmlFor="email"
        >
          Email
        </label>
        <input
          type="email"
          name="email"
          value={email}
          onChange={(event) => setEmail(event.target.value)}
          className="w-full p-3 border border-gray-400 rounded-lg outline-teal-500"
          id="email"
          required
        />
      </div>
      <div className="mb-6">
        <label
          className="block text-gray-700 font-medium mb-2 text-left"
          htmlFor="password"
        >
          Password
        </label>
        <input
          type="password"
          name="password"
          value={password}
          onChange={(event) => setPassword(event.target.value)}
          className="w-full p-3 border border-gray-400 rounded-lg outline-teal-500"
          id="password"
          required
        />
      </div>
      <p className="text-red-400">{error && error}</p>
      <button
        type="submit"
        className="bg-teal-500 transition duration-500 hover:bg-teal-600 text-white font-medium w-full p-3 rounded-lg"
      >
        {signUp ? "Register" : "Login"}
      </button>
      <div className="flex">
        <div className="flex-start ">
          <button
            className="mr-4 transition duration-500 hover:underline"
            onClick={(event) => toggleSignUp(event)}
          >
            {signUp ? "Sign In" : "Sign Up"}
          </button>
          <button
            className="transition duration-500 hover:underline"
            onClick={() => resetPassword()}
          >
            Forgot Password
          </button>
        </div>
      </div>
    </form>
  );
};
```

We also need a Barrel file to export the components from so inside the components folder create an index.ts for and paste this code in:

```typescript
export { AuthForm} from './AuthForm'
```

Let's zoom a bit in on some of the imports in this file so we can see what awesome functionality firebase/auth has for us that we use in the app.

```typescript
import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  sendPasswordResetEmail,
  updateProfile
} from 'firebase/auth';
```
________________



__getAuth__ we use on line 19 of the file, we pass the `FireBaseApp` as an argument. The `FireBaseApp` was created in the Firebase configuration file in the firebase folder of the app. So we pass the object with all the credentials of the app as an argument and store it inside the variable `auth`. This `auth` variable we need to call all the other functions we import from `firebase/auth`. So let's move on to the next one.

`__createUserWithEmailAndPassword__` - Well, I think you can guess what this callback function does. We use it in the submit handler of the signup part of the form. We pass the `auth` variable and the user email and password from the form input as arguments to persist this user in our Firebase Authentication part of the app. The next thing we do is add a display name by calling the `__updateProfile__` function and pass the user input field value to it.

The `__signInWithEmailAndPassword__` is used in the sign-in form to get the user from our firebase authorization database. We pass the `auth` variable and the email and password of the user to the function.

And Firebase also provides a nice password reset function which we use inside a resetPassword function on line 56. We just need to pass the `auth` variable and the users email address. Please note that the email could end up in the spam folder.

```jsx
const auth = getAuth(FireBaseApp);

```

Now add the form to the App.tsx file.
Your App.tsx file should look like this:

```typescript
import { AuthForm } from "../components";

const App = () => {
  return (
    <div className="grid place-items-center">
      <div className="w-3/6 max-md:w-full mt-5">
        <AuthForm />
      </div>
    </div>
  );
};

export default App;
```

And also update the `main.tsx` to add the dashboard route.

```typescript
import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import "./index.css";
import App from "./pages/App";
import Dashboard from "./pages/Dashboard";

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
  {
    path: "/dashboard",
    element: <Dashboard />,
  },
]);

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);
```

Let's add a new page and create a route for it. Inside the `pages` folder, create a new file called `Dashboard.tsx`, and paste the following code:


```typescript
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { getAuth } from "firebase/auth";
import { Navigation } from "../components";

export const Dashboard = () => {
  const auth = getAuth();
  const user = auth.currentUser;
  const navigation = useNavigate();

  useEffect(() => {
    if (user == null) {
      navigation("/");
    }
  }, [user, navigation]);

  return (
    <>
      <Navigation pageName="Dashboard" />
      <div className="container mx-auto m-5">
        <div className="grid grid-cols-1 gap-4">
          <div className="bg-white p-6 shadow-md mb-5 mt-5 transition duration-500 hover:shadow-xl rounded">
            <h2>
              Welcome:{" "}
              <span className="text-lg font-bold">{user?.displayName}</span>
            </h2>
            <hr className="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr>
            <h3>You are logged IN!!🔥🔥🔥🔥🔥</h3>
          </div>
        </div>
      </div>
    </>
  );
};

export default Dashboard;
```

We will also need a Navigation component.

Let's create a Navigation folder with an index.tsx file in it in the components folder and paste the following code:

```typescript
import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { getAuth, signOut } from "firebase/auth";

export const Navigation = ({ pageName }: { pageName: string }) => {
  const [signedIn, setSignedIn] = useState(true);
  const navigate = useNavigate();
  const auth = getAuth();

  const clickHandler = () => {
    setSignedIn(!signedIn);
    signOut(auth)
      .then(() => {
        navigate("/");
      })
      .catch((error) => {
        console.log(error);
      });
  };

  return (
    <nav className="bg-teal-300 p-4">
      <div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8 flex items-center justify-between">
        <div className="flex items-center">
          <Link to="/dashboard" className="text-white text-2xl font-bold">
            Tada!
          </Link>
        </div>
        <div className="ml-6 space-x-4 text-center text-white text-2xl font-bold">
          {pageName}
        </div>
        <div className="flex items-center">
          <button
            className="bg-teal-500 transition duration-100 hover:bg-teal-600 text-white font-bold py-2 px-4 rounded"
            onClick={() => clickHandler()}
          >
            {signedIn ? "Sign out" : "Sign in"}
          </button>
        </div>
      </div>
    </nav>
  );
};
```
Also add the navigation component to the components folder barrel file so the `index.ts` inside the components folder.
make sure it looks like this:

```typescript
export { AuthForm} from './AuthForm'
export { Navigation } from "./Navigation";
```

During the hackathon we will use a slightly different Dashboard page look and will be adjusted to match a Todo list app.


## Using the Firebase Emulator

Let's kick this one off by explaining what it is and why we need it. The Firebase Emulator allows us to develop locally, using our own local Firebase to test and develop new features. This means we don't interfere with our actual Firebase project. When we're happy with our new feature, we can push and deploy the code so in production, we actually use our live Firebase project. This helps us avoid polluting our Firebase project with testing data. We won't delve deeper into this topic in this blog but it will be covered during the workshop. We will need the Firebase Emulator when we start building collections and documents in the app.


## Firebase Hosting

Let's get this app on the web!
We need the Firebase toolkit to set up hosting. You'll also need it to run the emulator, but we can leave that bit for now.

To install the Firebase toolkit, we will do a global install like so:

```bash
    npm install -g firebase-tools
```

Once this is done, we need to initialize the app in order for the toolkit to work. In this process, we will connect the React app to the Firebase project on the web.

In the root of the project, run:

```bash
   firebase init
```

now you will see a CLI menu in this menu just select 
`Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub
Action deploys`

for now.

Now you will see a CLI menu. In this menu, just select Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys for now.

You can select it by using the arrow down key and select it by pressing the space bar. After this is done, press enter to continue to the next step and that is selecting the Firebase project.
Choose the option to select a project and pick your project by selecting it.
You can select a GitHub project, but it's optional and we will just deploy, so I selected no for now.
The next step is selecting a server where your app will be hosted, just choose one that is near you.

Now that the configuration is done, we are ready to deploy and we can do that by running this command.

```bash
firebase deploy
```

Now you should have deployed your very own app on the web!!

## Final words

Well done! You have successfully created a React App and added a Firebase project to it.
I hope you enjoyed this blog. I will probably add a follow-up after the meetup with more in-depth things explained, such how to use the Firestore database and create collections, and update and delete documents from it. So stay tuned and keep on coding! 🧑‍💻