Efficient In-Browser Storage with Dexie.js and Sveltekit

Daniel Boadzie
CodeX
Published in
9 min readMar 20, 2023

IndexedDB is an in-browser database API that allows web applications to store and retrieve data on the client side. Unlike other client-side storage solutions like cookies and local storage, IndexedDB is more powerful and flexible, making it an ideal choice for web developers who need to build complex web applications that require data management capabilities. With IndexedDB, developers can store structured data, perform advanced queries, and manipulate data using transactions, making it a powerful and efficient tool for web app development.

Dexiejs is a powerful wrapper library for IndexedDB that makes it easier for developers to work with IndexedDB databases. Dexiejs provides a clean and intuitive API that simplifies the process of creating, reading, updating, and deleting data from IndexedDB. With Dexiejs, developers can write less code, avoid common pitfalls, and leverage advanced features of IndexedDB with ease. Dexiejs also provides advanced query capabilities, efficient data synchronization, and excellent performance, making it a valuable tool for building modern web applications. For developers working with IndexedDB, Dexiejs is an important library to know and understand.

This article will delve into the powerful features of Dexiejs and explore how it simplifies and enhances the development experience of working with IndexedDB. It will provide a step-by-step guide for getting started with Dexiejs, including installation instructions and how to define and manipulate objects. The article will also explore features such as filtering and sorting data, Furthermore, it will discuss how Dexiejs can be used in conjunction with Sveltekit to build high-performance web applications that efficiently manage and manipulate data on the client side, resulting in faster and more responsive web apps.

Setup

To begin utilizing these tools, it is essential to install them, beginning with Sveltekit as the first step.

We’ll use pnpm to create our project and install the required dependencies.

pnpm create svelte@latest dexie-kit

The code above will prompt you to choose from a few options and generate a basic SvelteKit project based on your selection. The generated project should look something like this:

Once you have selected the options, you will need to install the dependencies for the project, which can be done by running the following command from the project’s directory:

cd dexie-kit

pnpm install

Adding Tailwind CSS

In addition, we can also install Tailwindcss to enhance the styling and design capabilities of our project, because why not. Fortunately, there is a useful tool called “svelte-add” that is specifically designed for this purpose.

npx svelte-add@latest tailwindcss

To install these new dependencies, you will need to use the following command:

pnpm install

Now open the project in your preferred editor; mine is vscode.

Getting Started with Dexie.js

Dexie.js is a JavaScript library that provides a straightforward way to work with indexedDB, a browser-based storage mechanism. With Dexie.js, you can easily create, read, update, and delete data in an indexedDB database.

To get started we will install using pnpm:

pnpm add dexie

Creating a database

Once you have included Dexie.js in your project, you can create a new Dexie instance, which will allow you to create and manage your indexedDB database. From there, you can define your data model and start working with your data.

The book model should include the following fields: title, author, genre, and price. Additionally, we will include a primary key field ++id which is an auto-incrementing integer.

// db.js
import Dexie from 'dexie';

export const db = new Dexie('bookDB');
db.version(1).stores({
books: '++id, title, author, genre, price', // Primary key and indexed props
});

Notice that the fields are separated by commas to indicate that they are different properties of the store. Commas are a common way to separate items in lists or arrays in many programming languages.

At this point, it would be helpful to have some dummy values that we can use to prepopulate our books store. We can add the following code to the db.js file to achieve this:

...
const dummyData = [
{
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
genre: 'Fiction',
price: 12.99
},
{
title: 'To Kill a Mockingbird',
author: 'Harper Lee',
genre: 'Fiction',
price: 10.99
},
{
title: 'The Catcher in the Rye',
author: 'J.D. Salinger',
genre: 'Fiction',
price: 9.99
},
{
title: 'The Hobbit',
author: 'J.R.R. Tolkien',
genre: 'Fantasy',
price: 14.99
},
{
title: 'The Lord of the Rings',
author: 'J.R.R. Tolkien',
genre: 'Fantasy',
price: 24.99
},
{
title: 'The Da Vinci Code',
author: 'Dan Brown',
genre: 'Thriller',
price: 7.99
}
];

db.on('populate', function (transaction) {
transaction.books.bulkAdd(dummyData);
});
db.open();

The code then sets up an event listener for the populate event on a db object, which is likely a reference to an IndexedDB database. When the populate event is triggered, the code uses the bulkAdd method to add all the book objects in dummyData to the database in a single transaction.

Finally, the code opens the database by calling db.open(). By doing this, we will have some data when our db is created.

Working with Dexiejs

Now that we have our data model set and prepopulated, let start working with the store in our Sveltekit application. We will begin by displaying the data in our +page.svelte file.

<script>
import { browser } from '$app/environment';
import { db } from '$lib/db';
import { liveQuery } from 'dexie';

$: books = liveQuery(() => (browser ? db.books.toArray() : []));
</script>

<section class="container text-slate-500 mx-auto px-0 ">
<h1 class="bg-slate-800 text-white p-2 text-4xl ">Dexie-Kit</h1>
<div class="mt-6 mx-4 grid grid-cols-1 lg:grid-cols-3 gap-4">
{#if $books}
{#each $books as book}
<div class="bg-slate-100 rounded-sm p-4 flex flex-col gap-x-4 gap-y-2">
<h1 class="text-2xl ">{book.title}</h1>
<p class="text-md">{book.author}</p>
<p class="italic">{book.genre}</p>
<p class="font-bold text-red-500">
{book.price.toLocaleString('en-US', {
style: 'currency',
currency: 'USD'
})}
</p>
</div>
{/each}
{/if}
</div>
</section>

This code sets up a live query for a Dexie database and displays the results on a webpage.

The first part of the code imports the necessary dependencies from various libraries, including browser and db from a library called dexie, and liveQuery from another library called dexie-react-hooks.

The code then sets up a $: reactive statement that uses the liveQuery function to create a live query of all the books in the db database. The resulting books variable will be updated automatically whenever the contents of the database change.

The second part of the code defines a section of HTML markup that displays the books on the webpage. It uses an {#each} block to iterate over the $books array and display each book's title, author, genre, and price. The price is formatted using the toLocaleString() method to display the currency in US dollars.

If you run the code now with pnpm dev, you will see the following:

Note that even when you refresh the page, the data remains because it is persisted in the browser using IndexedDB. The data will only be gone if you clear your browser data, such as cookies and cache. This is a powerful feature of web storage that allows for creating rich and dynamic web apps that can work offline and provide a seamless user experience.

Checking the DB

To check the check the right click on your page and select inspect then application , indexedDB the name of your store(booksDB) and then your table(books) and you should see your table with the prepopulated values.

Adding and Deleting a Book

It is now time to add the ability to add a book to our application through a form. We will create a form that binds to the values of our books store, and when the user submits the form, we will use the db.books.add method to add a new book to our store.

<script>
...
let title, author, genre, price;
...

const addBook = () => {
return db.books.add({ title, author, genre, price });
};

$: handleSubmit = () => {
if (title && author && genre && price) {
addBook();
title = '';
author = '';
genre = '';
price = 0.0;
}else {
alert('All fields are required');
}
};
</script>

<section>
...
<div class="mx-4 my-6">
<h1 class="text-4xl my-4">Add a Book</h1>
<form on:submit|preventDefault={handleSubmit} class="flex flex-wrap gap-3 max-w-lg" action="">
<input
class="focus:outline-none p-3 rounded-sm w-full ring-2"
placeholder="Title..."
bind:value={title}
type="text"
/>
<input
class="focus:outline-none p-3 rounded-sm w-full ring-2"
placeholder="Author..."
bind:value={author}
type="text"
/>
<input
class="focus:outline-none p-3 rounded-sm w-full ring-2"
placeholder="Genre..."
bind:value={genre}
type="text"
/>
<input
class="focus:outline-none p-3 rounded-sm w-full ring-2"
placeholder="Price..."
bind:value={price}
type="number"
/>
<button type="submit" class="px-4 py-2 bg-slate-700 text-white rounded-sm">Add Book</button>
</form>
</div>
</section>

Notice that we have added a form validation to our handleSubmit function to only run if all the fields are non-empty. If any of the fields are empty, the function returns an alert message indicating that all fields are required. This prevents the form from being submitted with empty values. Now if your run your app, you will see the following:

It will also be nice to add a deleteBook function that deletes a book with a given id when called. This function will come in handy when we want to remove a book from our books store. Here's how the function and the button will look like:

<script>
...
const deleteBook = (id) => {
return db.books.delete(id);
};
</script>

<section>
...
<div class="flex items-center justify-between ">
<p class="font-bold text-red-500">
{book.price.toLocaleString('en-US', {
style: 'currency',
currency: 'USD'
})}
</p>
<button on:click={deleteBook(book.id)}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-red-500 w-6 h-6 icon icon-tabler icon-tabler-x"
viewBox="0 0 24 24"
stroke-width="2"
stroke=""
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
...
</section>

When you run the app now and click on the delete button (denoted by the x SVG icon), the corresponding book will be deleted from the books store. This is made possible by the deleteBook function, which takes the ID of the book to be deleted as a parameter and uses the db.books.delete method to remove the book from the store. With this functionality in place, users can easily remove books from the store with a single click.

There is a lot more we can add to our app, such as the ability to edit books, sort and filter them, and so on. Dexie provides a rich set of APIs for working with IndexedDB, and you can find more information and examples in the documentation. With a little bit of creativity and effort, you can build powerful web apps that leverage the power of IndexedDB and Dexie.

Conclusion

In this article, we have explored the Dexie.js library and how it can be used to work with IndexedDB in the browser. We have seen how to create a new database and table, add data to it, and retrieve and display it in our web app. We have also learned about live queries and how they can help us keep our app up to date with changes to the database.

Using Dexie.js, we have built a simple book management app that allows us to add new books, delete them, and view them in a list. This app is a great starting point for building more complex web apps that use IndexedDB for data storage.

IndexedDB is a powerful browser-based storage solution that allows us to store and retrieve data in the browser, even when offline. With Dexie.js, we can easily work with IndexedDB and create dynamic, data-driven web apps that can provide a seamless user experience.

The code for this project is available here and the live app is available here

--

--

Daniel Boadzie
CodeX
Writer for

Data scientist | AI Engineer |Software Engineering|Trainer|Svelte Entusiast. Find out more about my me here https://www.linkedin.com/in/boadzie/