Firebase + React Boilerplate Setup

Sam Shapiro
4 min readAug 20, 2023

--

Setting up a brand new Firebase project that works seamlessly out of the box with a React front-end was more painful than I anticipated, so I made this list of instructions for spinning up a new Firebase+React project as much for myself as for anyone else.

This guide also includes settings to allow for local development with hot-reloading and connection to the Firebase Emulators, as I detailed in this post 3.5 years ago.

Note this uses the Web Modular API, not the Web Namespaced API. As of this writing (August 20th, 2023) both are still documented in Firebase docs, so for building on top of this boilerplate you’ll want to make sure to follow Web Modular API documentation.

  1. Initialize the React App & Firebase project.
npx create-react-app .
firebase init
# Select: firestore, functions, hosting
# Select: Create new project

2. Enable Firestore:

Open up the Firebase Console UI, select your newly created project, navigate to Firestore and click Create Database (start in test mode).

3. Create the Firebase Web App for your project:

firebase init # need to re-run after enabling database
# Select: firestore, functions, hosting
# Accept all defaults
# Do you want to install dependencies with npm now? Yes
# What do you want to use as your public directory? build
# Configure as a single-page app (rewrite all urls to /index.html)? No

firebase apps:create
# Please choose the platform of the app: Web
# This will spit out a new command out at the end, which you should then run:

firebase apps:sdkconfig WEB 1:abcdefghijk…
# This will spit out a firebase config to use in the next step below.

npm install firebase

4. In the src directory, create a file called firebase.js using the config that was spat out from the last firebase command in the previous step.

import { initializeApp } from "firebase/app";

// https://firebase.google.com/docs/functions/local-emulator#instrument-functions
import { getFunctions, connectFunctionsEmulator } from "firebase/functions";

// https://firebase.google.com/docs/emulator-suite/connect_firestore#android_apple_platforms_and_web_sdks
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";

const firebase = initializeApp({
projectId: "",
appId: "",
storageBucket: "",
apiKey: "",
authDomain: "",
messagingSenderId: "",
});
const functions = getFunctions(firebase);
const firestore = getFirestore(firebase);

if (window.location.hostname === "localhost") {
console.log(
"Testing locally: hitting local functions and firestore emulators."
);
connectFunctionsEmulator(functions, "127.0.0.1", 5001);
connectFirestoreEmulator(firestore, "127.0.0.1", 8080);
}

export { functions, firestore };

5. In firebase.json update the hosting.public value to build so that the deployed app serves the compiled build files outputted from react build .

{
// ...
"hosting": {
"public": "build",
// ...
}
}

6. Update functions/index.js with a boilerplate function to test and verify the front-end can properly call functions and interact with the database through them.

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { FieldValue } = require("firebase-admin/firestore");

admin.initializeApp();

exports.addUser = functions.https.onCall((data, context) => {
const { name } = data;

const db = admin.firestore();

return db
.collection("users")
.add({
name,
signupTimestamp: FieldValue.serverTimestamp(),
})
.then(() => {
return { message: "successfully added user", success: true };
})
.catch((error) => {
throw new functions.https.HttpsError("unknown", error.message, error);
});
});

7. Update App.js to a simple React app that allows you to add users to the database.

import logo from "./logo.svg";
import "./App.css";

import { useState } from "react";
import { functions } from "./firebase";
import { httpsCallable } from "firebase/functions";

function App() {
const [name, setName] = useState(""); // State to hold the input value
const [response, setResponse] = useState(""); // State to hold the response message

const handleAddUser = async () => {
try {
const addUserFunction = httpsCallable(functions, "addUser");
const result = await addUserFunction({ name });

if (result.data.success) {
setResponse(result.data.message);
setName(""); // Clear the name field on successful addition
} else {
setResponse("Failed to add user.");
}
} catch (error) {
setResponse("An error occurred.");
console.error(
"There was an error calling the addUser Firebase function",
error
);
}
};

return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>

<div className="user-input-section">
<input
type="text"
placeholder="Enter name..."
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button onClick={handleAddUser} disabled={!name.trim()}>
Add User
</button>
</div>

<h2>{response}</h2>
</header>
</div>
);
}

export default App;

8. Test Locally:

In one terminal, run firebase emulators:start

In a second terminal, run npm start

Now your local web app should open up, and you should be able to verify the users were added in the Firebase Emulator Suite UI: http://127.0.0.1:4000/firestore/data/users/

9. Deploy and test in production:

# Compile build files in the build directory.
npm run build

# Deploy
firebase deploy

Now go to the deployed web app and test adding a few users, then verify they are added in your production Firestore database from the Firebase web console.

Hope this helps!

Note: the code above can also be found in a boilerplate repo I made here: https://github.com/sambshapiro/firebase-react-boilerplate. However, everything you need to accomplish this is included in this article.

--

--