Using Firebase in Nuxt3 the Right Way

Client-side rendering Firebase Authentication and Firestore in Nuxt3 with ease, and an introduction to Nuxt3 Plugins.

Nishant Aanjaney Jalan
CodeX
5 min readDec 25, 2022

--

Photo by Pankaj Patel on Unsplash

I have always been a fan of Vue-style projects in web development, such as Vue 2, Vue 3 and Sveltekit. After the stable release of Nuxt3 in March 2022, I went head over heels for this framework. I have noticed that there aren’t many tutorials or articles that talk about integrating Firebase in Nuxt3.

Existing sources for Firebase integration with Nuxt3

When you search for this term, you would find some confusing (at least for me) and some outdated tutorials. Fireship.io talks about Firebase Auth with SSR (server-side rendering) in Nuxt3. This talks about signing in as a Firebase Administrator and creating Nitro API routes to receive access to features such as Authentication and Cloud Firestore. That is a good solution for SSR. However, in my opinion, it is not the easiest solution, especially for those who have not worked extensively with the Nitro server. You can have a look at my previous article on how to build a server with Nitro.

Nuxtjs website also talks about Firebase integration, however, it was for the old version — Nuxt2 — that does not support Vue 3’s Composition API.

My solution comes from the razorcx-courses’ GitHub repository — nuxt3-firebase-auth. They make use of plugins to provide instances of Auth and Firestore to the entire application.

Installing Firebase in your Nuxt3 Project

I believe that if you are reading this article, at this stage, you would know how to create a new Nuxt3 application

npx nuxi init my-web-app
cd my-web-app
npm i

npm i firebase

Create Firebase Plugin in your Nuxt3 Project

Your next step after a successful install is creating a plugin. In your root directory, create a folder called plugins (plural) and a typescript file in it called whatever you want it to be. I am going to call it firebase.client.ts. So, your project directory structure should look like this:

| node_modules/
| plugins/
| |- firebase.client.ts
|- .gitignore
|- app.vue
|- README.md
|- nuxt.config.ts
|- package-lock.json
|- package.json
|- tsconfig.json

In your firebase.client.ts file we can define a nuxt3 plugin by exporting the return object of the defineNuxtPlugin function.

export default defineNuxtPlugin()

This function takes a function parameter where the nuxtApp is passed as an argument.

export default defineNuxtPlugin(nuxtApp => {

})

From your Firebase console, copy and paste the code they provide to establish a connection.

import { initializeApp } from 'firebase/app'
import { getAuth } from "firebase/auth"
import { getFirestore } from 'firebase/firestore'
import { getAnalytics } from "firebase/analytics"

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

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

const app = initializeApp(firebaseConfig)

const analytics = getAnalytics(app)
const auth = getAuth(app)
const firestore = getFirestore(app)
})

To hide the important API keys and configuration, I have configured environment variables in my application.

Now most importantly, you need to attach the objects to the Nuxt3 Application for global usage.

import { initializeApp } from 'firebase/app'
import { getAuth } from "firebase/auth"
import { getFirestore } from 'firebase/firestore'
import { getAnalytics } from "firebase/analytics"

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

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

const app = initializeApp(firebaseConfig)

const analytics = getAnalytics(app)
const auth = getAuth(app)
const firestore = getFirestore(app)

nuxtApp.vueApp.provide('auth', auth)
nuxtApp.provide('auth', auth)

nuxtApp.vueApp.provide('firestore', firestore)
nuxtApp.provide('firestore', firestore)
})

Use the Firebase Instances in your Nuxt3 App

Now that you have provided the Auth and Firestore instances in your Nuxt3 application, we need to use them. You can do it in the <script setup /> part of your Vue files or create a composable that you can call.

Direct implementation

<script setup lang="ts">
import { createUserWithEmailAndPassword } from 'firebase/auth'

const creds = reactive({
email: "",
password: ""
})

const nuxtApp = useNuxtApp()
async function registerUser() {
try {
const { user } = await createUserWithEmailAndPassword(
nuxtApp.$auth,
creds.email,
creds.password
)
} catch (error: unknown) {
if (error instanceof Error) {
// handle error
}
}
}
</script>

<template>
...
</template>

Using Nuxt3 Composables (Recommended)

If you are unaware of using Composables in Nuxt3 here is a simple article that would brief you on it:

Firstly, let's create the composables root directory and name a file called useFirebaseAuth.ts.

| composables/
| |- useFirebaseAuth.ts
| node_modules/
| plugins/
|- .gitignore
|- app.vue
|- README.md
|- nuxt.config.ts
|- package-lock.json
|- package.json
|- tsconfig.json

Composable files can vary based on different programming styles. I prefer to export a default function that returns different states and properties.

import { createUserWithEmailAndPassword, User} from 'firebase/auth'

export default function() {
const { $auth } = useNuxtApp()

const user = useState<User | null>("fb_user", () => null)

const registerUser = async (email: string, password: string): Promise<boolean> => {
try {
const userCreds = await createUserWithEmailAndPassword($auth, email, password)
if (userCreds) {
user.value = userCreds.user
return true
}
} catch (error: unknown) {
if (error instanceof Error) {
// handle error
}
return false
}
return false
}

return {
user,
registerUser
}
}

Composable functions are a way to reuse the same code from different areas of your project. You are now not required to duplicate your code for the same functionality. To use this code, you have to invoke the function and call the method:

<script setup lang="ts">
const { registerUser } = useFirebaseAuth() // auto-imported

const creds = reactive({
email: "",
password: ""
})

async function handleRegistration() {
await registerUser(creds.email, creds.password)
}
</script>

<template>
...
</template>

As you can already see, this removes the mess from your Vue file and makes your code cleaner. Separating concerns in your codebase is highly important, as it promotes healthy teamwork habits, easier unit test cases and feasible debugging.

This article is part of the Nuxt3 Tutorial Series.

Nuxt3 Tutorial

8 stories
If you wish to read every article from me, consider joining the Medium 
program with this referral link.

Want to connect?

My GitHub profile.
My Portfolio website.

--

--

Nishant Aanjaney Jalan
CodeX
Editor for

Undergraduate Student | CS and Math Teacher | Android & Full-Stack Developer | Oracle Certified Java Programmer | https://cybercoder-naj.github.io