Setup firebase with firestore data in NextJS
Install Required Packages
npm install firebase @radix-ui/react-popover react-quill lucide-react date-fns framer-motion
Set up Firebase
Create a file called firebase.js
in a folder called lib
in the root of your project and add your Firebase configuration:
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "your-api-key",
authDomain: "your-auth-domain",
projectId: "your-project-id",
storageBucket: "your-storage-bucket",
messagingSenderId: "your-messaging-sender-id",
appId: "your-app-id",
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
export { db };
Create the Component
Create a new component called NewItem.tsx
in the components
folder:
"use client";
import React, { useState } from "react";
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@radix-ui/react-popover";
import { format } from "date-fns";
import { motion } from "framer-motion";
import { db } from "@/lib/firebase";
interface NewItemProps {
content?: string;
}
export function NewItem({ content }: NewItemProps) {
const [open, setOpen] = useState(false);
const [title, setTitle] = useState("");
const [date, setDate] = useState<Date | null>(null);
const [description, setDescription] = useState("");
const [label, setLabel] = useState("");
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(false);
const [markdownContent, setMarkdownContent] = useState("");
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) {
console.log("user", user);
}
setLoading(false);
});
return (): void => unsubscribe();
}, []);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const newItem: Item = {
title,
userId: user.uid,
description: markdownContent,
createdAt: serverTimestamp(),
id: "",
subject,
selectedDate: date,
label,
};
const docRef = await addDoc(collection(db, "items"), newItem);
newItem.id = docRef.id;
setItems((prevItems: Item[]) => [newItem, ...prevItems]);
setDescription("");
setTitle("");
setDate(null);
setSubject("");
setLabel("");
setMarkdownContent("");
toast({
title: "Item created successfully.",
description: `with title ${title}`,
});
} catch (error) {
toast({
title: "Something went wrong.",
description: `Your request failed. Please try again. ${error}`,
variant: "destructive",
});
console.error(error);
} finally {
setOpen(false);
}
};
// Render form and other JSX here...
return <>{/* Render Drawer and other JSX here... */}</>;
}
In this component:
- We have used the
useState
hook to manage the local state for title, date, description, label, and items. - The
handleSubmit
function is used to post the data to Firestore. It creates a new item object, posts it to Firestore, and then adds it to the local state. - The
Popover
component from@radix-ui/react-popover
is used to create a dropdown for selecting a date and a label. - The
ReactQuill
component is used for the description input field to allow markdown input. - The
Drawer
component fromvaul
is used to toggle the visibility of the form.
Fetching the Data
To fetch the data from Firestore and display it, you can create a separate component or use the useEffect
hook in the NewItem
component or another parent component.
In the useEffect
hook, you can use the onSnapshot
function from firebase/firestore
to listen for real-time updates from Firestore and update the local state accordingly.
useEffect(() => {
const unsubscribe = onSnapshot(collection(db, "items"), (snapshot) => {
const fetchedItems: Item[] = [];
snapshot.forEach((doc) => {
const item = doc.data() as Item;
item.id = doc.id;
fetchedItems.push(item);
});
setItems(fetchedItems);
});
return (): void => unsubscribe();
}, []);