How to connect to Firebase in Nuxt3

Guideline on how to connect your firebase DB in Nuxt3

Remembers the struggles to find out how to connect Firebase in Nuxt 3 when it was just released. I realized this blog post might be able those who are in search of find a way to connect Firebase in Nuxt 3. In this post, I will go thru with you the steps to connect your Firebase DB in Nuxt 3.

Create Nuxt3 Project

Start off by creating a Nuxt 3 starter project by running the following command.

npx nuxi@latest init <project-name>

Install Firebase

npm install firebase

Setup Configuration

You will need to create .env in your root directory before making the configuration in nuxt.config.ts. runtimeConfig here will allow us to access the variables using the useRunTimeConfig API.

// in nuxt.config.ts
export default defineNuxtConfig({
  devtools: { enabled: true },
  css: ["~/assets/main.css"],
  runtimeConfig: {
    public: {
      FB_API_KEY: process.env.NUXT_FB_API_KEY,
      FB_AUTH_DOMAIN: process.env.NUXT_FB_AUTH_DOMAIN,
      FB_PROJECT_ID: process.env.NUXT_FB_PROJECT_ID,
      FB_STORAGE_BUCKET: process.env.NUXT_FB_STORAGE_BUCKET,
      FB_MESSAGING_SENDER_ID: process.env.NUXT_FB_MESSAGING_SENDER_ID,
      FB_APP_ID: process.env.NUXT_FB_APP_ID,
			FB_MEASUREMENT_ID: process.env.NUXT_FB_MEASUREMENT_ID,
    },
  },
});

Initialize Firebase

In order to initialize Firebase, you will need to create a Nuxt plugin in the plugins directory. defineNuxtPLugin is a helper function to define your Nuxt Plugin. This is to make the authentication instance and firestore instance available in Nuxt instance.

useRunTimeConfig is used to access runtime config variables that is setup in nuxt.config.ts.

// in /plugins/firebase.client.ts
import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig();

  const firebaseConfig = {
    apiKey: config.public.FB_API_KEY,
    authDomain: config.public.FB_AUTH_DOMAIN,
    projectId: config.public.FB_PROJECT_ID,
    storageBucket: config.public.FB_STORAGE_BUCKET,
    messagingSenderId: config.public.FB_MESSAGING_SENDER_ID,
    appId: config.public.FB_APP_ID,
    measurementId: config.public.FB_MEASUREMENT_ID,
  };

  const app = initializeApp(firebaseConfig);

  const auth = getAuth(app);
  const firestore = getFirestore(app);

  // need to intentionally return in provide so that it will
  // automatically typed safe it for developer
  return {
    provide: {
      auth,
      db: firestore,
    },
  };
});

Sample Data Structure

This is the sample data format used in this project.

// sample data in firestore
{
    "name": "Some famouse cereal",
    "id": "oiansda0onnad224jnaaso",
    "createdAt": {
        "seconds": 1694325417,
        "nanoseconds": 801000000
    }
}

What is useNuxtApp?

useNuxtApp is a built-in composable that provides a way to access shared runtime context of Nuxt, which is available on both client and server side. It helps you access the Vue app instance, runtime hooks, runtime config variables and internal states, such as ssrContext and payload.

Reading Data from Firestore

In this case, as you can see db is the firestore instance reference (after the dollar symbol ($)).

// in components/DisplayDocs.vue
<script setup lang="ts">
  import { onBeforeMount, ref } from "vue";
  import type { Ref } from "vue";
  import { useNuxtApp } from "nuxt/app";

  //firebase
  import { collection, getDocs, Timestamp } from "firebase/firestore";

  //lodash
  import orderBy from "lodash/orderBy";

  //interface
  interface Product {
    id: string;
    name: string;
    created_at: Timestamp;
  }

  //props

  //define
  const nuxtApp = useNuxtApp();
  const values: Ref<Product[]> = ref([]);

  //function
  const getData = async () => {
    try {
      const collectionName = collection(nuxtApp.$db, "products");
      const data = await getDocs(collectionName);
      data.docs.forEach((doc) => {
        values.value.push(doc.data() as Product);
      });
      values.value = orderBy(values.value, ["createdAt"], ["desc"]);
    } catch (error) {
      console.log(error);
    }
  };

  onBeforeMount(async () => {
    await getData();
  });
</script>

<template>
  <div class="py-7">
    <h1 class="text-4xl font-extrabold text-center mb-7">
      Firebase Read Collection
    </h1>
    <ul v-if="values.length > 0" class="flex gap-5 px-10 justify-center">
      <li v-for="item in values" class="py-3 px-5 bg-slate-200 rounded-md">
        <h2 class="text-2xl font-semibold">{{ item.name }}</h2>
      </li>
    </ul>
    <p v-else class="text-center">Loading...</p>
  </div>
</template>

Create Authentication Component

Similar to firestore instance. From here, we can get the authentication instance using useNuxtApp API. auth is the authentication instance reference after the dollar symbol ($).

// in components/Register.vue
<script setup lang="ts">
  import { ref } from "vue";
  import { useNuxtApp } from "nuxt/app";

  //firebase
  import { createUserWithEmailAndPassword } from "firebase/auth

  //define
  const nuxtApp = useNuxtApp();
  const email = ref("");
  const password = ref("");

  const createAccount = async (e: Event) => {
    e.preventDefault();
    if (!email.value || !password.value) {
      return;
    }
    try {
      console.log("creating");
      await createUserWithEmailAndPassword(
        nuxtApp.$auth,
        email.value,
        password.value
      );
      console.log("successfully create an account!");
    } catch (error) {
      console.error(error);
    }
  };
</script>
<template>
  <form
    class="max-w-xl mx-auto py-5 flex flex-col gap-3"
    @submit="createAccount"
  >
    <h1 class="text-4xl font-extrabold text-center mb-3">Register</h1>
    <input
      type="email"
      name="email"
      id="email"
      placeholder="Email"
      autocomplete="off"
      required
      v-model="email"
    />
    <input
      type="password"
      name="password"
      id="password"
      placeholder="**********"
      required
      v-model="password"
    />
    <button
      type="submit"
      class="bg-slate-500 text-white py-2 px-5 rounded-lg cursor-pointer"
    >
      Register
    </button>
  </form>
</template>
<style scoped>
  input {
    @apply border rounded p-2;
  }
</style>
// in Login.vue
<script setup lang="ts">
  import { ref } from "vue";

  //store
  import { useNuxtApp } from "nuxt/app";

  //firebas
  import { signInWithEmailAndPassword } from "firebase/auth";

  const nuxtApp = useNuxtApp();
  const email = ref("");
  const password = ref("");

  const signInUser = async (e: Event) => {
    e.preventDefault();
    if (!email.value || !password.value) {
      return;
    }
    try {
      await signInWithEmailAndPassword(
        nuxtApp.$auth,
        email.value,
        password.value
      );
      console.log("successfully logged in!");
    } catch (error) {
      console.error(error);
    }
  };
</script>
<template>
  <form class="max-w-xl mx-auto py-5 flex flex-col gap-3" @submit="signInUser">
    <h1 class="text-4xl font-extrabold text-center mb-3">Login</h1>
    <input
      type="email"
      name="email"
      id="email"
      placeholder="Email"
      autocomplete="off"
      required
      v-model="email"
    />
    <input
      type="password"
      name="password"
      id="password"
      placeholder="**********"
      required
      v-model="password"
    />
    <button
      type="submit"
      class="bg-slate-500 text-white py-2 px-5 rounded-lg cursor-pointer"
    >
      Login
    </button>
  </form>
</template>
<style scoped>
  input {
    @apply border rounded p-2;
  }
</style>

Source Code

You may find the complete template source code in https://github.com/eazypau/nuxt3-firebase-template.

Side note

Check out my portfolio website (https://www.eazypau.com/).

Resources

Using Firebase in Nuxt3 the Right Way