Custom React Hooks
A comprehensive guide to the custom React hooks used in NotebookLLM for managing state and side effects.
Overview
NotebookLLM provides several custom React hooks that encapsulate complex state management and API interactions. These hooks follow React best practices and integrate seamlessly with the application's data fetching patterns.
useNotes
Manages notes within a notebook, providing CRUD operations and auto-save functionality with debouncing.
Usage
const {
notes,
loading,
error,
currentNote,
isSaving,
createNote,
updateNote,
deleteNote,
selectNote,
scheduleAutoSave,
cancelAutoSave
} = useNotes({ notebookId, autoSaveDelay?: number })Parameters
| Property | Type | Description |
|---|---|---|
notebookId | string | The ID of the notebook to load notes from |
autoSaveDelay | number | Delay in ms before auto-saving (default: 1000) |
Returns
| Property | Description |
|---|---|
notes | Array of notes in the notebook |
currentNote | Currently selected note or null |
loading | Boolean indicating if notes are being loaded |
error | Error message if operation failed |
isSaving | Boolean indicating if auto-save is in progress |
lastSaved | Date of last successful save |
Example
import { useNotes } from "@/hooks/use-notes"
function NotesEditor({ notebookId }: { notebookId: string }) {
const {
notes,
currentNote,
loading,
createNote,
updateNote,
selectNote
} = useNotes({ notebookId })
const handleSave = async (content: string) => {
if (currentNote) {
await updateNote(currentNote.id, { content })
}
}
const handleAutoSave = (content: string) => {
if (currentNote) {
scheduleAutoSave(currentNote.id, { content })
}
}
if (loading) return <div>Loading notes...</div>
return (
<div>
{notes.map(note => (
<NoteCard
key={note.id}
note={note}
onClick={() => selectNote(note.id)}
/>
))}
<button onClick={() => createNote({ title: "New Note" })}>
Add Note
</button>
</div>
)
}useStudio
Manages content generation in the Studio panel, including podcasts, quizzes, flashcards, and mind maps. Handles async content generation with polling for task status updates.
Usage
const {
items,
loading,
error,
generatingTool,
generateContent,
deleteContent,
refresh
} = useStudio({ notebookId, pollInterval?: number })Supported Content Types
Audio/Audio Overview- Generate audio- podcast-style
Quiz- Generate quiz questions Flashcards- Generate flashcard decksMind Map- Generate visual mind maps
StudioItem Type
interface StudioItem {
id: string
title: string
sourceCount: number
timeAgo: string
type: "quiz" | "audio" | "flashcards" | "mindmap" | "report" | "note"
status: "pending" | "processing" | "completed" | "failed"
isNew: boolean
hasInteractive?: boolean
content?: Record<string, unknown>
audioUrl?: string | null
}Example
import { useStudio } from "@/hooks/use-studio"
function StudioPanel({ notebookId }: { notebookId: string }) {
const { items, generatingTool, generateContent, deleteContent } =
useStudio({ notebookId })
const handleGenerate = async (tool: string) => {
await generateContent(tool)
}
return (
<div>
<div className="generation-buttons">
<button
onClick={() => handleGenerate("Audio")}
disabled={generatingTool !== null}
>
Generate Podcast
</button>
<button
onClick={() => handleGenerate("Quiz")}
disabled={generatingTool !== null}
>
Generate Quiz
</button>
</div>
<div className="content-list">
{items.map(item => (
<ContentCard
key={item.id}
item={item}
onDelete={() => deleteContent(item.id)}
/>
))}
</div>
</div>
)
}useGoogleDrive
Provides a collection of hooks for integrating with Google Drive, including file browsing, searching, importing, and connection management.
useGoogleDriveStatus
Manages Google Drive connection status and authentication state.
const { status, isLoading, error, refreshStatus } = useGoogleDriveStatus()useGoogleDriveFiles
Lists files and folders with pagination, navigation, and breadcrumbs support.
const {
files,
isLoading,
error,
currentFolder,
breadcrumbs,
nextPageToken,
loadFiles,
navigateToFolder,
navigateToRoot,
navigateUp,
loadMore
} = useGoogleDriveFiles()useGoogleDriveSearch
Provides file search functionality with pagination support.
const {
results,
isSearching,
searchQuery,
search,
searchMore,
clearSearch
} = useGoogleDriveSearch()useGoogleDriveImport
Handles file import from Google Drive to notebooks with progress tracking.
const {
importProgress,
isImporting,
importFile,
importMultipleFiles,
clearImportProgress
} = useGoogleDriveImport(notebookId, onImportComplete?)useGoogleDriveDisconnect
Provides disconnect functionality for Google Drive integration.
const { disconnect, isDisconnecting } = useGoogleDriveDisconnect()Example
import {
useGoogleDriveStatus,
useGoogleDriveFiles,
useGoogleDriveImport
} from "@/hooks/use-google-drive"
function GoogleDriveBrowser({ notebookId }: { notebookId: string }) {
const { status, refreshStatus } = useGoogleDriveStatus()
const { files, navigateToFolder, loadFiles } = useGoogleDriveFiles()
const { importFile, importProgress } = useGoogleDriveImport(notebookId)
useEffect(() => {
if (status?.connected) {
loadFiles()
}
}, [status?.connected])
if (!status?.connected) {
return <ConnectButton onConnect={refreshStatus} />
}
return (
<div>
<FileList
files={files}
onFolderClick={navigateToFolder}
/>
<ImportProgress progress={importProgress} />
</div>
)
}useSuggestedQuestions
Fetches AI-generated suggested questions based on document content or conversation context. Supports both document-based and conversation-based question suggestions.
Usage
const {
questions,
conversationQuestions,
isLoading,
isLoadingConversation,
error,
documentCount,
refresh,
refreshFromConversation
} = useSuggestedQuestions(notebookId)Two Modes
Document-Based
Initial questions generated from notebook content on mount. Uses the notebook's documents to create relevant study questions.
Conversation-Based (Option B)
Dynamic questions generated after each chat response based on the conversation context.
Example
import { useSuggestedQuestions } from "@/hooks/use-suggested-questions"
function QuestionSuggestions({ notebookId }: { notebookId: string }) {
const {
questions,
conversationQuestions,
isLoading,
refresh
} = useSuggestedQuestions(notebookId)
// Use document-based or conversation-based questions
const displayQuestions = conversationQuestions.length > 0
? conversationQuestions
: questions.map(q => q.text)
return (
<div>
<h3>Suggested Questions</h3>
{isLoading ? (
<Spinner />
) : (
<ul>
{displayQuestions.map((question, index) => (
<li key={index}>
<button onClick={() => askQuestion(question)}>
{question}
</button>
</li>
))}
</ul>
)}
<button onClick={refresh}>Refresh Suggestions</button>
</div>
)
}useScrollReveal
A hook for implementing scroll-based reveal animations using Framer Motion. Triggers animations when elements enter the viewport.
Usage
const {
ref,
isVisible,
animationProps
} = useScrollReveal(options?)Options
| Property | Description |
|---|---|
threshold | Percentage of element visible before triggering (0-1) |
once | Whether to trigger only once (default: true) |
margin | Margin from viewport edges |
Best Practices
Error Handling
Always handle loading and error states in your components. The hooks provideloading and error properties for this purpose.
Memoization
The hooks use useCallback and useMemo internally to prevent unnecessary re-renders. Avoid wrapping hook functions in your own memoization unless necessary.
Cleanup
Hooks like useNotes handle cleanup automatically (e.g., clearing auto-save timers). The cleanup runs on component unmount.