<?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[Viraj Nikam | Blogs]]></title><description><![CDATA[Viraj Nikam | Blogs]]></description><link>https://blog.virajnikam.in</link><generator>RSS for Node</generator><lastBuildDate>Sun, 17 May 2026 19:03:39 GMT</lastBuildDate><atom:link href="https://blog.virajnikam.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Build Desktop Applications with Tauri]]></title><description><![CDATA[1. Story behind.
"JavaScript developers can’t build anything except web applications." Every time I hear this, my reaction is — Really? 🤔
Many people think JavaScript is limited to just web development, but that’s far from the truth. Trust me, JavaS...]]></description><link>https://blog.virajnikam.in/build-desktop-applications-with-tauri</link><guid isPermaLink="true">https://blog.virajnikam.in/build-desktop-applications-with-tauri</guid><category><![CDATA[Tauri]]></category><category><![CDATA[Rust]]></category><category><![CDATA[desktop]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Mon, 04 Aug 2025 21:46:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754343971969/98d3b76a-c312-47e6-8547-19d74b999ebc.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-1-story-behind">1. Story behind.</h3>
<p>"JavaScript developers can’t build anything except web applications." Every time I hear this, my reaction is — Really? 🤔</p>
<p>Many people think JavaScript is limited to just web development, but that’s far from the truth. Trust me, JavaScript is almost everywhere.</p>
<p>In this article, we’ll explore how JavaScript can be used to build desktop applications using Tauri.</p>
<p>For context, Tauri uses Rust under the hood for its backend.</p>
<h3 id="heading-2setup-tauri-in-existing-react-project">2.Setup tauri in existing React Project.</h3>
<ol>
<li>step one install tauri cli</li>
</ol>
<pre><code class="lang-bash">npm install -g @tauri-apps/cli
// or <span class="hljs-keyword">if</span> you prefer using it locally <span class="hljs-keyword">then</span>
npm install --save-dev @tauri-apps/cli
</code></pre>
<ol start="2">
<li>Install tauri in your project</li>
</ol>
<pre><code class="lang-bash">npm install @tauri-apps/api  // This lets your React frontend talk to the Tauri backend (eg. <span class="hljs-keyword">for</span> filesystem, dialogs, etc.)
npx tauri init
</code></pre>
<p>This will add src-tauri folder and configure everything.</p>
<ol start="3">
<li>To run the tauri app</li>
</ol>
<pre><code class="lang-powershell">npm run tauri dev
</code></pre>
<h3 id="heading-3-new-tauri-app-setup">3. New Tauri App Setup.</h3>
<p>If you are starting with the tauri i would recommend you to start it from scratch. this would be easy to understand the setup.</p>
<pre><code class="lang-bash">npm create tauri-app@latest
</code></pre>
<p>follow the the instructions to setup the ui library, language preference and other necessary things.</p>
<ol>
<li><p>select the unique identifier for the application. (it’s mostly recommended to put you business domain)</p>
<pre><code class="lang-bash"> &gt;&gt; Identifier (com.tauri-app.app)
</code></pre>
</li>
<li><p>select the language for building frontend (UI layer), I recommend to use Typescript/Javascript</p>
<pre><code class="lang-bash"> &gt;&gt; Choose <span class="hljs-built_in">which</span> language to use <span class="hljs-keyword">for</span> your frontend
 Rust (Cargo)
 Typescript / Javascript (npm, yarn, pnpm, bun)
 .NET
</code></pre>
</li>
<li><p>select the package manager.</p>
<pre><code class="lang-powershell"> &gt;&gt; Choose your package manager 
 pnpm
 yarn
 npm
 bun
</code></pre>
</li>
<li><p>select the UI template</p>
<pre><code class="lang-powershell"> &gt;&gt; Choose your UI template 
 Vanilla
 Vue
 Svelte
 React
 Solid
 Angular
 Preact

 &gt;&gt; Choose your UI flavor 
 TypeScript
 JavaScript
</code></pre>
</li>
<li><p>install the libraries from package.json</p>
<pre><code class="lang-powershell"> <span class="hljs-built_in">cd</span> /project_name
 npm install
</code></pre>
</li>
<li><p>Run development server.</p>
<pre><code class="lang-powershell"> npm run tauri dev
</code></pre>
<p> You’ll now see a new window open with your app running.</p>
<p> Congratulations! We did it <strong>🤝</strong></p>
</li>
</ol>
<h3 id="heading-4-communication-between-ui-layer-and-backend-rust">4. Communication between UI layer and backend (Rust)</h3>
<p>To build the interactive application we need to keep the backend and the UI layer in sync, so for that we get the invoke api from the tauri, where we will create the function with all the logic at rust side and will call it with the help of invoke function from the UI layer using @tauri-apps/api.</p>
<ol>
<li>Create the function at rust side.</li>
</ol>
<pre><code class="lang-powershell">use tauri::{App, Manager};

<span class="hljs-comment">#[tauri::command]</span>
fn greet(name: &amp;str) -&gt; String {
    format!(<span class="hljs-string">"Hello, {}!"</span>, name)
}

fn main() {
    tauri::Builder::default()
        .setup(|app: &amp;mut App| {
            // Optional: <span class="hljs-keyword">do</span> something with app
                let win = app.get_window(<span class="hljs-string">"main"</span>).unwrap(); //This requires Manager because get_window is from the Manager trait.
                win.set_title(<span class="hljs-string">"New Title"</span>).unwrap();
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![<span class="hljs-type">greet</span>])
        .run(tauri::generate_context!())
        .expect(<span class="hljs-string">"error while running tauri application"</span>);
}
</code></pre>
<ol start="2">
<li>Now we can call the same function from react component</li>
</ol>
<pre><code class="lang-powershell">import { useState } from <span class="hljs-string">'react'</span>
import { invoke } from <span class="hljs-string">'@tauri-apps/api'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span><span class="hljs-params">()</span></span> {
  const [<span class="hljs-type">name</span>, <span class="hljs-type">setName</span>] = useState(<span class="hljs-string">''</span>)
  const [<span class="hljs-type">greeting</span>, <span class="hljs-type">setGreeting</span>] = useState(<span class="hljs-string">''</span>)

  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greetUser</span><span class="hljs-params">()</span></span> {
    const msg = await invoke(<span class="hljs-string">'greet'</span>, { name })
    setGreeting(msg)
  }

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;input value={name} onChange={(e) =&gt; setName(e.target.value)} /&gt;
      &lt;button onClick={greetUser}&gt;Greet&lt;/button&gt;
      &lt;p&gt;{greeting}&lt;/p&gt;
    &lt;/div&gt;
  )
}

export default App
</code></pre>
<p>This was all to make the communication between frontend and backend.</p>
<h3 id="heading-5-understanding-setup-app">5. Understanding .Setup( | app | { … })</h3>
<p>Understanding .Setup( | app | { … }) block in Tauri is very powerful. It's called right before your Tauri app launches, and it gives you access to the whole native backend (App, windows, plugins etc.).</p>
<p>It’s a lifecycle hook in tauri that runs: after the tauri initializes, but before the app window is shown. This is where you can configure or access window, Register Global State, Communicate with frontend or Use Plugins.</p>
<ol>
<li><p><strong>Access and Control Windows</strong></p>
<pre><code class="lang-powershell"> .setup(|app| {
     let win = app.get_window(<span class="hljs-string">"main"</span>).unwrap();
     win.set_title(<span class="hljs-string">"My App Title"</span>).unwrap();
     win.set_always_on_top(true).unwrap();
 })

 //You can use all the methods provided by tauri::Window
</code></pre>
</li>
<li><p><strong>Emit Events to Frontend</strong></p>
<pre><code class="lang-bash"> // Rust side (Backend)
 <span class="hljs-built_in">let</span> win = app.get_window(<span class="hljs-string">"main"</span>).unwrap();
 win.emit(<span class="hljs-string">"backend-ready"</span>, <span class="hljs-string">"Tauri backend initialized"</span>).unwrap();
</code></pre>
<pre><code class="lang-bash"> //React side (Frontend)
 //Listen this at react side 

 import { listen } from <span class="hljs-string">'@tauri-apps/api/event'</span>;
 listen(<span class="hljs-string">'backend-ready'</span>, (event) =&gt; {
   console.log(event.payload);
 });
</code></pre>
</li>
</ol>
<h3 id="heading-6-know-more-about-webview">6. Know More about WebView</h3>
<p>We can actually control Webview over here inside setup. we can control closeing of application waking application on start on the machine and lot more. Below is the example of how we can restrict user from closing the window and auto launching application on start of machine.</p>
<pre><code class="lang-powershell">.setup(|app| {
            let app_handle = app.handle();

            // Show main window on startup
            <span class="hljs-keyword">if</span> let Some(window) = app.get_webview_window(<span class="hljs-string">"main"</span>) {
                <span class="hljs-keyword">if</span> let Err(e) = window.show() {
                    eprintln!(<span class="hljs-string">"Failed to show window: {}"</span>, e);
                }
                <span class="hljs-keyword">if</span> let Err(e) = window.set_focus() {
                    eprintln!(<span class="hljs-string">"Failed to focus window: {}"</span>, e);
                }

                // Prevent window from closing
                window.on_window_event(<span class="hljs-built_in">move</span> |event| {
                    <span class="hljs-keyword">if</span> let tauri::WindowEvent::CloseRequested { api, .. } = event {
                        api.prevent_close();
                        println!(<span class="hljs-string">"Close prevented - application will continue running"</span>);
                    }
                });
            }

            <span class="hljs-comment">#[cfg(desktop)]</span>
            {
                // Enable autostart <span class="hljs-keyword">if</span> not already enabled
                let autostart_manager = app.autolaunch();
                <span class="hljs-keyword">if</span> let Ok(false) = autostart_manager.is_enabled() {
                    match autostart_manager.enable() {
                        Ok(_) =&gt; println!(<span class="hljs-string">"Autostart enabled successfully."</span>),
                        Err(e) =&gt; eprintln!(<span class="hljs-string">"Failed to enable autostart: {}"</span>, e),
                    }
                }
            }

            Ok(())
        })
</code></pre>
<h3 id="heading-7-understanding-event-emmiter">7. Understanding Event Emmiter.</h3>
<p>We will emmit the event at defined interval, and at frontend side we will receive that event and perform the specific task.</p>
<pre><code class="lang-bash">//Awaake_interval.rs

use tauri::{AppHandle, Emitter}; 
use std::time::Duration;

<span class="hljs-comment">#[tauri::command]</span>
pub async fn trigger_sync() -&gt; Result&lt;String, String&gt; {
    Ok(<span class="hljs-string">"sync_triggered"</span>.to_string())
}

pub async fn simple_background_timer(app_handle: AppHandle) {
    <span class="hljs-built_in">let</span> mut interval = tokio::time::interval(Duration::from_secs(30));

    loop {
        interval.tick().await;
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">let</span> Err(e) = app_handle.emit(<span class="hljs-string">"background_sync_trigger"</span>, ()) {
            eprintln!(<span class="hljs-string">"Failed to emit background_sync_trigger: {}"</span>, e);
        }
    }
}
</code></pre>
<pre><code class="lang-bash">
mod awake_interval


pub fn <span class="hljs-function"><span class="hljs-title">run</span></span>() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![awake_interval::trigger_sync])
        .setup(|app| {
            <span class="hljs-built_in">let</span> app_handle = app.handle();
            tauri::async_runtime::spawn(awake_interval::simple_background_timer(app_handle.clone()));

            Ok(())
        })
        .run(tauri::generate_context!())
        .expect(<span class="hljs-string">"error while running tauri application"</span>);
}
</code></pre>
<pre><code class="lang-bash">// App.jsx

import { useCallback, useEffect, useState } from <span class="hljs-string">"react"</span>;
import { listen } from <span class="hljs-string">"@tauri-apps/api/event"</span>;

const useBackgroundSync = () =&gt; {
  const [count, setCount] = useState(0);

  const performSync = async (syncFunction) =&gt; {
    try {
      <span class="hljs-keyword">if</span> (syncFunction) {
        await syncFunction();
      }
    } catch (error) {
      console.error(<span class="hljs-string">"Sync error:"</span>, error);
    }
  };

  const setupBackgroundSync = useCallback((syncFunction) =&gt; {
    <span class="hljs-built_in">let</span> unlisten;
    const setupListener = async () =&gt; {
      unlisten = await listen(<span class="hljs-string">"background_sync_trigger"</span>, () =&gt; {
        console.log(<span class="hljs-string">"Background sync trigger received"</span>);
        setCount((prevCount) =&gt; prevCount + 1);
        performSync(syncFunction);
      });
    };

    setupListener();

    <span class="hljs-built_in">return</span> () =&gt; {
      <span class="hljs-keyword">if</span> (unlisten) {
        unlisten();
      }
    };
  }, [performSync]);

  <span class="hljs-built_in">return</span> {
    count,
    performSync,
    setupBackgroundSync,
  };
};

<span class="hljs-built_in">export</span> default useBackgroundSync;
</code></pre>
<h3 id="heading-8-network-calls">8. Network Calls.</h3>
<p>We cannot use native fetch or any library just like axios to make the network calls. tauri restricts this to prevent unauthorized or malicious requests. For this tauri gives as <strong>http plugin.</strong></p>
<p>This plugin provides a safe bridge for network access in a desktop environment where direct access could pose security risks. Tauri enforces strict security by requiring you to whitelist domains in the <em>tauri.config.json</em> file to prevent unauthorized or malicious requests. Without this, the app will block outgoing requests for safety.</p>
<h3 id="heading-setup-steps">Setup Steps:</h3>
<ol>
<li><p><strong>Install the plugin:</strong></p>
<pre><code class="lang-bash"> cargo add tauri-plugin-http
</code></pre>
</li>
<li><p><strong>Add the plugin in</strong> lib.rs :</p>
<pre><code class="lang-bash"> .plugin(tauri_plugin_http::init())
</code></pre>
</li>
<li><p><strong>Whitelist URLs in</strong> default.json: capablities&gt;default.json</p>
<pre><code class="lang-bash"> {
   <span class="hljs-string">"<span class="hljs-variable">$schema</span>"</span>: <span class="hljs-string">"../gen/schemas/desktop-schema.json"</span>,
   <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"default"</span>,
   <span class="hljs-string">"description"</span>: <span class="hljs-string">"Capability for the main window"</span>,
   <span class="hljs-string">"windows"</span>: [<span class="hljs-string">"main"</span>],
   <span class="hljs-string">"remote"</span>: {
     <span class="hljs-string">"urls"</span>: [
       <span class="hljs-string">"http://localhost:9000"</span>,
       <span class="hljs-string">"https://base-url/api/v1/**/**/**"</span>
     ]
   },
   <span class="hljs-string">"permissions"</span>: [
     <span class="hljs-string">"core:default"</span>,
     {
       <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"http:default"</span>,
       <span class="hljs-string">"allow"</span>: [
         {
           <span class="hljs-string">"url"</span>: <span class="hljs-string">"http://localhost:9000"</span>
         },
         {
           <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://base-url/api/v1/**/**/**"</span>
         }
       ]
     }
   ]
 }
</code></pre>
</li>
</ol>
<pre><code class="lang-bash">npm install @tauri-apps/plugin-http
</code></pre>
<p>Now we can use the fetch method from @tauri-apps/plugin-http to make the network call</p>
<pre><code class="lang-bash">//action.js

import { fetch } from <span class="hljs-string">"@tauri-apps/plugin-http"</span>;

<span class="hljs-built_in">export</span> const userLogin = async (formData) =&gt; {
  try {
    const BASE_URL = import.meta.env.VITE_BASE_URL;
    const response = await fetch(`<span class="hljs-variable">${BASE_URL}</span>/auth/login`, {
      method: <span class="hljs-string">"POST"</span>,
      headers: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      body: JSON.stringify({
        email: formData.email,
        password: formData.password,
      }),
      credentials: <span class="hljs-string">"include"</span>,
    });
    const data = await response.json();
    <span class="hljs-built_in">return</span> data;
  } catch (error) {
    console.log(error);
    throw error;
  }
};
</code></pre>
<p>Thank You! 📚</p>
]]></content:encoded></item><item><title><![CDATA[IndexedDB - browsers own storage system.]]></title><description><![CDATA[Introduction.
In a recent article, I introduced Dexie.js, a wrapper library for IndexedDB that simplifies working with it. I should have published this article about using IndexedDB with native APIs before the Dexie.js article. Why? Because only when...]]></description><link>https://blog.virajnikam.in/indexeddb-browsers-own-storage-system</link><guid isPermaLink="true">https://blog.virajnikam.in/indexeddb-browsers-own-storage-system</guid><category><![CDATA[indexeddb]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Sat, 19 Jul 2025 10:37:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752921384486/9685cc9c-232b-4d36-966a-62e5794554a2.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction.</h3>
<p>In a recent article, I introduced <strong>Dexie.js</strong>, a wrapper library for IndexedDB that simplifies working with it. I should have published this article about using <strong>IndexedDB with native APIs</strong> before the Dexie.js article. Why? Because only when you experience the complexity and pain of writing native IndexedDB queries will you truly appreciate the abstraction Dexie.js provides.</p>
<p>So let’s not dive into Dexie.js again I’ve already discussed it in detail in my earlier post. If you're curious about how Dexie helps simplify things, I recommend reading that article after finishing this one.</p>
<h3 id="heading-why-indexeddb">Why IndexedDB?</h3>
<p>You might wonder: if we have localStorage which is easy to use, why do we need IndexedDB at all?</p>
<p>That’s a fair question but only partially correct. Let's talk about localStorage first:</p>
<ul>
<li><p>localStorage has a <strong>limited capacity</strong>, usually around <strong>5–10MB</strong>.</p>
</li>
<li><p>Its operations are <strong>synchronous</strong>, meaning large data inserts or updates can block the UI and make the app unresponsive.</p>
</li>
<li><p>It’s suitable for <strong>small, frequent, and simple data</strong> operations.</p>
</li>
</ul>
<p>In contrast, <strong>IndexedDB</strong> offers:</p>
<ul>
<li><p><strong>Asynchronous</strong> operations which means it doesn’t block the main thread.</p>
</li>
<li><p>Support for <strong>structured data</strong> stored in <strong>object stores</strong> (which you can think of as tables).</p>
</li>
<li><p><strong>Indexed querying</strong>, allowing you to query data more efficiently.</p>
</li>
<li><p>Support for <strong>versioning</strong> and schema upgrades.</p>
</li>
</ul>
<p>NOTE : WE CAN ONLY INCREMENT THE DATABASE VERSION, IF WE TRY TO DECREMENT THE VERSION THE onerror EVENT WILL TRIGGER.</p>
<h3 id="heading-lets-code-setting-up-indexeddb-with-native-api">Let’s Code: Setting Up IndexedDB with Native API</h3>
<p>IndexedDB uses an <strong>event-driven</strong> architecture. So, the sequence in which you write operations doesn’t matter as much as how you handle events.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Open a database connection</span>
<span class="hljs-keyword">const</span> database = indexedDB.open(<span class="hljs-string">"MyDatabase"</span>, <span class="hljs-number">1</span>); 
<span class="hljs-comment">// Pass: database name, and version number</span>
</code></pre>
<p>step 1 : Create objectStore (Tables) and Indexes.</p>
<pre><code class="lang-javascript">database.onupgradeneeded = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> db = event.target.result;

    <span class="hljs-keyword">if</span> (!db.objectStoreNames.contains(<span class="hljs-string">"students"</span>)) {
        <span class="hljs-keyword">const</span> store = db.createObjectStore(<span class="hljs-string">"students"</span>, { <span class="hljs-attr">keyPath</span>: <span class="hljs-string">"id"</span> });

        <span class="hljs-comment">// Indexes: (indexName, keyPath, options)</span>
        store.createIndex(<span class="hljs-string">"name"</span>, <span class="hljs-string">"name"</span>, { <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span> });
        store.createIndex(<span class="hljs-string">"email"</span>, <span class="hljs-string">"email"</span>, { <span class="hljs-attr">unique</span>: <span class="hljs-literal">true</span> });
    }
};
</code></pre>
<p>step 2 : Insert Data</p>
<pre><code class="lang-javascript">database.onsuccess = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> db = event.target.result;

    <span class="hljs-keyword">const</span> transaction = db.transaction(<span class="hljs-string">"students"</span>, <span class="hljs-string">"readwrite"</span>);
    <span class="hljs-keyword">const</span> store = transaction.objectStore(<span class="hljs-string">"students"</span>);

    <span class="hljs-keyword">const</span> request = store.add({
        <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Viraj Nikam"</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">"viraj@gmail.com"</span>
    });

    request.onsuccess = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Data inserted successfully!"</span>, event.target.result);
    };

    request.onerror = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to insert data:"</span>, event.target.error);
    };
};
</code></pre>
<h3 id="heading-indexeddb-native-api-crud-operations">IndexedDB Native API – CRUD Operations</h3>
<p><strong>1. Add Data (Create)</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> request = store.add({
    <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Viraj Nikam"</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">"viraj@gmail.com"</span>
});
</code></pre>
<p><strong>2. Get Data (Read)</strong></p>
<p>a. Get by primary key (id)</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> getRequest = store.get(<span class="hljs-number">1</span>);

getRequest.onsuccess = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Record fetched:"</span>, event.target.result);
};

getRequest.onerror = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error fetching record:"</span>, event.target.error);
};
</code></pre>
<p>b. Get by Index</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> index = store.index(<span class="hljs-string">"email"</span>);
<span class="hljs-keyword">const</span> emailRequest = index.get(<span class="hljs-string">"viraj@gmail.com"</span>);

emailRequest.onsuccess = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Record fetched by email:"</span>, event.target.result);
};
</code></pre>
<p><strong>3. Update Data</strong></p>
<p>To update, you first fetch the record, modify it, and then use put() (not add(), since add() will fail if the key already exists):</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> getRequest = store.get(<span class="hljs-number">1</span>);

getRequest.onsuccess = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> data = event.target.result;
    data.name = <span class="hljs-string">"Viraj S. Nikam"</span>; <span class="hljs-comment">// Updating the name</span>

    <span class="hljs-keyword">const</span> updateRequest = store.put(data);

    updateRequest.onsuccess = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Record updated successfully!"</span>);
    };
};
</code></pre>
<p><strong>4. Delete Data</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> deleteRequest = store.delete(<span class="hljs-number">1</span>);

deleteRequest.onsuccess = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Record deleted successfully!"</span>);
};

deleteRequest.onerror = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to delete record:"</span>, event.target.error);
};
</code></pre>
<h3 id="heading-final-execution">Final Execution :</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> database = indexedDB.open(<span class="hljs-string">"MyDatabase"</span>, <span class="hljs-number">1</span>) <span class="hljs-comment">// Here you need to pass name of the database and it's version</span>

<span class="hljs-comment">// add tables to your db over here at onupgradeneeded</span>
database.onupgradeneeded(<span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
    <span class="hljs-keyword">const</span> dbInstance = evt.target.result;

    <span class="hljs-keyword">if</span>(!dbInstance.objectStoreNames.contains(<span class="hljs-string">"students"</span>)){
            <span class="hljs-keyword">const</span> request = dbInstance.createObjectStore(<span class="hljs-string">"students"</span>, {<span class="hljs-attr">keyPath</span> : <span class="hljs-string">"id"</span>});
            <span class="hljs-comment">//createIndex used to define the columns, Its necessary to define Indexes because it helps you at time of queries</span>
            request.createIndex(<span class="hljs-string">"name"</span>, <span class="hljs-string">"name"</span>, {<span class="hljs-attr">unique</span> : <span class="hljs-literal">false</span>}) <span class="hljs-comment">// takes column name, accessar key for that perticular column, and object of the constraints</span>
            request.createIndex(<span class="hljs-string">"email"</span>, <span class="hljs-string">"email"</span>, {<span class="hljs-attr">unique</span> : <span class="hljs-literal">true</span>})
    }
})

<span class="hljs-comment">//perform your queries over here</span>
database.onsuccess = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> db = event.target.result;
    <span class="hljs-keyword">const</span> transaction = db.transaction(<span class="hljs-string">"students"</span>, <span class="hljs-string">"readwrite"</span>);
    <span class="hljs-keyword">const</span> store = transaction.objectStore(<span class="hljs-string">"students"</span>);

    <span class="hljs-comment">// Add</span>
    <span class="hljs-keyword">const</span> addRequest = store.add({
        <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Jane Doe"</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">"jane@example.com"</span>
    });

    <span class="hljs-comment">// Read by ID</span>
    <span class="hljs-keyword">const</span> getRequest = store.get(<span class="hljs-number">2</span>);
    getRequest.onsuccess = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Fetched:"</span>, getRequest.result);

    <span class="hljs-comment">// Update</span>
    getRequest.onsuccess = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> data = getRequest.result;
        data.name = <span class="hljs-string">"Jane D."</span>;
        <span class="hljs-keyword">const</span> updateRequest = store.put(data);
        updateRequest.onsuccess = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Updated!"</span>);
    };

    <span class="hljs-comment">// Delete</span>
    <span class="hljs-keyword">const</span> deleteRequest = store.delete(<span class="hljs-number">1</span>);
    deleteRequest.onsuccess = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Deleted record with ID 1"</span>);
};
</code></pre>
<h3 id="heading-wrapping-up">Wrapping Up.</h3>
<p>Working with native IndexedDB can feel a bit messy and confused due to its event-based flow. That’s exactly why libraries like <strong>Dexie.js</strong> exist to make your life easier.</p>
<p>But understanding the native API is crucial for debugging, customization, and appreciating how much a wrapper is actually helping you.</p>
<p>Thank You !☺️</p>
]]></content:encoded></item><item><title><![CDATA[Dexie.js : The Smart Way to Handel Local Database in JavaScript]]></title><description><![CDATA[Introduction
If you’ve ever worked with IndexedDB, you know it can be a bit painful—lots of boilerplate and a complex API just to do simple things. That’s where Dexie.js comes in. It’s a lightweight wrapper that makes IndexedDB simple, clean, and dev...]]></description><link>https://blog.virajnikam.in/dexiejs-learn-the-basics</link><guid isPermaLink="true">https://blog.virajnikam.in/dexiejs-learn-the-basics</guid><category><![CDATA[Dexie.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[offline]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Tue, 08 Jul 2025 19:03:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752001007448/b60008f1-f777-4588-bf2b-99486755b061.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>If you’ve ever worked with <strong>IndexedDB</strong>, you know it can be a bit painful—lots of boilerplate and a complex API just to do simple things. That’s where <strong>Dexie.js</strong> comes in. It’s a lightweight wrapper that makes IndexedDB simple, clean, and developer-friendly.</p>
<p>I recently started building an <strong>offline-first application</strong>, and I needed something more powerful than localStorage and less messy than raw IndexedDB. After exploring a few options, <strong>Dexie.js</strong> turned out to be the perfect fit.</p>
<p>In this article, I’ll walk you through the basics—setting up a database, performing CRUD operations, and integrating it with <strong>React</strong> (don’t worry if you’re not into React, the concepts will still make sense).</p>
<h3 id="heading-why-i-chose-dexiejs-for-offline-data-management">Why I Chose Dexie.js for Offline Data Management.</h3>
<p>Dexie.js is a smart wrapper around <strong>IndexedDB</strong> that makes working with local databases in JavaScript much easier and cleaner. If you’ve ever tried IndexedDB directly, you know how tricky it can be. Dexie simplifies that headache.</p>
<p>While building an <strong>offline-first application</strong>, one big challenge is choosing the right way to manage local data. localStorage is too limited, and working directly with IndexedDB feels like wrestling with unnecessary complexity.</p>
<p><strong>Dexie.js</strong>—a lightweight and developer-friendly solution that keeps things simple while still giving you the power of IndexedDB under the hood.</p>
<h3 id="heading-lets-write-the-code">Let’s write the code.</h3>
<p>First, we need to set up the database instance:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Dexie <span class="hljs-keyword">from</span> <span class="hljs-string">"dexie"</span>;

<span class="hljs-keyword">const</span> db = <span class="hljs-keyword">new</span> Dexie(<span class="hljs-string">'myDatabase'</span>);
            db.version(<span class="hljs-number">1</span>).stores({
                    <span class="hljs-attr">user</span> : <span class="hljs-string">'++id, name, email, password, roleId'</span>,
                    <span class="hljs-attr">tasks</span> : <span class="hljs-string">'++id, title, description, isCompleted, createdAt, updatedAt'</span>
            })
</code></pre>
<p>That’s it! This small setup gives you two tables: user and tasks. Now you can start performing different operations without the hassle of raw IndexedDB code.</p>
<h3 id="heading-crud-operations-with-dexiejs"><strong>CRUD Operations with Dexie.js</strong></h3>
<p>Below are the most common operations:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'path-to-db'</span>
<span class="hljs-comment">//Add Task</span>
<span class="hljs-keyword">const</span> addTaskToDB = <span class="hljs-keyword">async</span>(data)=&gt;{
    <span class="hljs-keyword">await</span> db.tasks.add(data)
}

<span class="hljs-comment">//Delete Task</span>
<span class="hljs-keyword">const</span> deleteTaskById = <span class="hljs-keyword">async</span>(id)=&gt;{
    <span class="hljs-keyword">await</span> db.tasks.delete(id)
}

<span class="hljs-comment">//Update Task</span>
<span class="hljs-keyword">const</span> updateTask = <span class="hljs-keyword">async</span>(data)=&gt;{
    <span class="hljs-keyword">await</span> db.tasks.put(data)
}

<span class="hljs-comment">//Get Task by Id</span>
<span class="hljs-keyword">const</span> getTaskById = <span class="hljs-keyword">async</span> (id)=&gt;{
    <span class="hljs-keyword">return</span> db.tasks.get(id)
}

<span class="hljs-comment">//Get all Tasks</span>
<span class="hljs-keyword">const</span> getAllTasks = <span class="hljs-keyword">async</span>()=&gt;{
    <span class="hljs-keyword">return</span> db.tasks.toArray();
}

<span class="hljs-keyword">export</span> {addTaskToDB}
</code></pre>
<h3 id="heading-using-dexie-in-a-react-component"><strong>Using Dexie in a React Component</strong></h3>
<p>Here’s a quick example to show how we can integrate it with React:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {addTaskToDB} <span class="hljs-keyword">from</span> <span class="hljs-string">'actions'</span>
<span class="hljs-keyword">const</span> MyReactComponent = <span class="hljs-function">()=&gt;</span>{
    <span class="hljs-keyword">const</span> [newTask, setNewTask] = useState({
            <span class="hljs-attr">title</span> : <span class="hljs-string">""</span>,
            <span class="hljs-attr">description</span> : <span class="hljs-string">""</span>
      });

    <span class="hljs-keyword">const</span> addTask = <span class="hljs-keyword">async</span>()=&gt;{
        <span class="hljs-keyword">await</span> addTaskToDB(newTask);
         globalThis.alert(<span class="hljs-string">"Task Added"</span>)
    }

    <span class="hljs-keyword">const</span> handelTaskInputChange = <span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
        <span class="hljs-keyword">const</span> {name, value} = evt.target;
        setNewTask(<span class="hljs-function">(<span class="hljs-params">prev</span>)=&gt;</span>({...prev, [name]: value}))
     }


    <span class="hljs-keyword">return</span>(
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{addTask}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelTaskInputChange}</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{newTask.title}/</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelTaskInputChange}</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{newTask.description}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Add Task +<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    )
}</span>
</code></pre>
<h3 id="heading-advance-queries"><strong>Advance Queries</strong></h3>
<ol>
<li><p>Suppose you need to update specific fields (like name) for multiple rows in a Dexie.js table based on their id. A common real-world scenario is when you have an array of objects, and you want to update the database to reflect new values where the id matches.</p>
<p> Let’s break this down with an example:</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> data = [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"Sarthak"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"Shubham"</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"Rahul"</span> }
];

<span class="hljs-keyword">await</span> db.tableName
  .where(<span class="hljs-string">"id"</span>)
  .anyOf(data.map(<span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.id))
  .modify(<span class="hljs-function">(<span class="hljs-params">record</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> updated = data.find(<span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.id === record.id);
    <span class="hljs-keyword">if</span> (updated) {
      record.name = updated.name;
    }
  });
</code></pre>
<h3 id="heading-final-thoughts"><strong>Final Thoughts</strong></h3>
<p>If you’re tired of the limitations of localStorage or the complexity of IndexedDB, Dexie.js is the perfect middle ground. It’s fast, reliable, and a great tool for building <strong>offline-first applications</strong>.</p>
<p>I hope this guide helped you get started. If you’ve used Dexie before, share your experience in the comments!</p>
]]></content:encoded></item><item><title><![CDATA[React Hook Form]]></title><description><![CDATA[Lesson 1: Story behind.
Before I discovered React Hook Form, I realized I was a complete noob when it came to handling form data in React. I used to manage form state and validations using local state variables. While this approach works for simple f...]]></description><link>https://blog.virajnikam.in/react-hook-form</link><guid isPermaLink="true">https://blog.virajnikam.in/react-hook-form</guid><category><![CDATA[React]]></category><category><![CDATA[react-hook-form]]></category><category><![CDATA[forms]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Viraj Nikam]]></dc:creator><pubDate>Fri, 27 Jun 2025 13:37:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750998019497/803b8534-10d2-4161-97e5-6fff311f00d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-lesson-1-story-behind">Lesson 1: Story behind.</h3>
<p>Before I discovered <strong>React Hook Form</strong>, I realized I was a complete <strong>noob</strong> when it came to handling form data in React. I used to manage form state and validations using local state variables. While this approach works for <strong>simple forms</strong>, it quickly becomes <strong>messy and hard to scale</strong> as your form grows.</p>
<p>To make things more maintainable, I thought of lifting the state to a <strong>global level</strong> using something like <strong>Context API</strong> or even a state management library like Redux or Zustand. But that also felt like <strong>overkill</strong> for just managing form inputs and validations.</p>
<h3 id="heading-lesson-2-introduction">Lesson 2: Introduction.</h3>
<p><mark>React Hook Form is a lightweight library which uses react hooks to manage the form state and validations. </mark> It fits right into your existing react component structure, making it easy to build and manage forms without a lot of extra code.</p>
<h3 id="heading-lesson-3-lets-write-and-understand-the-code">Lesson 3: let's write and understand the code.</h3>
<p>Managing form state usually means keeping track of local state with the useState hook, context api or any other state management library like redux or zustand. React hook form takes care of this by providing you its own context api.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {useForm, FormProvider} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-hook-form'</span>;


<span class="hljs-keyword">const</span> ReactHookFormExample = <span class="hljs-function">()=&gt;</span>{

  <span class="hljs-keyword">const</span> rfhMethods = useForm({
    <span class="hljs-attr">defaultValues</span> : {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"Jhon doe"</span>
            <span class="hljs-attr">mobile</span> : <span class="hljs-number">4523652145</span>
        }
    });

  <span class="hljs-keyword">const</span> {register, reset, handelSubmit, <span class="hljs-attr">formState</span>:{errors}} = rfhMethods;

  <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">(<span class="hljs-params">formData</span>)=&gt;</span>{
    <span class="hljs-built_in">console</span>.log(formData, <span class="hljs-string">"FORM_DATA"</span>);
    reset();
  }


<span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FormProvider</span> {<span class="hljs-attr">...rfhMethods</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> <span class="hljs-attr">className</span>=<span class="hljs-string">'user-name'</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> {<span class="hljs-attr">...register</span>("<span class="hljs-attr">name</span>")} /&gt;</span>
                 {errors.name &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.name.message}<span class="hljs-tag">&lt;/<span class="hljs-name">span</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> <span class="hljs-attr">className</span>=<span class="hljs-string">'user-mobile'</span>&gt;</span>
                 <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> {<span class="hljs-attr">...register</span>("<span class="hljs-attr">mobile</span>")}&gt;</span>
                  {errors.mobile &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.mobile.message}<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">FormProvider</span>&gt;</span>
        )
}</span>
</code></pre>
<p>In the above example, useForm provides several methods &amp; properties to manage the form:</p>
<ol>
<li><p><strong>register</strong> : a function to register the fields.</p>
</li>
<li><p><strong>handelSubmit</strong> : a function to handel the form submission.</p>
</li>
<li><p><strong>formState</strong> : its an object containing the form state with validation errors.</p>
</li>
</ol>
<p>FormProvider is the context provider given to us by react hook form, to handel the form state in the nested components. eg:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> {useFormContext} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-hook-form'</span>;

<span class="hljs-keyword">const</span> nestedChildComponent = <span class="hljs-function">()=&gt;</span>{
    <span class="hljs-keyword">const</span> {register} = useFormContext();

<span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'user-city'</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">select</span> {<span class="hljs-attr">...register</span>("<span class="hljs-attr">city</span>")}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'pune'</span>&gt;</span>Pune<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'amdabad'</span>&gt;</span>Amdabad<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'mumbai'</span>&gt;</span>Mumbai<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
       )
}
</code></pre>
<p>Here, with the useFormContext method you can access all the methods that useForm provides.</p>
<h3 id="heading-lesson-4-traditional-forms-in-react-why-its-a-struggle">Lesson 4 : Traditional Forms in React – Why It's a Struggle</h3>
<p>When you're building forms using plain React (also called "traditional forms"), things can quickly get messy. You have to manually handle every input, track changes, validate data, and manage errors — all by yourself. It may sound simple at first, but even a basic form can turn into a lot of boilerplate code.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, {useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">const</span> TraditionalForms = <span class="hljs-function">()=&gt;</span>{
    <span class="hljs-keyword">const</span> [formData, setFormData] = useState({<span class="hljs-attr">name</span>:<span class="hljs-string">""</span>, <span class="hljs-attr">email</span>:<span class="hljs-string">""</span>});
    <span class="hljs-keyword">const</span> [errors, setErrors] = useState({});

    <span class="hljs-keyword">const</span> handelInputChange = <span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
        <span class="hljs-keyword">const</span> {name, value} = evt?.target;
            setFormData(<span class="hljs-function">(<span class="hljs-params">prev</span>)=&gt;</span>({...prev, [name]: value}))
     }

    <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">(<span class="hljs-params">evt</span>)=&gt;</span>{
        evt.preventDefault();

        <span class="hljs-keyword">const</span> errors = {};
        <span class="hljs-keyword">if</span>(formData.name?.length &lt; <span class="hljs-number">2</span>) errors.name = <span class="hljs-string">"name should contain atlist 2 characters"</span>;
        <span class="hljs-keyword">if</span>(!<span class="hljs-regexp">/\S+@\S+\.\S+/</span>.test(formData.email)) errors.email = <span class="hljs-string">"enter valid email"</span>;

        setErrors(errors);

        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">Object</span>.keys(errors).length===<span class="hljs-number">0</span>){
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"FORM_DATA"</span>, formData);
          }

    };


    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{onSubmit}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'name'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelInputChange}</span> /&gt;</span>
            {errors?.name &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>}

            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'email'</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'email'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.email}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handelInputChange}</span> /&gt;</span>
            {errors?.email &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{errors.email}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>}

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span>&gt;</span> Submit <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span> //type : "submit" | "button" | "reset" by default to submit
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
           )
</code></pre>
<p>If you're just starting out, building forms manually is a great way to learn. But as your project grows, you'll definitely want to use a form library to avoid drowning in state management and validation logic.</p>
<p><strong>Traditional forms teach you the basics — but modern form libraries help you move faster and write cleaner code.</strong></p>
]]></content:encoded></item></channel></rss>