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()
}, [])