useFirestoreDocument

The useFirestoreDocument hook is used to get or subscribe to a Firestore document using the useQuery hook.

import React from "react";
import { useFirestoreDocument } from "@react-query-firebase/firestore";
import { doc, DocumentSnapshot, DocumentData } from "firebase/firestore";
import { firestore } from "../firebase";

function Product({ id }: { id: string }) {
  const ref = doc(firestore, "products", id);

  // Provide the ref to the hook
  const query = useFirestoreDocument(["products", id], ref, {
    subscribe: true,
  });

  if (query.isLoading) {
    return <div>Loading...</div>;
  }

  const snapshot: DocumentSnapshot<DocumentData> = query.data;

  return <div>Product: {snapshot.data().name}</div>;
}

By default the query will peform a one-time get request, however by providing the subscribe flag, we can subscribe to any changes on our query in realtime.

If you just need the data of the returned query (rather than the DocumentSnapshot), use useFirestoreDocumentData instead.

TypeScript

By default the query returns a DocumentSnapshot whose data type is DocumentData. If you know the data type of your query documents, either provide the reference from a typed collection instance or directly to the hook.

type Product = {
  name: string;
};

// Option 1: Override the generic DocumentData type on the hook
const ref = doc(firestore, "products", id);
const query = useFirestoreDocument<Product>(["products", id], products);

// Option 2: Provide a converter
const productsCollection = collection(firestore, "products").withConverter<Product>(...);
const ref = doc(productsCollection, id);
const query = useFirestoreDocument("products", products);

// The query data on success will be successfully typed:
const snapshot: DocumentSnapshot<Product> = query.data;

useFirestoreDocumentData

The useFirestoreDocumentData hook is used to get or subscribe to a Firestore document using the useQuery hook, however instead the document data is returned rather than a DocumentSnapshot.

import React from "react";
import { useFirestoreDocumentData } from "@react-query-firebase/firestore";
import { doc, DocumentSnapshot, DocumentData } from "firebase/firestore";
import { firestore } from "../firebase";

function Product({ id }: { id: string }) {
  const ref = doc(firestore, "products", id);

  // Provide the ref to the hook
  const query = useFirestoreDocumentData(["products", id], ref, {
    subscribe: true,
  });

  if (query.isLoading) {
    return <div>Loading...</div>;
  }

  const data: DocumentData = query.data;

  return <div>Product: {data.name}</div>;
}

TypeScript

By default the query returns a type of DocumentData. If you know the data type of your query document, either provide the collection instance a converter or a type to the query:

type Product = {
  name: string;
};

// Option 1: Override the generic DocumentData type on the hook
const ref = doc(firestore, "products", id);
const query = useFirestoreDocumentData<Product>(["products", id], products);

// Option 2: Provide a converter
const productsCollection = collection(firestore, "products").withConverter<Product>(...);
const ref = doc(productsCollection, id);
const query = useFirestoreDocumentData("products", products);

// The query data on success will be successfully typed:
const data: Product = query.data;

Advanced Usage

Both the useFirestoreDocument and useFirestoreDocumentData hooks are similar in the arguments they accept, and both provide tight integration with the useQuery hook allowing you to perform some useful operations on the query data.

Cached vs Server documents

When querying a collection without subscribing to changes, you can specify a source parameter to indiciate whether the cached or server documents should be queried:

useFirestoreDocument(["products", id], ref, {
  subscribe: false, // or undefined
  source: "cache", // or 'server'
});

If no value is provided, a combination of cache and server will be used (as default).

Include metadata changes

If querying a document by subscribing, you can also subscribe to metadata changes.

useFirestoreDocument(["products", id], ref, {
  subscribe: true,
  includeMetadataChanges: true,
});

Modify the return type

If you're only interested in a specific part of the data, you can use the select option of the useQuery hook to only return specific parts of the data.

const query = useFirestoreDocumentData(["products", id], ref, {
  subscribe: true,
}, {
  select(data) {
    return data?.name;
  },
});

if (query.isSuccess) {
  console.log('Product name: ', query.data);
}

TypeScript users can also override the return type.

type Product = {
  name: string;
  price: number;
};

const query = useFirestoreDocumentData<Product, string | null>(["products", id], ref, {
  subscribe: true,
}, {
  select(data: Product): string | null {
    if (data) {
      return${data.price}`
    }

    return 'Sold out';
  },
});

if (query.isSuccess) {
  // query.data is a string
  console.log('Product price: ', query.data);
}