Vue 3 Firebase Firestore CRUD Web App Tutorial Using Composition API

by Didin J. on Jul 09, 2025 Vue 3 Firebase Firestore CRUD Web App Tutorial Using Composition API

Build a Firebase Firestore CRUD web app using Vue 3, Composition API, and Vite. Learn modern Vue practices with real-time updates and clean routing.

In this tutorial, you’ll learn how to build a modern CRUD (Create, Read, Update, Delete) web application using Vue 3, the Composition API, and Firebase Firestore. We’ll walk through setting up a Vue 3 project with Vite, integrating Firebase’s latest modular SDK, and using Firestore as a real-time NoSQL database.

The final application will allow users to:

  • Add new records to Firestore

  • Display a list of users/documents

  • Edit existing records

  • Delete records in real-time

We’ll also leverage Vue Router 4 for page navigation and the Composition API for more flexible and readable component logic.

🔧 Tech Stack & Tools

  • Vue 3 (with Composition API)

  • Vite (for fast development setup)

  • Firebase 10+ Modular SDK

  • Cloud Firestore (Firebase NoSQL database)

  • Vue Router 4

Whether you're new to Vue 3 or looking to upgrade an older Vue-Firebase app, this hands-on guide will help you understand how to work with Firestore using modern Vue practices.

Let’s get started by creating the Vue 3 project.


1. Setting Up the Vue 3 Project

We’ll use Vite to scaffold our Vue 3 application. Vite is a fast build tool that’s now the recommended standard over Vue CLI.

📦 Step 1: Create a Vue 3 Project

Make sure you have Node.js installed (preferably v22+). Then run:

npm create vite@latest vue3-firebase-crud -- --template vue

Follow the prompts:

  • Project name: vue3-firebase-crud

  • Framework: Vue

  • Variant: JavaScript (or TypeScript if preferred)

Then navigate into the project and install dependencies:

cd vue3-firebase-crud
npm install

▶️ Step 2: Run the Development Server

npm run dev

You should see the Vite development server running at http://localhost:5173.

Vue 3 Firebase Firestore CRUD Web App Tutorial Using Composition API - vite + vue

🧱 Project Structure Overview

After setup, your folder will look like this:

vue3-firebase-crud/
├── index.html
├── package.json
├── vite.config.js
├── public/
├── src/
│   ├── assets/
│   ├── components/
│   ├── views/
│   ├── App.vue
│   ├── main.js

We’ll organize our app using:

  • components/: Shared components

  • views/: Route-based pages (List, Add, Edit)

  • firebase.js: Firebase config and initialization

🔌 Step 3: Install Vue Router

We’ll need Vue Router to handle navigation between the user list, add, and edit pages.

Install it with:

npm install vue-router@4

We’ll configure it shortly in the next steps.


2. Installing Firebase and Initializing Firestore

To connect our Vue 3 app to Firebase and Firestore, we need to create a Firebase project and initialize it using the Firebase modular SDK (v10+).

🔧 Step 1: Create a Firebase Project

  1. Go to Firebase Console

  2. Click “Add project”, and follow the steps

  3. Disable Google Analytics (optional) and click Create Project

  4. After creation, click Web app (</>) to register your app

  5. Name it something like VueFirebaseCRUDApp

  6. Copy the Firebase config object – we’ll use it in the next step

📦 Step 2: Install Firebase SDK

In your project folder, install Firebase:

npm install firebase

📁 Step 3: Create the Firebase Configuration File

Create a new file in the src directory:

src/firebase.js

// Import Firebase modular SDK
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_PROJECT_ID.appspot.com",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

// Initialize Firestore
const db = getFirestore(app);

export { db };

🔒 Tip: Never commit your actual Firebase keys to public GitHub repos.

✅ Step 4: Verify Firestore Access

Go back to the Firebase console:

  1. Click on Firestore Database

  2. Click Create database

  3. Start in test mode for now (secure it later!)

  4. Choose your Firestore location and create the database

We’re now ready to set up Vue Router and start building the actual app views.


3. Vue Router Setup

We’ll use Vue Router 4 to manage navigation between three main views:

  • UserList.vue – display all users

  • AddUser.vue – add a new user

  • EditUser.vue – update existing user data

📁 Step 1: Create a Router File

Inside the src folder, create a new directory called router and a file named index.js:

src/router/index.js

import { createRouter, createWebHistory } from 'vue-router';
import UserList from '../views/UserList.vue';
import AddUser from '../views/AddUser.vue';
import EditUser from '../views/EditUser.vue';

const routes = [
  {
    path: '/',
    name: 'UserList',
    component: UserList
  },
  {
    path: '/add',
    name: 'AddUser',
    component: AddUser
  },
  {
    path: '/edit/:id',
    name: 'EditUser',
    component: EditUser,
    props: true
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

export default router;

🧩 Step 2: Add Views

Create a new folder named views inside src, and add placeholder components:

src/views/UserList.vue

<template>
  <div><h2>User List</h2></div>
</template>

src/views/AddUser.vue

<template>
  <div><h2>Add User</h2></div>
</template>

src/views/EditUser.vue

<template>
  <div><h2>Edit User {{ $route.params.id }}</h2></div>
</template>

⚙️ Step 3: Register the Router in main.js

Update the src/main.js file to include the router:

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

createApp(App).use(router).mount('#app');

🧭 Step 4: Add Navigation Links

Edit App.vue to add navigation and a <router-view />:

src/App.vue

<template>
  <div class="container">
    <nav>
      <router-link to="/">User List</router-link> |
      <router-link to="/add">Add User</router-link>
    </nav>
    <router-view />
  </div>
</template>

<style>
nav {
  margin: 1rem 0;
}
</style>

With routing in place, we’re ready to build the actual CRUD functionality using Firestore.


4. Building the Firestore CRUD App

We’ll create three views that interact with Firestore:

  • UserList.vue: fetch and display users

  • AddUser.vue: add a new user document

  • EditUser.vue: update or delete a user document

Let’s begin with the User model, then implement each component step by step.

🧾 Firestore Collection Structure

We’ll use a Firestore collection named users, where each document includes:

{
  "name": "John Doe",
  "email": "[email protected]"
}

✅ Step 1: AddUser.vue – Add New User

src/views/AddUser.vue

<template>
  <div>
    <h2>Add User</h2>
    <form @submit.prevent="addUser">
      <input v-model="name" placeholder="Name" required />
      <input v-model="email" placeholder="Email" required type="email" />
      <button type="submit">Add</button>
    </form>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { db } from '../firebase';
import { collection, addDoc } from 'firebase/firestore';
import { useRouter } from 'vue-router';

const name = ref('');
const email = ref('');
const router = useRouter();

const addUser = async () => {
  try {
    await addDoc(collection(db, 'users'), {
      name: name.value,
      email: email.value
    });
    router.push('/');
  } catch (error) {
    console.error("Error adding user:", error);
  }
};
</script>

📄 Step 2: UserList.vue – Display All Users

src/views/UserList.vue

<template>
  <div>
    <h2>User List</h2>
    <ul>
      <li v-for="user in users" :key="user.id">
        {{ user.name }} ({{ user.email }})
        <router-link :to="`/edit/${user.id}`">Edit</router-link>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { db } from '../firebase';
import { collection, onSnapshot } from 'firebase/firestore';

const users = ref([]);

onMounted(() => {
  const unsub = onSnapshot(collection(db, 'users'), (snapshot) => {
    users.value = snapshot.docs.map(doc => ({
      id: doc.id,
      ...doc.data()
    }));
  });
});
</script>

✅ This uses onSnapshot to auto-update in real-time whenever the Firestore collection changes.

✏️ Step 3: EditUser.vue – Update and Delete

src/views/EditUser.vue

<template>
  <div>
    <h2>Edit User</h2>
    <form @submit.prevent="updateUser">
      <input v-model="name" placeholder="Name" required />
      <input v-model="email" placeholder="Email" required type="email" />
      <button type="submit">Update</button>
      <button type="button" @click="deleteUser" style="margin-left: 1rem;">Delete</button>
    </form>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { db } from '../firebase';
import {
  doc,
  getDoc,
  updateDoc,
  deleteDoc
} from 'firebase/firestore';

const route = useRoute();
const router = useRouter();
const name = ref('');
const email = ref('');

const userRef = doc(db, 'users', route.params.id);

onMounted(async () => {
  const docSnap = await getDoc(userRef);
  if (docSnap.exists()) {
    const data = docSnap.data();
    name.value = data.name;
    email.value = data.email;
  }
});

const updateUser = async () => {
  await updateDoc(userRef, {
    name: name.value,
    email: email.value
  });
  router.push('/');
};

const deleteUser = async () => {
  await deleteDoc(userRef);
  router.push('/');
};
</script>

✅ Now your app supports full Firestore CRUD:

  • Real-time list of users

  • Add, edit, and delete users


5. Styling the App

You can use any styling method, but we’ll keep things simple with plain CSS and a minimal layout. Alternatively, you can enhance it using Tailwind CSS, which works well with Vite.

Option A: Basic CSS Styling (No dependencies)

You can add global styles  src/style.css or inline in App.vue.

src/App.vue (updated)

<template>
  <div class="container">
    <nav>
      <router-link to="/">User List</router-link>
      <router-link to="/add">Add User</router-link>
    </nav>
    <router-view />
  </div>
</template>

<style>
.container {
  max-width: 600px;
  margin: 2rem auto;
  font-family: system-ui, sans-serif;
}

nav {
  display: flex;
  gap: 1rem;
  margin-bottom: 2rem;
}

nav a {
  text-decoration: none;
  color: #2c3e50;
  padding: 0.5rem 1rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}

form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

input {
  padding: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}

button {
  width: fit-content;
  padding: 0.5rem 1.5rem;
  background: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>

✨ Option B: Use Tailwind CSS (Optional)

To use Tailwind, run:

npm install tailwindcss @tailwindcss/vite

Then configure tailwind.config.js:

import { defineConfig } from 'vite'
import vue from "@vitejs/plugin-vue";
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [vue(), tailwindcss()]
});

Add Tailwind to src/style.css:

@import "tailwindcss";

And import it in main.js:

import './style.css';

Then update components with Tailwind classes like:

<input class="border rounded p-2" />

You now have a clean, usable interface.


6. Testing the Application

✅ What to Test

🟢 Add User

  1. Navigate to /add or click the Add User link.

  2. Fill in the name and email fields.

  3. Click Add.

  4. ✅ You should be redirected to the user list and see the new user appear instantly.

🟢 Read Users

  1. On the home page (/), you should see a list of all users in the Firestore users collection.

  2. The list updates in real-time when changes occur.

🟢 Edit User

  1. Click the Edit link next to any user.

  2. The form will be pre-filled with that user’s info.

  3. Change the data and click Update.

  4. ✅ You should be redirected and see the updated information.

🟢 Delete User

  1. On the edit screen, click the Delete button.

  2. ✅ The user should be removed from Firestore and the list.

🧪 Tips for Local Testing

  • Network tab: Use DevTools → Network to ensure Firebase API calls succeed

  • Firestore console: View data in real time in the Firebase Console → Firestore

  • Error handling: Check the console for any errors during add, update, or delete operations

📱 Optional: Mobile Responsiveness

If you’re using Tailwind or flexible layouts, try resizing the browser window or testing on mobile to confirm responsiveness.


7. Conclusion

In this tutorial, you’ve built a complete Firestore CRUD web application using Vue 3, Composition API, Firebase 10+ modular SDK, and Vue Router 4. You’ve learned how to:

  • Scaffold a modern Vue 3 app using Vite

  • Integrate Firebase and initialize Firestore

  • Create views for adding, listing, editing, and deleting users

  • Use modular Firestore functions like addDoc, getDoc, updateDoc, deleteDoc, and onSnapshot

  • Apply basic or Tailwind styling for a clean, responsive UI

8. Key Takeaways

  • Use the Composition API to write cleaner, reusable logic in Vue 3.

  • The Firebase modular SDK allows tree-shaking and better performance.

  • Firestore onSnapshot enables real-time UI updates out of the box.

  • Vue Router 4 and Vite make development fast and smooth.

  • Always test all CRUD operations and handle errors gracefully.

With this foundation, you can extend the app to support user authentication, advanced filtering, pagination, or even deploy it using Firebase Hosting.

You can find the working source code on our GitHub.

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

Thanks!