1. Prerequisites

Before we begin, make sure you have the following installed on your machine:

  • Node.js v18+ — Download from nodejs.org
  • npm or yarn — Comes with Node.js
  • A Google account — For Firebase console access
  • VS Code (recommended) — With ESLint and Prettier extensions
â„šī¸
This tutorial uses React 18 with Vite as the build tool (faster than Create React App) and Firebase v10 (modular SDK).

2. Project Setup

Let's scaffold a new React project using Vite:

bash
# Create new Vite + React project
npm create vite@latest my-firebase-app -- --template react
cd my-firebase-app
npm install

# Install Firebase SDK
npm install firebase

# Start development server
npm run dev

Your project structure should look like this:

text
my-firebase-app/
├── src/
│   ├── components/
│   ├── firebase.js      ← Firebase config
│   ├── App.jsx
│   └── main.jsx
├── index.html
└── package.json

3. Firebase Configuration

Go to console.firebase.google.com, create a new project, then add a Web App. Copy your config object:

javascript
// src/firebase.js
import { initializeApp } from 'firebase/app';
import { getDatabase } from 'firebase/database';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT.firebaseapp.com",
  databaseURL: "https://YOUR_PROJECT-default-rtdb.firebaseio.com",
  projectId: "YOUR_PROJECT",
  storageBucket: "YOUR_PROJECT.appspot.com",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

const app = initializeApp(firebaseConfig);
export const db = getDatabase(app);
export const auth = getAuth(app);
âš ī¸
Never commit your Firebase config to a public GitHub repo. Use environment variables: .env.local with VITE_FIREBASE_API_KEY=... and access via import.meta.env.VITE_FIREBASE_API_KEY.

4. React Components

Create a simple task list component that reads and writes to Firebase:

jsx
// src/components/TaskList.jsx
import { useState, useEffect } from 'react';
import { db } from '../firebase';
import { ref, onValue, push, remove } from 'firebase/database';

function TaskList() {
  const [tasks, setTasks] = useState([]);
  const [input, setInput] = useState('');

  useEffect(() => {
    const tasksRef = ref(db, 'tasks');
    const unsubscribe = onValue(tasksRef, (snapshot) => {
      const data = snapshot.val();
      if (data) {
        const taskList = Object.entries(data).map(([id, val]) => ({
          id,
          ...val
        }));
        setTasks(taskList);
      } else {
        setTasks([]);
      }
    });
    // Cleanup listener on unmount
    return () => unsubscribe();
  }, []);

  const addTask = async () => {
    if (!input.trim()) return;
    await push(ref(db, 'tasks'), {
      text: input,
      createdAt: Date.now(),
      done: false
    });
    setInput('');
  };

  const deleteTask = async (id) => {
    await remove(ref(db, `tasks/${id}`));
  };

  return (
    <div className="task-container">
      <div className="task-input-row">
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && addTask()}
          placeholder="Add a new task..."
        />
        <button onClick={addTask}>Add</button>
      </div>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>
            <span>{task.text}</span>
            <button onClick={() => deleteTask(task.id)}>đŸ—‘ī¸</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TaskList;

5. CRUD Operations

Firebase Realtime Database uses these core functions from the modular SDK:

Read (Real-time listener)

javascript
import { ref, onValue, get } from 'firebase/database';

// Real-time listener (updates on every change)
onValue(ref(db, 'tasks'), (snapshot) => {
  console.log(snapshot.val());
});

// One-time read
const snapshot = await get(ref(db, 'tasks'));
console.log(snapshot.val());

Write, Update, Delete

javascript
import { ref, set, push, update, remove } from 'firebase/database';

// Create (auto-generated ID)
await push(ref(db, 'tasks'), { text: 'Buy groceries', done: false });

// Create (custom ID)
await set(ref(db, 'users/user123'), { name: 'Alfan', role: 'admin' });

// Update (partial)
await update(ref(db, 'tasks/-NxxxxID'), { done: true });

// Delete
await remove(ref(db, 'tasks/-NxxxxID'));
💡
Use push() for lists (tasks, posts, messages) — it generates a time-based unique key. Use set() when you want to define the path (e.g., user profile keyed by UID).

6. Adding Authentication

javascript
import { auth } from '../firebase';
import {
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signInWithPopup,
  GoogleAuthProvider,
  onAuthStateChanged,
  signOut
} from 'firebase/auth';

// Register with email
await createUserWithEmailAndPassword(auth, email, password);

// Login with email
await signInWithEmailAndPassword(auth, email, password);

// Google login
const provider = new GoogleAuthProvider();
await signInWithPopup(auth, provider);

// Listen to auth state
onAuthStateChanged(auth, (user) => {
  if (user) {
    console.log('Logged in:', user.uid, user.email);
  } else {
    console.log('Logged out');
  }
});

// Logout
await signOut(auth);

7. Deploy to Netlify

Build and deploy your app to Netlify in minutes:

bash
# Build the app
npm run build

# Install Netlify CLI globally
npm install -g netlify-cli

# Login and deploy
netlify login
netlify deploy --prod --dir=dist
✅
Add your Firebase environment variables in Netlify Dashboard → Site Settings → Environment Variables. This keeps your API keys secure and out of your codebase.
🎉

You've built your first React + Firebase app!

Continue learning with these next steps: