Hooks
No hooks found in any category.
useIndexedDB
stateInstallation
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
Name | Type | Default | Description |
---|---|---|---|
databaseName | string | - | The name of the IndexedDB database to open or create |
storeName | string | - | The name of the object store within the database |
options? | UseIndexedDBOptions | - | Optional configuration object |
Return Type
UseIndexedDBReturn<T>
Property | Type | Description |
---|---|---|
data | T | null | The most recently retrieved data from IndexedDB |
error | string | null | Error message if any operation failed |
loading | boolean | Whether 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