$ref โ
The $ref composable provides a lightweight way to access a Firestore document reactively through the browser's IndexedDB cache, automatically pulling from cache on mount.
๐ง Purpose โ
This utility allows your components to bind to a Firestore document reactively without making live queries every time. Instead, data is served from local IndexedDB and kept up to date automatically by syncing with Firestore in the background. Handles deduplication so you can call the same doc in many queries and only ever have one listener at a time.
๐งช API โ
Ref may only be used in a component lifecycle to avoid memory leaks.
$ref(docPath: string): ComputedRef<any>Parameters โ
docPath(string): The full path of the Firestore document you want to observe. Example:users/abc123.
Returns โ
- A
computedref that tracks the live value of the document from IndexedDB.
๐ก Usage โ
const user = $ref("users/abc123")
watchEffect(() => {
console.log("User data:", user.value)
})- โ Automatically pulls from cache
- ๐ Listens for updates via
$pool.listenToDocument - ๐งน Cleans up listeners on unmount
๐ง How It Works โ
Checks if the client is running (
import.meta.client). If not, returns a null ref.If a listener already exists for that document, reuses the existing one.
Otherwise:
- Initializes an IndexedDB entry using
useIDBKeyvalfrom@vueuse - Registers a Firestore listener via
$pool.listenToDocument - Sets up a Vue
computedref to track the IDB value - Automatically unsubscribes from the Firestore listener when the component is unmounted
- Initializes an IndexedDB entry using
๐ Server-Side Safety โ
If the utility is called in a server context (e.g., during SSR), it returns a computed(() => null) to avoid breaking hydration.
๐ฆ Dependencies โ
@vueuse/integrations/useIDBKeyval- Firebase
- Vue's
computed,ref, andonUnmounted
๐งผ Best Practices โ
- Use
$refwhen you want a single document to be tracked and reactive in your component - Combine with
$queryto build composite UIs from cached reactive collections - Don't rely on
$reffor one-off reads โ use$doc.get()instead for direct results
๐ ๏ธ Example Use Case โ
<script setup>
const currentInvoice = $ref("invoices/abc123")
</script>
<template>
<div v-if="currentInvoice">
<h2>{{ currentInvoice.title }}</h2>
<p>{{ currentInvoice.amount }} USD</p>
</div>
</template>๐ค Internal Notes โ
- Relies on
$poolfor subscription management listenersis a map shared across documents to avoid duplicate listeners- All document data is stored under the key
arkfire:<docPath>in IndexedDB
๐ Related โ
- [
$query] - for querying multiple documents as a collection - [
$doc] - for CRUD operations - [
$pool] - manages live listeners and cache sync