VueFire, a first class Vue and Firebase experience — including support for Nuxt, is now stable.
Building a better VueFire
Last year we announced that we were partnering with Eduardo San Martin Morote to build out a fully fledged Vue and Firebase experience. Since then, we’ve been at work building our features like Nuxt support and taking the library through alpha and beta stages. We’re happy to say that the library is now stable.
Idiomatic composables for realtime data and other Firebase services.
Today we are excited to announce that VueFire is now stable and ready for use.
In this blog we’ll be covering all the ways VueFire helps simplify Vue and Firebase development, but make sure to check out the documentation for a full breakdown.
What’s new in VueFire?
One-line Realtime Data Bindings
VueFire provides you with convenient composables to create realtime data bindings for both Firestore and the Realtime Database.
<script setup>
import { useCollection } from 'vuefire';
import { collection } from 'firebase/firestore';
// This is a composable. It only needs a ref to manage
// the subscription.
const todos = useCollection(collection(db, 'todos'));
</script>
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
<span>{{ todo.text }}</span>
</li>
</ul>
</template>
With just a few lines of code you have a todos
array that is kept in sync with database changes, local or remote.
Behind the scenes, VueFire relies on the Firebase SDK to do the heavy lifting with methods like onSnapshot()
and onValue()
, stripping away all the boilerplate code to produce a tailored developer experience for Vue developers.
Apart from useCollection()
(for Firestore), you will also find equivalents for documents with useDocument()
and for lists and objects (for the Realtime Database) with useDatabaseList()
and useDatabaseObject()
:
import { useDatabaseList, useDatabaseObject, useDatabase } from 'vuefire'
import { ref as dbRef } from 'firebase/database'
const db = useDatabase()
const contacts = useDatabaseList(dbRef(db, 'contacts'))
const someContact = useDatabaseObject(dbRef(db, 'contacts', '24'))
VueFire allows you to pass dynamic references to documents and collections and automatically unbinds the previous reference and binds the new one. For example, we can rely on the route params to bind to a specific document:
<script setup lang="ts">
import { useRoute } from 'vue'
import { useDocument } from 'vuefire'
import { doc } from 'firebase/firestore'
const route = useRoute()
// note the () => doc() to pass a dynamic reference
const contact = useDocument(() => doc(db, 'contacts', route.params.id))
</script>
<template>
<Contact :contact="contact" />
</template>
When navigating from /contacts/1
to /contacts/2
, the previous document will be unbound and the new one will be bound, all by just passing a function to a document instead of just the document. Note this also works with computed()
properties and ref()
variables.
Authentication made easier
VueFire takes underlying Firebase Authentication functions and wraps them into Vue composables, making it easy and idiomatic to use in your Vue apps.
<script setup>
import { useCurrentUser } from 'vuefire'
const user = useCurrentUser()
</script>
<template>
<p v-if="user">Hello {{ user.providerData.displayName }}</p>
</template>
The user
variable is reactive, so you can use it with other Composition API functions like a computed()
, or watch()
.
<script setup>
import { useCurrentUser } from 'vuefire'
import { useRouter } from 'vue-router'
const user = useCurrentUser()
watch(user, user => {
if (user == null) {
router.push('/login')
}
})
</script>
<template>
<!-- ... -->
</template>
The user variable will be null
if no user is authenticated, so you can watch it to redirect to the login page in just a few lines as shown above.
Staying true to the Firebase SDK
VueFire tries to not get in your way and lets you use the Firebase SDK as you would normally do. It provides APIs that are helpful in the context of Vue, but you can and should still use the Firebase SDK for many things such as transactions, batch writes, converters, and more.
While there are specific Vue integrations you still have access to Firebase references by manually calling doc()
, collection()
, and other methods from the Firebase SDK. We believe this helps us stay closer to the Firebase SDK and makes it more intuitive to use both at the same time. For example, you cannot use VueFire to write bound data, you must use the Firebase SDK for update operations:
<script setup lang="ts">
import { useFirestore } from 'vuefire'
import { doc, addDoc, serverTimestamp } from "firebase/firestore";
const db = useFirestore()
function addTodo(text: string) {
// ✨ add a new todo
await addDoc(collection(db, "todos"), {
text,
createdAt: serverTimestamp(),
done: false
});
}
</script>
VueFire doesn’t cover every Firebase service, nor does it try to. Most of the time an abstraction is not needed and the Firebase SDK is already enough. For example, you can use the Messaging service with the Firebase SDK directly:
<script setup>
import { useFirebaseApp } from 'vuefire'
import { getMessaging, onMessage } from 'firebase/messaging'
const firebaseApp = useFirebaseApp()
const messaging = getMessaging(firebaseApp)
onMessage(messaging, payload => {
console.log('Message received. ', payload)
})
</script>
There’s no Vue integration or VueFire specific code needed.
Nuxt support
One of the most exciting features of VueFire is its Nuxt Module. Not only does it take care of Firebase initialization but it also handles one of the most complex parts of integrating Firebase with Nuxt: Server Side Rendering, (SSR). In order to use VueFire’s Nuxt Module, install it with your favorite package manager:
npm install nuxt-vuefire
Then you can configure it in your Nuxt Config file:
export default defineNuxtConfig({
modules: [
// ... other modules
'nuxt-vuefire',
],
vuefire: {
// Firebase App configuration
config: {
apiKey: '...',
authDomain: '...',
projectId: '...',
appId: '...',
},
},
})
On top of that, VueFire’s Nuxt Module also makes it really easy to use features like AppCheck, Authentication, and Emulators.
App Check support
Firebase App Check is is an additional layer of security that helps protect access to your services by attesting that incoming traffic is coming from your app, and blocking traffic that doesn’t have valid credentials. It helps protect your backend from abuse, such as billing fraud, phishing, app impersonation, and data poisoning.
Enabling AppCheck is as simple as adding the appCheck
property to the vuefire
configuration object:
export default defineNuxtConfig({
// ...
vuefire: {
// Enables AppCheck
appCheck: {
// Allows you to use a debug token in development
debug: process.env.NODE_ENV !== 'production',
provider: 'ReCaptchaEnterprise',
key: '...',
},
},
})
By using the debug
option, you can use a debug token in development. This is useful to test locally without the Emulators, either during development or right before an important release.
Server and Client Authentication sync
Authentication sync via the client and server can be difficult to manage. Each machine needs to verify and maintain the same authentication context. This can require setting up cookies, background Cloud Functions, or other complex methods.
However, authentication is also a breeze to setup with VueFire’s Nuxt Module:
export default defineNuxtConfig({
// ...
vuefire: {
// Enables and initializes the auth module
auth: {
enabled: true,
},
},
})
By default, this won’t enable server side rendering pages that require authenticated users. In most cases, this is fine as you don’t need SEO for pages that are hidden behind authentication. This will also save you a lot of computing power.
However, it’s possible to enable this feature by letting the module mint a cookie for your authenticated users and use it on each request to authenticate them. Then, rendering the page as if the user was authenticated:
export default defineNuxtConfig({
// ...
vuefire: {
// Enables and initializes the auth module
auth: {
enabled: true,
sessionCookie: true,
},
},
})
Once setup, you can use the useCurrentUser()
like shown above. It’s automatically imported to make it even easier to use. You can also await for the user to be logged within middlewares with getCurrentUser()
:
export default defineNuxtRouteMiddleware(async to => {
const user = await getCurrentUser()
if (!user) {
// redirect to the login page
return navigateTo({
path: '/login',
query: {
// keep the original path as a query parameter
redirect: to.fullPath,
},
})
}
})
Then within your route, you can define the page metadata:
<script setup lang="ts">
definePageMeta({
middleware: ['authenticated'],
});
</script>
This allows you to easily protect routes from unauthenticated users, all with a few lines of code.
Emulator setup made easy.
The Firebase Emulator Suite is the recommended way of developing with Firebase. This allows you to develop against a local instance of Firebase services rather than dealing with a cloud connected project that requires a network connection or could have billing implications.
Normally, to use the emulators you’d need to write code to tell the Firebase SDK to connect to the Emulators rather than a cloud project. However, in the Nuxt Module this all happens automatically.
By default, if you have a firebase.json
file with the emulators
property, VueFire will automatically enable emulators during dev.
A brand new docs site.
The VueFire docs are live at https://vuefire.vuejs.org/.
The docs are open sourced within the main repository. If you want to see any changes or additions please let us know!
Give it a try today
Here at Firebase we are big fans of Vue and are so excited to simplify Vue and Firebase development with VueFire. Let us know what you think!