Build a Firestore CRUD App with React 19 and Firebase 10+

by Didin J. on Jul 16, 2025 Build a Firestore CRUD App with React 19 and Firebase 10+

Learn how to build a modern Firestore CRUD web app using React 19.1.0 and Firebase v10+. Includes React Hooks, Router v6, and Firebase modular API.

React and Firebase make a powerful combo for building real-time web apps. In this tutorial, weโ€™ll walk through building a Firestore CRUD app using the latest versions:

  • โš›๏ธ React 19.1.0 with Hooks & functional components

  • ๐Ÿ”ฅ Firebase v10+ using modular imports

  • ๐Ÿ›ฃ๏ธ React Router v6

  • โœ… No class components, no deprecated APIs

The result will be a clean and modern CRUD web application.

Prerequisites


1. Set Up Your React Project

Create the app using Vite (or CRA):

npm create vite@latest react-firebase-crud --template react
cd react-firebase-crud
npm install

Then install dependencies:

npm install firebase react-router-dom@6


2. Firebase Setup

2.1 Create Firebase Project

  1. Go to console.firebase.google.com

  2. Create a project, then:

    • Add a Web app

    • Copy the Firebase config

    • Enable Firestore in test mode

2.2 Create firebase.ts

// src/firebase.ts
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_PROJECT.appspot.com",
  messagingSenderId: "SENDER_ID",
  appId: "APP_ID",
};

const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);

3. App Structure

src/
โ”œโ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ BoardList.tsx
โ”‚   โ”œโ”€โ”€ BoardAdd.tsx
โ”‚   โ”œโ”€โ”€ BoardEdit.tsx
โ”‚   โ””โ”€โ”€ BoardShow.tsx
โ”œโ”€โ”€ types/
โ”‚   โ””โ”€โ”€ Board.ts
โ”œโ”€โ”€ firebase.ts
โ”œโ”€โ”€ App.tsx
โ””โ”€โ”€ main.tsx

Add those directories and files.

mkdir src/components
touch src/components/BoardList.tsx
touch src/components/BoardAdd.tsx
touch src/components/BoardEdit.tsx
touch src/components/BoardShow.tsx
mkdir src/types
touch src/types/Board.ts


4. Routing Setup

main.tsx

import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { BrowserRouter } from "react-router-dom";

createRoot(document.getElementById("root")!).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

App.tsx

import { Routes, Route, Link } from 'react-router-dom';
import BoardList from './components/BoardList';
import BoardAdd from './components/BoardAdd';
import BoardEdit from './components/BoardEdit';
import BoardShow from './components/BoardShow';

function App() {
  return (
    <div className="container">
      <h1>React Firebase Firestore CRUD</h1>
      <nav>
        <Link to="/">Boards</Link> | <Link to="/add">Add</Link>
      </nav>
      <Routes>
        <Route path="/" element={<BoardList />} />
        <Route path="/add" element={<BoardAdd />} />
        <Route path="/edit/:id" element={<BoardEdit />} />
        <Route path="/show/:id" element={<BoardShow />} />
      </Routes>
    </div>
  );
}

export default App;


5. CRUD Components

5.1 src/types/Board.ts

// src/types/Board.ts
export interface Board {
  id?: string;
  title: string;
  description: string;
}

5.2 src/components/BoardList.tsx

import { useEffect, useState } from "react";
import { db } from "../firebase";
import { collection, getDocs, deleteDoc, doc } from "firebase/firestore";
import { Link } from "react-router-dom";
import type { Board } from "../types/Board";

function BoardList() {
  const [boards, setBoards] = useState<Board[]>([]);

  useEffect(() => {
    const fetchBoards = async () => {
      const snapshot = await getDocs(collection(db, "boards"));
      setBoards(
        snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() } as Board))
      );
    };
    fetchBoards();
  }, []);

  const deleteBoard = async (id: string) => {
    await deleteDoc(doc(db, "boards", id));
    setBoards(boards.filter((board) => board.id !== id));
  };

  return (
    <div>
      <h2>Board List</h2>
      <ul>
        {boards.map((board) => (
          <li key={board.id}>
            <Link to={`/show/${board.id}`}>{board.title}</Link>{" "}
            <Link to={`/edit/${board.id}`}>Edit</Link>{" "}
            <button onClick={() => deleteBoard(board.id!)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default BoardList;

5.2 BoardAdd.jsx

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { db } from "../firebase";
import { collection, addDoc } from "firebase/firestore";

function BoardAdd() {
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");
  const navigate = useNavigate();

  const onSubmit = async (e: any) => {
    e.preventDefault();
    await addDoc(collection(db, "boards"), { title, description });
    navigate("/");
  };

  return (
    <form onSubmit={onSubmit}>
      <h2>Add Board</h2>
      <input
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Title"
        required
      />
      <textarea
        value={description}
        onChange={(e) => setDescription(e.target.value)}
        placeholder="Description"
        required
      />
      <button type="submit">Save</button>
    </form>
  );
}

export default BoardAdd;

5.3 BoardEdit.jsx

import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { db } from "../firebase";
import { doc, getDoc, updateDoc } from "firebase/firestore";
import type { Board } from "../types/Board";

function BoardEdit() {
  const [board, setBoard] = useState<Board | undefined>();
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();

  useEffect(() => {
    if (!id) return;

    const loadBoard = async () => {
      const snap = await getDoc(doc(db, "boards", id));
      setBoard(snap.data() as Board);
    };
    loadBoard();
  }, [id]);

  const onSubmit = async (e: any) => {
    if (!id || !board) return;

    e.preventDefault();
    await updateDoc(doc(db, "boards", id), { ...board });
    navigate("/");
  };

  return (
    <form onSubmit={onSubmit}>
      <h2>Edit Board</h2>
      <input
        value={board!.title}
        onChange={(e) => {
          if (!board) return;
          setBoard({ ...board, title: e.target.value });
        }}
        required
      />
      <textarea
        value={board!.description}
        onChange={(e) => {
          if (!board) return;
          setBoard({ ...board, description: e.target.value });
        }}
        required
      />
      <button type="submit">Update</button>
    </form>
  );
}

export default BoardEdit;

5.4 BoardShow.jsx

import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { db } from "../firebase";
import { doc, getDoc } from "firebase/firestore";
import type { Board } from "../types/Board";

function BoardShow() {
  const [board, setBoard] = useState<Board | undefined>();
  const { id } = useParams();

  useEffect(() => {
    if (!id) return;

    const fetchBoard = async () => {
      const snap = await getDoc(doc(db, "boards", id));
      setBoard(snap.data() as Board);
    };
    fetchBoard();
  }, [id]);

  return board ? (
    <div>
      <h2>{board.title}</h2>
      <p>{board.description}</p>
    </div>
  ) : (
    <p>Loading...</p>
  );
}

export default BoardShow;


Test It Out

npm run dev
  • Navigate to /add, create a board

  • See it listed on /

  • Click to view, edit, or delete

Build a Firestore CRUD App with React 19 and Firebase 10+ - Board List

Build a Firestore CRUD App with React 19 and Firebase 10+ - Board Details

Build a Firestore CRUD App with React 19 and Firebase 10+ - Add oard


Conclusion

Youโ€™ve now built a full-featured CRUD Firestore App using:

  • โœ… React 19.1.0

  • โœ… Firebase 10+ modular SDK

  • โœ… React Router 6

  • โœ… Functional components and hooks

This codebase is cleaner, modern, and future-ready.

You can find the full working source code on our GitHub.

That's just the basics. If you need more deep learning about React, you can take the following cheap course:

Thanks!