<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Insights and Inspiration for Developers - Blog by Nazrul Islam]]></title><description><![CDATA[Discover a wealth of insights, tips, and inspiration for developers on the blog by Nazrul Islam. Stay up-to-date with the latest trends, tutorials, and best practices in programming, web development]]></description><link>https://stacknothing.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1722767782657/bd0263ea-7c2b-4d0d-b70f-3daf9cb7c744.png</url><title>Insights and Inspiration for Developers - Blog by Nazrul Islam</title><link>https://stacknothing.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 24 Sep 2026 09:37:00 GMT</lastBuildDate><atom:link href="https://stacknothing.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building Dhikrly: Making Prayer Reminders Actually Reliable in an Offline-First PWA]]></title><description><![CDATA[Introduction
Dhikrly (ذِكْرلي) is a daily Adhkār, Du'ā, and Ṣalāh (prayer) tracker — an offline-first Progressive Web App with cross-device sync, live at dhikrly.com. I built it solo, end-to-end: prod]]></description><link>https://stacknothing.hashnode.dev/building-dhikrly-making-prayer-reminders-actually-reliable-in-an-offline-first-pwa</link><guid isPermaLink="true">https://stacknothing.hashnode.dev/building-dhikrly-making-prayer-reminders-actually-reliable-in-an-offline-first-pwa</guid><category><![CDATA[next js]]></category><category><![CDATA[supabase]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Nazrul Islam]]></dc:creator><pubDate>Fri, 11 Sep 2026 14:39:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64bb66a984439c88d3d1dee8/74ccb098-ed15-4ec1-bbe0-0a0315b5d06a.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2>
<p>Dhikrly (ذِكْرلي) is a daily Adhkār, Du'ā, and Ṣalāh (prayer) tracker — an offline-first Progressive Web App with cross-device sync, live at <a href="https://dhikrly.com">dhikrly.com</a>. I built it solo, end-to-end: product, design, and full-stack engineering, on Next.js 14, TypeScript, Tailwind CSS, and Supabase, with prayer-time calculation via adhan.js and background notifications via web-push and Vercel Cron.</p>
<p>The app tracks two things: adhkār/du'ā completion (with streaks and a 16-week heatmap) and the five daily prayers (with Prayed / Jamā'ah / Delayed / Missed status, plus Sunnah, Tahajjud, and Nafl tracking). Both of those only matter if the app can actually remind a user <em>at the right moment</em> — and that's where the hardest problem in the whole build showed up.</p>
<p>This post walks through it using the STAR framework: <strong>Situation</strong>, <strong>Task</strong>, <strong>Action</strong>, <strong>Result</strong>.</p>
<h2>Situation</h2>
<p>The entire value proposition of Dhikrly rests on timely reminders — five daily prayers plus morning/evening adhkār, each tied to a specific, location-dependent time that shifts every day. The obvious first approach — schedule a client-side timer while the app is open — quietly falls apart the moment a user closes the tab or the PWA goes to the background. Browsers (and iOS Safari in particular) suspend or kill background JS execution, so any reminder logic living in the page itself simply stops running exactly when it's needed most: when nobody has the app open, which for a prayer app is most of the day.</p>
<p>As a solo developer with no backend team and no native app shell to lean on, I had to solve this with only what a PWA gives you.</p>
<h2>Task</h2>
<p>I needed a notification system that would reliably fire at the correct, per-user, per-location prayer and adhkār times — independent of whether the PWA was open, backgrounded, or fully closed — and do it without a native app, without a dedicated ops team, and without breaking on the platform most hostile to background web execution (iOS).</p>
<h2>Action</h2>
<p><strong>1. Ruled out client-only scheduling.</strong> Any approach that depended on the page or even the service worker staying alive to <em>decide when</em> to notify was a dead end — it had to work with the app fully closed, so the "when" had to be decided server-side, not client-side.</p>
<p><strong>2. Moved scheduling to the server.</strong> I built a Vercel Cron job hitting an API route (<code>app/api/cron/notifications</code>) that runs on a schedule, computes each user's next prayer and adhkār times server-side using <code>adhan.js</code> against their saved location, and triggers a push for anyone due a reminder — the client doesn't need to be running at all for this to work.</p>
<p><strong>3. Built the delivery path with the Web Push API.</strong> Subscriptions are managed through a dedicated <code>app/api/push</code> endpoint (subscribe/unsubscribe) and stored per-device, so notifications go out via <code>web-push</code> with VAPID keys. On the client, a service worker (<code>public/sw.js</code>) listens for push events and renders the native notification — this is the piece that lets a notification appear even when the browser tab isn't open, and it's paired with a <code>usePushSubscription</code> hook to manage permission state and re-subscription when it lapses.</p>
<p><strong>4. Kept prayer-time math off the client's clock.</strong> Because reminders are computed server-side against <code>adhan.js</code>, correctness doesn't depend on trusting whatever time zone or clock state the user's device happens to be in — the server calculates once, per user, against their stored location.</p>
<p><strong>5. Versioned the data model as it evolved.</strong> Local storage keys like <code>duas_checked_v3</code> and <code>salah_log_v1</code> reflect schema iterations to the tracking data as the notification and streak logic matured — offline-first storage that also has to sync cleanly to Supabase across devices adds real constraints on how forgiving (or not) you can be with reshaping that data later.</p>
<p><strong>6. Shipped it behind a disciplined branch workflow.</strong> Working alone didn't mean working carelessly: everything flows <code>feature/* → develop → main</code>, with <code>main</code> auto-deploying to production via Vercel and <code>develop</code> kept always-deployable — partly so a notification-path change couldn't go live untested.</p>
<h2>Result</h2>
<p>Prayer and adhkār reminders now fire from the server regardless of the app's open/closed state, which was the entire point — a prayer tracker that only reminds you while you're already looking at it isn't solving the actual problem. The app has shipped through release 1.4.0 across 60 commits, running in production at dhikrly.com with cross-device sync, offline support, and the full Ṣalāh and Adhkār tracking feature set live.</p>
<h2>Takeaways</h2>
<p>Building this alone meant the constraint wasn't "how do I write this code" but "which layer should even be responsible for this decision" — and pushing the scheduling logic server-side, rather than fighting the browser to keep client-side timers alive, was the choice that made the feature trustworthy rather than merely working in a demo.</p>
<hr />
<p><em>Dhikrly is live at</em> <a href="https://dhikrly.com"><em>dhikrly.com</em></a><em>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Mastering IndexedDB: A Developer’s Guide to Efficient Client-Side Storage]]></title><description><![CDATA[Data storage is a crucial part of most web applications, whether for tracking user data or managing application data. As modern web applications become faster and more robust, efficient client-side storage solutions are needed to enhance performance ...]]></description><link>https://stacknothing.hashnode.dev/mastering-indexeddb-a-developers-guide-to-efficient-client-side-storage</link><guid isPermaLink="true">https://stacknothing.hashnode.dev/mastering-indexeddb-a-developers-guide-to-efficient-client-side-storage</guid><category><![CDATA[offline storage]]></category><category><![CDATA[indexeddb]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[client-side storage]]></category><category><![CDATA[web storage api]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Nazrul Islam]]></dc:creator><pubDate>Thu, 06 Mar 2025 11:19:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741259855432/497dfdbd-8074-4bcc-b7c3-34791dfbdb6d.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Data storage is a crucial part of most web applications, whether for tracking user data or managing application data. As modern web applications become faster and more robust, efficient client-side storage solutions are needed to enhance performance and development.</p>
<h2 id="heading-introduction-to-client-side-storage">Introduction to Client-Side Storage</h2>
<p>This tutorial explores how to use IndexedDB for web application data storage, including setting it up and manipulating data using its API. By the end of this guide, you'll have a functional <strong>to-do application</strong> that utilizes IndexedDB.</p>
<h2 id="heading-what-is-indexeddb">What is IndexedDB?</h2>
<p>IndexedDB is a <strong>low-level API for client-side storage</strong>. It provides a <strong>persistent NoSQL database</strong> in the browser, allowing applications to store various types of data, such as:</p>
<ul>
<li><p><strong>Files and blobs</strong></p>
</li>
<li><p><strong>Images and videos</strong></p>
</li>
<li><p><strong>Structured data</strong> (objects, lists, and arrays)</p>
</li>
</ul>
<p>IndexedDB is widely used for <strong>progressive web apps (PWAs)</strong>, <strong>caching</strong>, and <strong>gaming applications</strong>, as it supports transactions and large-scale data storage.</p>
<h3 id="heading-base-html-structure">Base HTML Structure</h3>
<p>Create a basic <strong>to-do application</strong> with an interface for viewing and adding tasks. Open <code>index.html</code> and add the following:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>TODO APP<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"styles.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>TODO APP<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">aside</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>TODOs<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"todos"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">ol</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ol</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">aside</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">aside</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"add"</span>&gt;</span> 
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Add Todo<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"title"</span>&gt;</span>Todo Title<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"desc"</span>&gt;</span>Todo Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"desc"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Save<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">aside</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This structure includes:</p>
<ol>
<li><p>A section to <strong>display to-dos</strong> stored in IndexedDB.</p>
</li>
<li><p>A form to <strong>add new to-dos</strong> to the database.</p>
</li>
</ol>
<h3 id="heading-basic-styling">Basic Styling</h3>
<p>Open <code>styles.css</code> and add the following styles:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span> {
    <span class="hljs-attribute">font-family</span>: sans-serif;
}

<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">800px</span>;
}

<span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-tag">section</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#3182d4</span>;
}

<span class="hljs-selector-class">.add</span>, <span class="hljs-selector-class">.view</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">40%</span>;
}

<span class="hljs-selector-class">.add</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#ebe6e6</span>;
}

<span class="hljs-selector-tag">ol</span> {
    <span class="hljs-attribute">list-style-type</span>: none;
}
</code></pre>
<hr />
<h2 id="heading-initializing-indexeddb">Initializing IndexedDB</h2>
<p>Now, let’s create the IndexedDB database and an object store (<code>todo_tb</code>) to store our to-do items. Open <code>index.js</code> and add the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> db;
<span class="hljs-keyword">const</span> openOrCreateDB = <span class="hljs-built_in">window</span>.indexedDB.open(<span class="hljs-string">'todo_db'</span>, <span class="hljs-number">1</span>);

openOrCreateDB.addEventListener(<span class="hljs-string">'error'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error opening DB'</span>));

openOrCreateDB.addEventListener(<span class="hljs-string">'success'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Successfully opened DB'</span>);
  db = openOrCreateDB.result;
});

openOrCreateDB.addEventListener(<span class="hljs-string">'upgradeneeded'</span>, <span class="hljs-function"><span class="hljs-params">init</span> =&gt;</span> {
  db = init.target.result;

  db.onerror = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error loading database.'</span>);
  };

  <span class="hljs-keyword">const</span> table = db.createObjectStore(<span class="hljs-string">'todo_tb'</span>, { <span class="hljs-attr">keyPath</span>: <span class="hljs-string">'id'</span>, <span class="hljs-attr">autoIncrement</span>:<span class="hljs-literal">true</span> });

  table.createIndex(<span class="hljs-string">'title'</span>, <span class="hljs-string">'title'</span>, { <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span> });
  table.createIndex(<span class="hljs-string">'desc'</span>, <span class="hljs-string">'desc'</span>, { <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span> });
});
</code></pre>
<h3 id="heading-explanation">Explanation:</h3>
<ul>
<li><p><strong>Creates</strong> an IndexedDB database (<code>todo_db</code>).</p>
</li>
<li><p><strong>Creates</strong> an object store (<code>todo_tb</code>) with an auto-incrementing ID.</p>
</li>
<li><p><strong>Adds indexes</strong> for <code>title</code> and <code>desc</code>.</p>
</li>
</ul>
<hr />
<h2 id="heading-saving-data-to-indexeddb">Saving Data to IndexedDB</h2>
<p>Next, let’s implement the function to save <strong>to-do items</strong> to the database.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> todos = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'ol'</span>);
<span class="hljs-keyword">const</span> form = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'form'</span>);
<span class="hljs-keyword">const</span> todoTitle = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#title'</span>);
<span class="hljs-keyword">const</span> todoDesc = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#desc'</span>);
<span class="hljs-keyword">const</span> submit = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'button'</span>);

form.addEventListener(<span class="hljs-string">'submit'</span>, addTodo);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTodo</span>(<span class="hljs-params">e</span>) </span>{
  e.preventDefault();
  <span class="hljs-keyword">const</span> newTodo = { <span class="hljs-attr">title</span>: todoTitle.value, <span class="hljs-attr">body</span>: todoDesc.value };
  <span class="hljs-keyword">const</span> transaction = db.transaction([<span class="hljs-string">'todo_tb'</span>], <span class="hljs-string">'readwrite'</span>);
  <span class="hljs-keyword">const</span> objectStore = transaction.objectStore(<span class="hljs-string">'todo_tb'</span>);
  <span class="hljs-keyword">const</span> query = objectStore.add(newTodo);
  query.addEventListener(<span class="hljs-string">'success'</span>, <span class="hljs-function">() =&gt;</span> {
    todoTitle.value = <span class="hljs-string">''</span>;
    todoDesc.value = <span class="hljs-string">''</span>;
  });
  transaction.addEventListener(<span class="hljs-string">'complete'</span>, <span class="hljs-function">() =&gt;</span> {
    showTodos();
  });
  transaction.addEventListener(<span class="hljs-string">'error'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Transaction error'</span>));
}
</code></pre>
<hr />
<h2 id="heading-retrieving-and-displaying-data">Retrieving and Displaying Data</h2>
<p>To <strong>display stored to-dos</strong>, modify the IndexedDB success event listener:</p>
<pre><code class="lang-javascript">openOrCreateDB.addEventListener(<span class="hljs-string">'success'</span>, <span class="hljs-function">() =&gt;</span> {
  db = openOrCreateDB.result;
  showTodos();
});
</code></pre>
<p>Then, define <code>showTodos</code> to fetch and display stored items:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">showTodos</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">while</span> (todos.firstChild) {
    todos.removeChild(todos.firstChild);
  }
  <span class="hljs-keyword">const</span> objectStore = db.transaction(<span class="hljs-string">'todo_tb'</span>).objectStore(<span class="hljs-string">'todo_tb'</span>);
  objectStore.openCursor().addEventListener(<span class="hljs-string">'success'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {

    <span class="hljs-keyword">const</span> pointer = e.target.result;
    <span class="hljs-keyword">if</span>(pointer) {
      <span class="hljs-keyword">const</span> listItem = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'li'</span>);
      <span class="hljs-keyword">const</span> h3 = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'h3'</span>);
      <span class="hljs-keyword">const</span> pg = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'p'</span>);
      listItem.appendChild(h3);
      listItem.appendChild(pg);
      todos.appendChild(listItem);
      h3.textContent = pointer.value.title;
      pg.textContent = pointer.value.body;
      listItem.setAttribute(<span class="hljs-string">'data-id'</span>, pointer.value.id);
      <span class="hljs-keyword">const</span> deleteBtn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'button'</span>);
      listItem.appendChild(deleteBtn);
      deleteBtn.textContent = <span class="hljs-string">'Remove'</span>;
      deleteBtn.addEventListener(<span class="hljs-string">'click'</span>, deleteItem);
      pointer.continue();
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">if</span>(!todos.firstChild) {
        <span class="hljs-keyword">const</span> listItem = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'li'</span>);
        listItem.textContent = <span class="hljs-string">'No Todo.'</span>
        todos.appendChild(listItem);
      }

      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Todos all shown'</span>);
    }
  });
}
</code></pre>
<hr />
<h2 id="heading-deleting-data">Deleting Data</h2>
<p>To implement <strong>deletion</strong>, define the <code>deleteItem</code> function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deleteItem</span>(<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">const</span> todoId = <span class="hljs-built_in">Number</span>(e.target.parentNode.getAttribute(<span class="hljs-string">'data-id'</span>));
  <span class="hljs-keyword">const</span> transaction = db.transaction([<span class="hljs-string">'todo_tb'</span>], <span class="hljs-string">'readwrite'</span>);
  <span class="hljs-keyword">const</span> objectStore = transaction.objectStore(<span class="hljs-string">'todo_tb'</span>);
  objectStore.delete(todoId);
  transaction.addEventListener(<span class="hljs-string">'complete'</span>, <span class="hljs-function">() =&gt;</span> {
    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
    alert(<span class="hljs-string">`Todo with id of <span class="hljs-subst">${todoId}</span> deleted`</span>)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Todo:<span class="hljs-subst">${todoId}</span> deleted.`</span>);
    <span class="hljs-keyword">if</span>(!todos.firstChild) {
      <span class="hljs-keyword">const</span> listItem = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'li'</span>);
      listItem.textContent = <span class="hljs-string">'No Todo.'</span>;
      todos.appendChild(listItem);
    }
  });
  transaction.addEventListener(<span class="hljs-string">'error'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Transaction error'</span>));
}
</code></pre>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>IndexedDB is a powerful storage solution for modern web applications. In this tutorial, we:</p>
<ul>
<li><p>Set up an IndexedDB database</p>
</li>
<li><p>Implemented <strong>Add</strong>, <strong>Retrieve</strong>, and <strong>Delete</strong> functionalities</p>
</li>
<li><p>Built a <strong>to-do application</strong> using IndexedDB</p>
</li>
</ul>
<h3 id="heading-drawbacks-of-indexeddb">Drawbacks of IndexedDB</h3>
<ul>
<li><p><strong>Complex API</strong> compared to LocalStorage</p>
</li>
<li><p><strong>Asynchronous operations</strong> make debugging harder</p>
</li>
<li><p><strong>Browser limitations</strong> (some older versions don’t support it fully)</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>