--- title: IndexedDB API pagination_prev: demos/desktop/index pagination_next: demos/local/index sidebar_custom_props: summary: Reading and writing data in an in-browser NoSQL database ---
:::warning pass IndexedDB is a very low-level API. Browser vendors recommend using libraries or [WebSQL](/docs/demos/data/websql) in production applications. ::: The IndexedDB API provides an in-browser sandboxed local data store for JSON objects. Like the [Local Storage API](/docs/demos/data/storageapi), IndexedDB is a popular choice for offline storage. ## Wrapper Libraries A number of popular wrapper libraries seek to simplify IndexedDB operations. :::note The wrapper libraries in this section have been used by SheetJS users in production sites. ::: ### localForage :::note This demo was last tested on 2023 June 1 with `localForage` 1.10.0 ::: `localForage` is a IndexedDB wrapper that presents an async Storage interface. Arrays of objects can be stored using `setItem` using row index as key: ```js const aoo = XLSX.utils.sheet_to_json(ws); for(var i = 0; i < aoo.length; ++i) await localForage.setItem(i, aoo[i]); ``` Recovering the array of objects involves an iteration over the storage: ```js const aoo = []; await localforage.iterate((v, k) => { aoa[+k] = v; }); const ws = XLSX.utils.json_to_sheet(aoo); ``` #### Demo This demo prepares a small IndexedDB database with some sample data. After saving the exported file, the IndexedDB database can be inspected in the "IndexedDB" section of the "Application" Tab of Developer Tools: ![IndexedDB view in Developer Tools](pathname:///storageapi/lforage.png) ```jsx live function SheetJSLocalForage() { const data = [ { Name: "Barack Obama", Index: 44 }, { Name: "Donald Trump", Index: 45 }, { Name: "Joseph Biden", Index: 46 } ]; const xport = React.useCallback(async() => { /* force use of IndexedDB and connect to DB */ localforage.config({ driver: [ localforage.INDEXEDDB ], name: "SheetQL", size: 2097152 }); /* create sample data */ await localforage.clear(); for(var i = 0; i < data.length; ++i) await localforage.setItem(i, data[i]); /* pull data and generate aoa */ const aoo = []; await localforage.iterate((v, k) => { aoo[+k] = v; }); /* export */ const ws = XLSX.utils.json_to_sheet(aoo); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Presidents"); XLSX.writeFile(wb, "SheetJSLocalForage.xlsx"); }); return ( ); } ``` ### DexieJS :::note This demo was last tested on 2023 June 1 with DexieJS 3.2.4 ::: DexieJS is a minimalistic wrapper for IndexedDB. It provides a convenient interface for creating multiple logical tables, well-suited for workbooks. #### Importing Data When configuring tables, DexieJS needs a schema. The schema definition supports primary keys and other properties, but they are not required: ```js /* assuming `wb` is a workbook from XLSX.read */ var db = new Dexie("SheetJSDexie"); db.version(1).stores(Object.fromEntries(wb.SheetNames.map(n => ([n, "++"])))); ``` After the database is configured, `bulkPut` can insert arrays of objects: ```js /* loop over worksheet names */ for(let i = 0; i <= wb.SheetNames.length; ++i) { /* get the worksheet for the specified index */ const wsname = wb.SheetNames[i]; const ws = wb.Sheets[wsname]; if(!ws) continue; /* generate an array of objects */ const aoo = XLSX.utils.sheet_to_json(ws); /* push to idb */ await db[wsname].bulkPut(aoo); } ``` This demo inserts all data from a selected worksheet into a database, then fetches the data from the first worksheet in reverse: ```jsx live /* The live editor requires this function wrapper */ function SheetJSDexieImport(props) { const [__html, setHTML] = React.useState("Select a spreadsheet"); return ( <> { try { /* get data as an ArrayBuffer */ const file = e.target.files[0]; const data = await file.arrayBuffer(); /* parse worksheet */ const wb = XLSX.read(data); /* load into indexeddb */ await Dexie.delete("SheetJSDexie"); const db = new Dexie("SheetJSDexie"); const wsnames = wb.SheetNames.map(n => ([n, "++"])); db.version(1).stores(Object.fromEntries(wsnames)); /* loop over worksheet names */ for(let i = 0; i <= wb.SheetNames.length; ++i) { /* get the worksheet for the specified index */ const wsname = wb.SheetNames[i]; const ws = wb.Sheets[wsname]; if(!ws) continue; /* generate an array of objects */ const aoo = XLSX.utils.sheet_to_json(ws); /* push to idb */ await db[wsname].bulkPut(aoo); } /* fetch the first table in reverse order */ const rev = await db[wb.SheetNames[0]].reverse().toArray(); setHTML(rev.map(r => JSON.stringify(r)).join("\n")); } catch(e) { setHTML(e && e.message || e); }}}/> > ); } ``` #### Exporting Data `db.tables` is a plain array of table objects. `toArray` fetches data: ```js /* create blank workbook */ const wb = XLSX.utils.book_new(); /* loop tables */ for(const table of db.tables) { /* get data */ const aoo = await table.toArray(); /* create worksheet */ const ws = XLSX.utils.json_to_sheet(aoo); /* add to workbook */ XLSX.utils.book_append_sheet(wb, ws, table.name); } ``` This demo prepares a small database with some sample data. ```jsx live function SheetJSDexieExport() { const data = [ { Name: "Barack Obama", Index: 44 }, { Name: "Donald Trump", Index: 45 }, { Name: "Joseph Biden", Index: 46 } ]; const xport = React.useCallback(async() => { /* prepare db */ await Dexie.delete("SheetJSDexie"); var db = new Dexie("SheetJSDexie"); db.version(1).stores({ Presidents: "++" }); db.Presidents.bulkPut(data); /* pull data and generate workbook */ const wb = XLSX.utils.book_new(); for(const table of db.tables) { const aoo = await table.toArray(); const ws = XLSX.utils.json_to_sheet(aoo); XLSX.utils.book_append_sheet(wb, ws, table.name); } XLSX.writeFile(wb, "SheetJSDexie.xlsx"); }); return ( ); } ```