npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@manufac/firebase

v1.0.4

Published

A typed Firebase utility library for React applications. Provides type-safe Firestore CRUD operations, real-time subscriptions, Firebase Storage helpers, and authentication — all wired through a React context provider.

Readme

@manufac/firebase

A typed Firebase utility library for React applications. Provides type-safe Firestore CRUD operations, real-time subscriptions, Firebase Storage helpers, and authentication — all wired through a React context provider.

Setup

1. Define your collection schema

Use Zod to define schemas for each collection. Extend BaseDataSchema to inherit the required id, createdAt, and updatedAt fields.

// schema.ts
import { z } from "zod";
import { BaseDataSchema } from "@manufac/firebase";

export const ProductSchema = BaseDataSchema.extend({
  name: z.string(),
  price: z.number(),
  isAvailable: z.boolean(),
});

export type Product = z.infer<typeof ProductSchema>;

2. Define your collection map

Create a collection map that links collection name strings to their Zod schemas. This is the type-safe contract used by all hooks and utilities.

// collection-map.ts
import { ProductSchema } from "./schema";

export const CollectionMap = {
  products: ProductSchema,
} as const;

export type AppCollectionMap = typeof CollectionMap;

3. Wrap your app with FirebaseProvider

Pass your initialized Firebase database, storage, and auth instances to the provider.

// main.tsx
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";
import { getAuth } from "firebase/auth";
import { FirebaseProvider } from "@manufac/firebase";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
};

const app = initializeApp(firebaseConfig);
const database = getFirestore(app);
const storage = getStorage(app);
const auth = getAuth(app);

const queryClient = new QueryClient();

function App(): JSX.Element {
  return (
    <QueryClientProvider client={queryClient}>
      <FirebaseProvider database={database} storage={storage} auth={auth}>
        <YourApp />
      </FirebaseProvider>
    </QueryClientProvider>
  );
}

Firestore

Fetch a document by ID

import { useDocumentByID } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function ProductDetail({ productID }: { productID: string }): JSX.Element {
  const { data: product, isPending } = useDocumentByID<AppCollectionMap, "products">(
    "products",
    productID,
  );

  if (isPending) {
    return <p>Loading...</p>;
  }

  if (product === null || product === undefined) {
    return <p>Product not found.</p>;
  }

  return <p>{product.name} — ${product.price}</p>;
}

Fetch a list of documents (with optional pagination)

import { useDocuments } from "@manufac/firebase";
import { where } from "firebase/firestore";
import type { AppCollectionMap } from "./collection-map";

function AvailableProducts(): JSX.Element {
  const {
    data: products,
    isPending,
    activePage,
    totalPages,
    handleNextPage,
    handlePreviousPage,
  } = useDocuments<AppCollectionMap, "products">({
    collectionName: "products",
    queryConstraints: [where("isAvailable", "==", true)],
    isPaginationEnabled: true,
    pageSize: 10,
  });

  if (isPending) {
    return <p>Loading...</p>;
  }

  return (
    <div>
      {products?.map((product) => (
        <p key={product.id}>{product.name}</p>
      ))}
      <p>
        Page {activePage} of {totalPages}
      </p>
      <button onClick={handlePreviousPage} disabled={activePage === 1}>
        Previous
      </button>
      <button onClick={handleNextPage} disabled={activePage === totalPages}>
        Next
      </button>
    </div>
  );
}

Subscribe to real-time document updates

import { useRealtimeDocumentByID } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function LiveProduct({ productID }: { productID: string }): JSX.Element {
  const product = useRealtimeDocumentByID<AppCollectionMap, "products">("products", productID);

  if (product === null) {
    return <p>Product not found.</p>;
  }

  return <p>{product.name} — ${product.price}</p>;
}

Subscribe to real-time collection updates (with optional pagination)

import { useRealtimeDocuments } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function LiveProductList(): JSX.Element {
  const { data: products, activePage, totalPages, handleNextPage, handlePreviousPage } =
    useRealtimeDocuments<AppCollectionMap, "products">({
      collectionName: "products",
      isPaginationEnabled: true,
      pageSize: 10,
    });

  return (
    <div>
      {products.map((product) => (
        <p key={product.id}>{product.name}</p>
      ))}
      <p>
        Page {activePage} of {totalPages}
      </p>
      <button onClick={handlePreviousPage} disabled={activePage === 1}>
        Previous
      </button>
      <button onClick={handleNextPage} disabled={activePage === totalPages}>
        Next
      </button>
    </div>
  );
}

Delete a document

import { useDeleteDocumentByID } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function DeleteProductButton({ productID }: { productID: string }): JSX.Element {
  const { mutate: deleteProduct, isPending } = useDeleteDocumentByID<AppCollectionMap>("products");

  const handleClick = () => {
    deleteProduct(productID);
  };

  return (
    <button onClick={handleClick} disabled={isPending}>
      {isPending ? "Deleting..." : "Delete"}
    </button>
  );
}

Create a document (hook)

import { useCreateDocument } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function CreateProductButton(): JSX.Element {
  const { mutate: createProduct, isPending } = useCreateDocument<AppCollectionMap, "products">("products");

  const handleClick = () => {
    createProduct({ name: "Widget", price: 9.99, isAvailable: true });
  };

  return (
    <button onClick={handleClick} disabled={isPending}>
      {isPending ? "Creating..." : "Create product"}
    </button>
  );
}

To use a specific document ID, pass id in the data object. If a document with that ID already exists the call will throw.

createProduct({ id: "my-custom-id", name: "Widget", price: 9.99, isAvailable: true });

Update a document

import { useUpdateDocument } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function ToggleAvailability({ productID }: { productID: string }): JSX.Element {
  const { mutate: updateProduct, isPending } = useUpdateDocument<AppCollectionMap, "products">("products");

  const handleClick = () => {
    updateProduct({ id: productID, isAvailable: false });
  };

  return (
    <button onClick={handleClick} disabled={isPending}>
      {isPending ? "Updating..." : "Mark unavailable"}
    </button>
  );
}

Get document count

import { useTotalDocumentCount } from "@manufac/firebase";
import type { AppCollectionMap } from "./collection-map";

function ProductCount(): JSX.Element {
  const { data: count } = useTotalDocumentCount<AppCollectionMap>("products");

  return <p>Total products: {count ?? 0}</p>;
}

Firebase Storage

Upload a file

import { useUploadFile } from "@manufac/firebase";

function FileUploader(): JSX.Element {
  const { mutate: uploadFile, isPending } = useUploadFile();

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file !== undefined) {
      uploadFile({ path: `uploads/${file.name}`, data: file });
    }
  };

  return <input type="file" onChange={handleChange} disabled={isPending} />;
}

Get a download URL

import { useDownloadFileURL } from "@manufac/firebase";

function ProductImage({ path }: { path: string }): JSX.Element {
  const { data: url } = useDownloadFileURL(path);

  if (url === undefined) {
    return <p>Loading image...</p>;
  }

  return <img src={url} alt="Product" />;
}

Delete a file

import { useDeleteFile } from "@manufac/firebase";

function DeleteFileButton({ path }: { path: string }): JSX.Element {
  const { mutate: deleteFile, isPending } = useDeleteFile();

  const handleClick = () => {
    deleteFile(path);
  };

  return (
    <button onClick={handleClick} disabled={isPending}>
      {isPending ? "Deleting..." : "Delete file"}
    </button>
  );
}

Authentication

import { useAuth } from "@manufac/firebase";

function UserStatus(): JSX.Element {
  const { user, isLoading, logout } = useAuth();

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

  if (user === null) {
    return <p>Not signed in.</p>;
  }

  return (
    <div>
      <p>Signed in as {user.email}</p>
      <button onClick={logout}>Sign out</button>
    </div>
  );
}