Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Use Firebase Auth with Vue.js

To use Firebase Auth with Vue.js, install the Firebase SDK, initialize the auth module in a shared file, and create a Vue composable that wraps onAuthStateChanged in a reactive ref. This composable provides login, logout, and user state to any component through the Composition API, keeping auth logic centralized and your templates clean.

What you'll learn

  • How to initialize Firebase Auth in a Vue 3 project with modular SDK imports
  • How to build a reusable useAuth composable with reactive user state
  • How to implement Google sign-in and email/password auth flows
  • How to protect routes with a Vue Router navigation guard
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read15-20 minFirebase JS SDK v9+, Vue 3 with Composition API, Vite or Vue CLIMarch 2026RapidDev Engineering Team
TL;DR

To use Firebase Auth with Vue.js, install the Firebase SDK, initialize the auth module in a shared file, and create a Vue composable that wraps onAuthStateChanged in a reactive ref. This composable provides login, logout, and user state to any component through the Composition API, keeping auth logic centralized and your templates clean.

Building Firebase Authentication into a Vue.js App

Firebase Auth pairs well with Vue.js because both favor declarative patterns. This tutorial walks you through initializing Firebase in a Vite-based Vue 3 project, creating a composable that tracks the current user reactively, wiring up Google and email/password sign-in, and adding a navigation guard so only authenticated users can reach protected pages.

Prerequisites

  • A Firebase project with Authentication enabled in the Firebase Console
  • Google sign-in provider enabled under Authentication > Sign-in method
  • A Vue 3 project scaffolded with Vite (npm create vue@latest)
  • Node.js 18+ installed

Step-by-step guide

1

Install the Firebase SDK

Add the Firebase JavaScript SDK to your Vue project. The modular v9+ SDK supports tree-shaking, so only the code you import ends up in your production bundle. You will import specific functions from firebase/auth rather than loading the entire library.

typescript
1npm install firebase

Expected result: The firebase package appears in your package.json dependencies.

2

Create the Firebase configuration file

Create a dedicated file that initializes Firebase and exports the auth instance. Store your Firebase config values in environment variables prefixed with VITE_ so Vite exposes them to client code. Never hardcode API keys directly in source files.

typescript
1// src/lib/firebase.ts
2import { initializeApp } from 'firebase/app'
3import { getAuth } from 'firebase/auth'
4
5const firebaseConfig = {
6 apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
7 authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
8 projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
9 storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
10 messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
11 appId: import.meta.env.VITE_FIREBASE_APP_ID
12}
13
14const app = initializeApp(firebaseConfig)
15export const auth = getAuth(app)

Expected result: The auth instance is exported and ready for use across the app.

3

Build the useAuth composable

Create a composable that listens to Firebase auth state changes and exposes a reactive user ref, plus login and logout methods. The onAuthStateChanged listener fires when the user signs in, signs out, or when the page loads and Firebase restores the session. Unsubscribe from the listener when the component unmounts to prevent memory leaks.

typescript
1// src/composables/useAuth.ts
2import { ref, onMounted, onUnmounted } from 'vue'
3import { auth } from '@/lib/firebase'
4import {
5 onAuthStateChanged,
6 signInWithPopup,
7 GoogleAuthProvider,
8 signInWithEmailAndPassword,
9 createUserWithEmailAndPassword,
10 signOut,
11 type User
12} from 'firebase/auth'
13
14export function useAuth() {
15 const user = ref<User | null>(null)
16 const loading = ref(true)
17 let unsubscribe: (() => void) | null = null
18
19 onMounted(() => {
20 unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
21 user.value = firebaseUser
22 loading.value = false
23 })
24 })
25
26 onUnmounted(() => {
27 unsubscribe?.()
28 })
29
30 async function loginWithGoogle() {
31 const provider = new GoogleAuthProvider()
32 await signInWithPopup(auth, provider)
33 }
34
35 async function loginWithEmail(email: string, password: string) {
36 await signInWithEmailAndPassword(auth, email, password)
37 }
38
39 async function registerWithEmail(email: string, password: string) {
40 await createUserWithEmailAndPassword(auth, email, password)
41 }
42
43 async function logout() {
44 await signOut(auth)
45 }
46
47 return { user, loading, loginWithGoogle, loginWithEmail, registerWithEmail, logout }
48}

Expected result: The composable returns a reactive user ref that updates automatically on auth state changes.

4

Use the composable in a login component

Import the useAuth composable into a Vue component to display login buttons and the current user. Destructure the reactive refs and methods, then bind them to your template. The user ref updates reactively, so the template re-renders when the auth state changes.

typescript
1<script setup lang="ts">
2import { ref } from 'vue'
3import { useAuth } from '@/composables/useAuth'
4
5const { user, loading, loginWithGoogle, loginWithEmail, logout } = useAuth()
6const email = ref('')
7const password = ref('')
8</script>
9
10<template>
11 <div v-if="loading">Checking auth...</div>
12 <div v-else-if="user">
13 <p>Welcome, {{ user.displayName || user.email }}</p>
14 <button @click="logout">Sign out</button>
15 </div>
16 <div v-else>
17 <button @click="loginWithGoogle">Sign in with Google</button>
18 <form @submit.prevent="loginWithEmail(email, password)">
19 <input v-model="email" type="email" placeholder="Email" />
20 <input v-model="password" type="password" placeholder="Password" />
21 <button type="submit">Sign in</button>
22 </form>
23 </div>
24</template>

Expected result: The component shows a login form when the user is not authenticated and displays the user name after login.

5

Add a navigation guard for protected routes

Use Vue Router's beforeEach guard to redirect unauthenticated users away from protected pages. Since onAuthStateChanged is asynchronous, wrap the auth check in a Promise that resolves once Firebase finishes restoring the session. This prevents the guard from redirecting before Firebase has had a chance to load the cached session.

typescript
1// src/router/index.ts
2import { createRouter, createWebHistory } from 'vue-router'
3import { auth } from '@/lib/firebase'
4import { onAuthStateChanged } from 'firebase/auth'
5
6function getCurrentUser() {
7 return new Promise((resolve) => {
8 const unsubscribe = onAuthStateChanged(auth, (user) => {
9 unsubscribe()
10 resolve(user)
11 })
12 })
13}
14
15const router = createRouter({
16 history: createWebHistory(),
17 routes: [
18 { path: '/', component: () => import('@/views/Home.vue') },
19 { path: '/login', component: () => import('@/views/Login.vue') },
20 { path: '/dashboard', component: () => import('@/views/Dashboard.vue'), meta: { requiresAuth: true } }
21 ]
22})
23
24router.beforeEach(async (to) => {
25 if (to.meta.requiresAuth) {
26 const user = await getCurrentUser()
27 if (!user) return '/login'
28 }
29})
30
31export default router

Expected result: Navigating to /dashboard without being logged in redirects to /login automatically.

Complete working example

src/composables/useAuth.ts
1// src/composables/useAuth.ts
2import { ref, onMounted, onUnmounted } from 'vue'
3import { auth } from '@/lib/firebase'
4import {
5 onAuthStateChanged,
6 signInWithPopup,
7 GoogleAuthProvider,
8 signInWithEmailAndPassword,
9 createUserWithEmailAndPassword,
10 signOut,
11 type User
12} from 'firebase/auth'
13
14export function useAuth() {
15 const user = ref<User | null>(null)
16 const loading = ref(true)
17 const error = ref<string | null>(null)
18 let unsubscribe: (() => void) | null = null
19
20 onMounted(() => {
21 unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
22 user.value = firebaseUser
23 loading.value = false
24 })
25 })
26
27 onUnmounted(() => {
28 unsubscribe?.()
29 })
30
31 async function loginWithGoogle() {
32 try {
33 error.value = null
34 const provider = new GoogleAuthProvider()
35 await signInWithPopup(auth, provider)
36 } catch (err: any) {
37 error.value = err.message
38 }
39 }
40
41 async function loginWithEmail(email: string, password: string) {
42 try {
43 error.value = null
44 await signInWithEmailAndPassword(auth, email, password)
45 } catch (err: any) {
46 error.value = err.message
47 }
48 }
49
50 async function registerWithEmail(email: string, password: string) {
51 try {
52 error.value = null
53 await createUserWithEmailAndPassword(auth, email, password)
54 } catch (err: any) {
55 error.value = err.message
56 }
57 }
58
59 async function logout() {
60 try {
61 error.value = null
62 await signOut(auth)
63 } catch (err: any) {
64 error.value = err.message
65 }
66 }
67
68 return {
69 user,
70 loading,
71 error,
72 loginWithGoogle,
73 loginWithEmail,
74 registerWithEmail,
75 logout
76 }
77}

Common mistakes when using Firebase Auth with Vue.js

Why it's a problem: Querying Firestore or calling protected functions before onAuthStateChanged has fired, causing 'Missing or insufficient permissions' errors

How to avoid: Always wait for the loading ref to become false before making authenticated requests. Use the composable's loading state to gate data-fetching logic.

Why it's a problem: Not unsubscribing from onAuthStateChanged when the component unmounts, leading to memory leaks and stale callbacks

How to avoid: Store the unsubscribe function returned by onAuthStateChanged and call it in onUnmounted. The composable pattern handles this automatically.

Why it's a problem: Using signInWithRedirect instead of signInWithPopup and getting null from getRedirectResult due to browser cookie restrictions

How to avoid: Modern browsers block third-party cookies needed for redirect flows. Use signInWithPopup for web apps, or configure your authDomain to match your custom domain if redirect is required.

Why it's a problem: Forgetting to enable the sign-in provider in the Firebase Console, resulting in auth/operation-not-allowed errors

How to avoid: Go to Firebase Console > Authentication > Sign-in method and enable each provider you want to use. Google requires no extra config, but GitHub and others need client ID and secret.

Best practices

  • Initialize Firebase once in a shared module and export the auth instance — never create multiple app instances
  • Use the modular v9+ import syntax (import { getAuth } from 'firebase/auth') for tree-shaking
  • Store Firebase config values in VITE_ prefixed environment variables, not in source code
  • Wrap all auth operations in try/catch blocks and surface errors through a reactive error ref
  • Always unsubscribe from onAuthStateChanged in onUnmounted to prevent memory leaks
  • Use a navigation guard that waits for Firebase to restore the session before redirecting
  • For complex apps with many auth-dependent features, consider RapidDev to handle multi-provider auth flows, session management, and role-based access out of the box

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Vue 3 project using Vite and Firebase v9+. Show me how to create a useAuth composable that handles Google sign-in, email/password login, and logout with reactive user state. Include a Vue Router navigation guard that waits for onAuthStateChanged before redirecting unauthenticated users.

Firebase Prompt

Add Firebase Authentication to my Vue 3 app. I need a composable with Google sign-in and email/password flows, a login page with both options, and a route guard that redirects to /login if the user is not authenticated. Use Firebase modular SDK v9+ imports.

Frequently asked questions

Does Firebase Auth work with Vue 2?

Yes. The Firebase SDK is framework-agnostic, so it works with Vue 2. However, you cannot use the Composition API composable pattern. Instead, call onAuthStateChanged in your component's created hook and store the user in the data() object.

How do I persist auth state across page reloads in Vue?

Firebase Auth persists the session in the browser by default using IndexedDB. When the page reloads, onAuthStateChanged fires with the cached user. You do not need Pinia or localStorage for session persistence.

Can I use signInWithRedirect instead of signInWithPopup?

You can, but redirect flows are fragile in modern browsers due to third-party cookie restrictions. If you must use redirect, set your authDomain to your custom domain and serve the Firebase __/auth/ handler from that domain. For most Vue apps, signInWithPopup is simpler and more reliable.

How do I add role-based access control with Firebase Auth in Vue?

Use Firebase custom claims set via the Admin SDK on your server. After setting a claim like { role: 'admin' }, access it in Vue via user.getIdTokenResult() and check tokenResult.claims.role. Use this in your navigation guard to restrict routes by role.

Why does user.value return null even after signing in?

The most common cause is checking user.value before onAuthStateChanged has fired. Always wait for the loading ref to become false before reading the user state. If loading is false and user is still null, verify the sign-in provider is enabled in the Firebase Console.

Do I need to install a separate package for Google sign-in?

No. The Google sign-in provider is built into the firebase/auth module. Just import GoogleAuthProvider and call signInWithPopup. No additional packages are required for web apps.

How do I handle the auth/popup-blocked error?

Browsers block popups that are not triggered by a direct user action. Make sure signInWithPopup is called inside a click event handler, not inside an async callback or setTimeout. If the popup is still blocked, fall back to signInWithRedirect.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.