useHooks.iov4.1.2
DocsBlogGitHub
Hooks
No hooks found in any category.

useIndexedDB

state

Installation

npx usehooks-cli@latest add use-indexed-db

Description

A React hook for managing IndexedDB operations with automatic database initialization, error handling, and React state synchronization. IndexedDB is a low-level API for client-side storage of significant amounts of structured data.

Parameters

NameTypeDefaultDescription
databaseNamestring-The name of the IndexedDB database to open or create
storeNamestring-The name of the object store within the database
options?UseIndexedDBOptions-Optional configuration object

Return Type

UseIndexedDBReturn<T>
PropertyTypeDescription
dataT | nullThe most recently retrieved data from IndexedDB
errorstring | nullError message if any operation failed
loadingbooleanWhether the database is currently being initialized
setItem(key: string, value: T) => Promise<void>Store a value in IndexedDB with the given key
getItem(key: string) => Promise<T | null>Retrieve a value from IndexedDB by key
removeItem(key: string) => Promise<void>Remove a value from IndexedDB by key
clear() => Promise<void>Clear all data from the object store
getAllKeys() => Promise<string[]>Get all keys from the object store

Examples

Basic Usage

Simple key-value storage with IndexedDB

1const { data, error, loading, setItem, getItem } = useIndexedDB('myApp', 'userData'); 2 3const saveUserData = async () => { 4 try { 5 await setItem('user-123', { name: 'John Doe', email: 'john@example.com' }); 6 console.log('User data saved!'); 7 } catch (err) { 8 console.error('Failed to save:', err); 9 } 10}; 11 12const loadUserData = async () => { 13 try { 14 const userData = await getItem('user-123'); 15 console.log('Loaded user:', userData); 16 } catch (err) { 17 console.error('Failed to load:', err); 18 } 19}; 20 21if (loading) return <div>Initializing database...</div>; 22if (error) return <div>Error: {error}</div>; 23 24return ( 25 <div> 26 <button onClick={saveUserData}>Save User Data</button> 27 <button onClick={loadUserData}>Load User Data</button> 28 {data && <pre>{JSON.stringify(data, null, 2)}</pre>} 29 </div> 30);

Advanced Usage with Custom Schema

Using IndexedDB with custom database schema and upgrade handling

1interface TodoItem { 2 id: string; 3 title: string; 4 completed: boolean; 5 createdAt: Date; 6} 7 8const { setItem, getItem, getAllKeys, clear } = useIndexedDB<TodoItem>( 9 'todoApp', 10 'todos', 11 { 12 version: 2, 13 onUpgradeNeeded: (db, oldVersion, newVersion) => { 14 if (oldVersion < 1) { 15 // Create todos store 16 const todosStore = db.createObjectStore('todos'); 17 } 18 if (oldVersion < 2) { 19 // Add index for completed status 20 const transaction = db.transaction(['todos'], 'readwrite'); 21 const todosStore = transaction.objectStore('todos'); 22 if (!todosStore.indexNames.contains('completed')) { 23 todosStore.createIndex('completed', 'completed', { unique: false }); 24 } 25 } 26 } 27 } 28); 29 30const addTodo = async (title: string) => { 31 const todo: TodoItem = { 32 id: crypto.randomUUID(), 33 title, 34 completed: false, 35 createdAt: new Date() 36 }; 37 await setItem(todo.id, todo); 38}; 39 40const loadAllTodos = async () => { 41 const keys = await getAllKeys(); 42 const todos = await Promise.all( 43 keys.map(key => getItem(key)) 44 ); 45 return todos.filter(Boolean) as TodoItem[]; 46};

File Storage

Storing and retrieving files using IndexedDB

1const { setItem, getItem } = useIndexedDB<File>('fileStorage', 'files'); 2 3const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => { 4 const file = event.target.files?.[0]; 5 if (file) { 6 try { 7 await setItem(file.name, file); 8 console.log('File stored successfully!'); 9 } catch (err) { 10 console.error('Failed to store file:', err); 11 } 12 } 13}; 14 15const downloadFile = async (fileName: string) => { 16 try { 17 const file = await getItem(fileName); 18 if (file) { 19 const url = URL.createObjectURL(file); 20 const a = document.createElement('a'); 21 a.href = url; 22 a.download = fileName; 23 a.click(); 24 URL.revokeObjectURL(url); 25 } 26 } catch (err) { 27 console.error('Failed to download file:', err); 28 } 29}; 30 31return ( 32 <div> 33 <input type="file" onChange={handleFileUpload} /> 34 <button onClick={() => downloadFile('example.txt')}> 35 Download File 36 </button> 37 </div> 38);

Dependencies

react

Notes

  • IndexedDB is asynchronous and more powerful than localStorage for large amounts of data
  • Supports storing complex objects, files, and blobs without serialization
  • Automatically handles database initialization and schema upgrades
  • Provides transaction-based operations for data consistency
  • Works offline and follows same-origin policy
  • Browser storage quotas apply - see MDN documentation for details
  • Not available in all environments (e.g., some private browsing modes)
  • All operations are Promise-based for better async handling

Implementation

1"use client"; 2 3import { useState, useEffect, useCallback } from "react"; 4 5interface UseIndexedDBOptions { 6 version?: number; 7 onUpgradeNeeded?: ( 8 db: IDBDatabase, 9 oldVersion: number, 10 newVersion: number 11 ) => void; 12} 13 14interface UseIndexedDBReturn<T> { 15 data: T | null; 16 error: string | null; 17 loading: boolean; 18 setItem: (key: string, value: T) => Promise<void>; 19 getItem: (key: string) => Promise<T | null>; 20 removeItem: (key: string) => Promise<void>; 21 clear: () => Promise<void>; 22 getAllKeys: () => Promise<string[]>; 23} 24 25export function useIndexedDB<T = any>( 26 databaseName: string, 27 storeName: string, 28 options: UseIndexedDBOptions = {} 29): UseIndexedDBReturn<T> { 30 const [data, setData] = useState<T | null>(null); 31 const [error, setError] = useState<string | null>(null); 32 const [loading, setLoading] = useState<boolean>(false); 33 const [db, setDb] = useState<IDBDatabase | null>(null); 34 35 const { version = 1, onUpgradeNeeded } = options; 36 37 // Initialize IndexedDB connection 38 useEffect(() => { 39 if (typeof window === "undefined") return; 40 41 const initDB = async () => { 42 try { 43 setLoading(true); 44 setError(null); 45 46 const request = indexedDB.open(databaseName, version); 47 48 request.onerror = () => { 49 setError(`Failed to open database: ${request.error?.message}`); 50 setLoading(false); 51 }; 52 53 request.onsuccess = () => { 54 setDb(request.result); 55 setLoading(false); 56 }; 57 58 request.onupgradeneeded = (event) => { 59 const database = request.result; 60 const oldVersion = event.oldVersion; 61 const newVersion = event.newVersion || version; 62 63 // Create object store if it doesn't exist 64 if (!database.objectStoreNames.contains(storeName)) { 65 database.createObjectStore(storeName); 66 } 67 68 // Call custom upgrade handler if provided 69 if (onUpgradeNeeded) { 70 onUpgradeNeeded(database, oldVersion, newVersion); 71 } 72 }; 73 } catch (err) { 74 setError(`IndexedDB initialization error: ${err}`); 75 setLoading(false); 76 } 77 }; 78 79 initDB(); 80 81 return () => { 82 if (db) { 83 db.close(); 84 } 85 }; 86 }, [databaseName, storeName, version, onUpgradeNeeded]); 87 88 // Set item in IndexedDB 89 const setItem = useCallback( 90 async (key: string, value: T): Promise<void> => { 91 if (!db) { 92 throw new Error("Database not initialized"); 93 } 94 95 return new Promise((resolve, reject) => { 96 const transaction = db.transaction([storeName], "readwrite"); 97 const store = transaction.objectStore(storeName); 98 const request = store.put(value, key); 99 100 request.onsuccess = () => { 101 setData(value); 102 resolve(); 103 }; 104 105 request.onerror = () => { 106 const errorMsg = `Failed to set item: ${request.error?.message}`; 107 setError(errorMsg); 108 reject(new Error(errorMsg)); 109 }; 110 }); 111 }, 112 [db, storeName] 113 ); 114 115 // Get item from IndexedDB 116 const getItem = useCallback( 117 async (key: string): Promise<T | null> => { 118 if (!db) { 119 throw new Error("Database not initialized"); 120 } 121 122 return new Promise((resolve, reject) => { 123 const transaction = db.transaction([storeName], "readonly"); 124 const store = transaction.objectStore(storeName); 125 const request = store.get(key); 126 127 request.onsuccess = () => { 128 const result = request.result || null; 129 setData(result); 130 resolve(result); 131 }; 132 133 request.onerror = () => { 134 const errorMsg = `Failed to get item: ${request.error?.message}`; 135 setError(errorMsg); 136 reject(new Error(errorMsg)); 137 }; 138 }); 139 }, 140 [db, storeName] 141 ); 142 143 // Remove item from IndexedDB 144 const removeItem = useCallback( 145 async (key: string): Promise<void> => { 146 if (!db) { 147 throw new Error("Database not initialized"); 148 } 149 150 return new Promise((resolve, reject) => { 151 const transaction = db.transaction([storeName], "readwrite"); 152 const store = transaction.objectStore(storeName); 153 const request = store.delete(key); 154 155 request.onsuccess = () => { 156 setData(null); 157 resolve(); 158 }; 159 160 request.onerror = () => { 161 const errorMsg = `Failed to remove item: ${request.error?.message}`; 162 setError(errorMsg); 163 reject(new Error(errorMsg)); 164 }; 165 }); 166 }, 167 [db, storeName] 168 ); 169 170 // Clear all items from the store 171 const clear = useCallback(async (): Promise<void> => { 172 if (!db) { 173 throw new Error("Database not initialized"); 174 } 175 176 return new Promise((resolve, reject) => { 177 const transaction = db.transaction([storeName], "readwrite"); 178 const store = transaction.objectStore(storeName); 179 const request = store.clear(); 180 181 request.onsuccess = () => { 182 setData(null); 183 resolve(); 184 }; 185 186 request.onerror = () => { 187 const errorMsg = `Failed to clear store: ${request.error?.message}`; 188 setError(errorMsg); 189 reject(new Error(errorMsg)); 190 }; 191 }); 192 }, [db, storeName]); 193 194 // Get all keys from the store 195 const getAllKeys = useCallback(async (): Promise<string[]> => { 196 if (!db) { 197 throw new Error("Database not initialized"); 198 } 199 200 return new Promise((resolve, reject) => { 201 const transaction = db.transaction([storeName], "readonly"); 202 const store = transaction.objectStore(storeName); 203 const request = store.getAllKeys(); 204 205 request.onsuccess = () => { 206 resolve(request.result as string[]); 207 }; 208 209 request.onerror = () => { 210 const errorMsg = `Failed to get keys: ${request.error?.message}`; 211 setError(errorMsg); 212 reject(new Error(errorMsg)); 213 }; 214 }); 215 }, [db, storeName]); 216 217 return { 218 data, 219 error, 220 loading, 221 setItem, 222 getItem, 223 removeItem, 224 clear, 225 getAllKeys, 226 }; 227} 228