<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Praveen Sripati</title>
    <description>The latest articles on DEV Community by Praveen Sripati (@praveen-sripati).</description>
    <link>https://dev.to/praveen-sripati</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3263039%2Fe4ff8b2c-f74d-4396-b578-3d6cb6708419.jpeg</url>
      <title>DEV Community: Praveen Sripati</title>
      <link>https://dev.to/praveen-sripati</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/praveen-sripati"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Wed, 23 Jul 2025 06:42:51 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/-37o0</link>
      <guid>https://dev.to/praveen-sripati/-37o0</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c" class="crayons-story__hidden-navigation-link"&gt;Introducing Nexus: A Polished Dashboard Built with Vite, React, and Shadcn/ui&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/praveen-sripati" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3263039%2Fe4ff8b2c-f74d-4396-b578-3d6cb6708419.jpeg" alt="praveen-sripati profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/praveen-sripati" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen Sripati
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen Sripati
                
              
              &lt;div id="story-author-preview-content-2708347" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/praveen-sripati" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3263039%2Fe4ff8b2c-f74d-4396-b578-3d6cb6708419.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen Sripati&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 20 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c" id="article-link-2708347"&gt;
          Introducing Nexus: A Polished Dashboard Built with Vite, React, and Shadcn/ui
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontendchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontendchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/css"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;css&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;61&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              18&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Introducing Nexus: A Polished Dashboard Built with Vite, React, and Shadcn/ui</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Sun, 20 Jul 2025 15:20:01 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c</link>
      <guid>https://dev.to/praveen-sripati/introducing-nexus-a-polished-dashboard-built-with-vite-react-and-shadcnui-494c</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend/axero"&gt;Frontend Challenge: Office Edition sponsored by Axero, Holistic Webdev: Office Space&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;For this challenge, I built "Nexus", a modern, single-page intranet dashboard designed to be a company's digital heartbeat. My goal was to create a clean, intuitive interface that masterfully balances high-efficiency productivity with vibrant community engagement, directly addressing the core challenge of information overload in the modern workplace.&lt;/p&gt;

&lt;p&gt;The layout is a responsive two-column design featuring a main content area and a right sidebar for tools and events. Nexus provides a central hub for company news, team events, and essential tools, all while fostering a positive culture through features like a "Kudos" feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;You can experience the live application and view the complete source code at the links below.&lt;/p&gt;

&lt;p&gt;Live Demo URL: &lt;a href="https://nexus-drab-ten.vercel.app/" rel="noopener noreferrer"&gt;https://nexus-drab-ten.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub Repository: &lt;a href="https://github.com/praveen-sripati/nexus" rel="noopener noreferrer"&gt;https://github.com/praveen-sripati/nexus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa94e2n9lbx0h5k1cyucy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa94e2n9lbx0h5k1cyucy.gif" alt="App Gif" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;My journey in building Nexus was a fantastic deep dive into creating a polished application using a modern frontend stack: React, Vite, TypeScript, Tailwind CSS, and shadcn/ui.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Process &amp;amp; Key Decisions
&lt;/h3&gt;

&lt;p&gt;From the start, I began by setting up the project with Vite for a blazing-fast development experience. The choice of TypeScript was deliberate to ensure type safety and code quality from the ground up.&lt;/p&gt;

&lt;p&gt;For the UI, the combination of Tailwind CSS and shadcn/ui was a game-changer. Tailwind's utility-first approach made it easy to implement the responsive two-column layout, while shadcn/ui provided beautifully designed, accessible components like Cards, Buttons, and Dropdown Menus, which saved a huge amount of time.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Choice I'm Proud Of: The Custom Theme Provider
&lt;/h3&gt;

&lt;p&gt;One of the first features I implemented was a light/dark mode theme switcher. Instead of reaching for a library like next-themes, I decided to build it from scratch. This was a great learning experience and something I'm particularly proud of.&lt;/p&gt;

&lt;p&gt;Ultimately, this project was a rewarding challenge that took me from a detailed concept to a complete, deployed, and polished application.&lt;/p&gt;

&lt;p&gt;Thanks to DEV and Axero for the opportunity.&lt;/p&gt;

&lt;p&gt;Made with ❤️ by Praveen Sripati&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DEV Username: &lt;a class="mentioned-user" href="https://dev.to/praveen-sripati"&gt;@praveen-sripati&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The Passwordless Future: A Security Upgrade or a Costly Mistake?</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Fri, 11 Jul 2025 08:05:32 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/the-passwordless-future-a-security-upgrade-or-a-costly-mistake-207</link>
      <guid>https://dev.to/praveen-sripati/the-passwordless-future-a-security-upgrade-or-a-costly-mistake-207</guid>
      <description>&lt;p&gt;For decades, we’ve relied on complex passwords to guard our digital lives. But let’s be honest: they’re often weak, reused, and a pain to remember. Enter passwordless authentication, a modern solution promising a future free from &lt;code&gt;P@$$w0rd123!&lt;/code&gt;. But is it actually safer?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;So, What is Passwordless Authentication?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It’s any method that verifies who you are without asking for a secret you have to remember. Instead, it relies on something unique you have or something unique you are.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Biometrics:&lt;/strong&gt; Uses your unique physical traits like a fingerprint scan or facial recognition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Magic Links &amp;amp; OTPs:&lt;/strong&gt; Sends a one-time login link or code to your trusted email or phone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware Keys:&lt;/strong&gt; Relies on a physical USB or NFC device that you plug in or tap to approve a login.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authenticator Apps:&lt;/strong&gt; Generates a constantly changing, time-sensitive code on your smartphone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Does This Look Like in Code?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ever wondered what happens behind the scenes? While the full cryptographic process is complex, the basic flow is surprisingly straightforward. Here’s a simplified peek at how a browser and a server might interact during a “passkey” registration using the &lt;strong&gt;WebAuthn API&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Client-Side&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This code asks the server for a challenge, then tells the browser to use that challenge to create a new passkey, which triggers your device’s built-in security like Face ID or a fingerprint scan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Get a unique challenge from the server&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/register-challenge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Ask the browser's API to create a new passkey&lt;/span&gt;
&lt;span class="c1"&gt;//    This prompts the user for their fingerprint, face, etc.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Send the newly created credential to the server for verification&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/register-verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Server-Side&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The server generates the challenge, and then later verifies the credential sent back from the browser to ensure it’s valid before saving it for the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Endpoint to provide the secure challenge&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/register-challenge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateSecureRandomChallenge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Store challenge to verify later&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;challenge&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Endpoint to verify the new credential and save it&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/register-verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storedChallenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Verify the credential against the stored challenge&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isCredentialValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;storedChallenge&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If valid, save the user's public key for future logins&lt;/span&gt;
    &lt;span class="nf"&gt;savePublicKeyForUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what’s missing? &lt;strong&gt;A password.&lt;/strong&gt; The browser and your device handle the secure creation of the credential. This fundamental shift is what makes the system so powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Good vs. The Bad: A Quick Rundown&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Passwordless methods offer a huge leap forward, but it’s not a perfect system. Here are the pros and cons in a nutshell.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The “Good” 👍&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Makes Phishing Nearly Impossible:&lt;/strong&gt; Hackers can’t steal a password that doesn’t exist.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eliminates Brute-Force Attacks:&lt;/strong&gt; Guessing millions of password combinations becomes obsolete.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offers a Smoother User Experience:&lt;/strong&gt; Logging in is faster, easier, and much less frustrating.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provides Stronger Security by Default:&lt;/strong&gt; Most methods are inherently multi-factor, boosting safety.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The “Bad” 👎&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Poses a Lost Device Risk:&lt;/strong&gt; Losing your phone could mean losing your primary login key.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creates Implementation Hurdles:&lt;/strong&gt; Shifting legacy systems to passwordless can be complex for businesses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Raises Biometric Privacy Concerns:&lt;/strong&gt; Your unique biological data needs to be stored securely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Varies in Strength:&lt;/strong&gt; A simple email link is less secure than a dedicated hardware key.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Is It Really Safer?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Yes, the consensus among security experts is that &lt;strong&gt;a well-implemented passwordless system is far more secure than traditional passwords.&lt;/strong&gt; It tackles the single biggest vulnerability: human error.&lt;/p&gt;

&lt;p&gt;For a truly secure passwordless future, we need to focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iron-Clad Account Recovery:&lt;/strong&gt; You must have a secure way to get back in if you lose your device.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Layered, Multi-Factor Approach:&lt;/strong&gt; Combining methods, like a fingerprint scan to approve a passkey, is key.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clear User Education:&lt;/strong&gt; Users need to understand how to protect their new digital keys.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While we haven’t ditched every single password just yet, the shift is happening fast. The future of your digital identity is shaping up to be more secure, and thankfully, a lot less forgettable.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Micro-Frontends Explained: Is It Time to Break Up Your Monolith?</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Wed, 02 Jul 2025 20:35:54 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/micro-frontends-explained-is-it-time-to-break-up-your-monolith-2ccm</link>
      <guid>https://dev.to/praveen-sripati/micro-frontends-explained-is-it-time-to-break-up-your-monolith-2ccm</guid>
      <description>&lt;p&gt;Have you heard the buzz? It seems like everyone in the tech world is talking about “micro-frontends.” It’s a term that gets thrown around in team meetings and on tech blogs, often hailed as the future of web development. But what does it actually mean? Is it just hype, or is it a revolutionary approach you should be considering?&lt;/p&gt;

&lt;p&gt;Let’s break it down in simple terms, ditch the complex jargon, and use some visuals to understand why so many are making the switch — and if it’s the right move for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is a Monolithic Front-End?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Imagine your entire website’s front-end is like one giant, solid block. All the pieces — the user profiles, the product pages, the checkout process — are tightly coupled together in a single codebase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kddp4bxirl8tawbyva3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kddp4bxirl8tawbyva3.jpeg" alt="Monolithic" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;monolithic architecture&lt;/strong&gt;. It’s a single, unified codebase for the entire user interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is a Micro-Frontend?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now, imagine breaking that giant block into smaller, independent cubes. Each cube is its own mini-application (like the search bar or shopping cart), but they all connect and work together to form the complete structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7il91ejohtjr62ap95e.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7il91ejohtjr62ap95e.jpeg" alt="Micro-Frontend" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;micro-frontend architecture&lt;/strong&gt;. You’re splitting a large front-end into smaller, manageable pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Micro-Frontends (The Pros)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Faster, Independent Teams:&lt;/strong&gt; Small teams can own a specific feature (e.g., the search bar, the shopping cart). They can develop, test, and deploy their feature without waiting for other teams. This means faster updates and innovation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freedom to Choose Technology:&lt;/strong&gt; One team can use React, another can use Vue, and a third can use Angular — all within the same application. This allows teams to use the best tool for their specific job and makes it easier to try new technologies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easier to Scale:&lt;/strong&gt; As your company grows, you can easily add more teams to work on new features without creating a complex, tangled codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved Fault Isolation:&lt;/strong&gt; If one micro-frontend has a bug or fails, it doesn’t take down the entire website. The rest of the application can continue to function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simpler Codebases:&lt;/strong&gt; Each micro-frontend has a smaller, more focused codebase, making it easier for developers to understand, maintain, and update.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why You Might Want to Wait (The Cons)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Increased Complexity:&lt;/strong&gt; You now have multiple codebases, multiple deployment pipelines, and the challenge of making them all work together seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintaining a Consistent Look and Feel:&lt;/strong&gt; Ensuring that all the different parts of your application look and feel like a single, cohesive product requires strong communication and a shared design system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Potential for Larger Bundle Sizes:&lt;/strong&gt; If not managed carefully, having multiple frameworks and duplicated dependencies can lead to slower load times for the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More Operational Overhead:&lt;/strong&gt; Managing the infrastructure for multiple small applications can be more challenging than for a single large one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;So, Should You Switch?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Consider micro-frontends if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have a large, complex application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You have multiple teams that need to work independently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You are struggling with slow development cycles and deployment bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You might want to stick with a monolith if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have a small to medium-sized application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You have a small development team.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The complexity of managing a distributed system outweighs the benefits for your project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;References to deep dive:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://micro-frontends.org/" rel="noopener noreferrer"&gt;Micro Frontends Main Site&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/pulse/micro-frontends-from-begining-expert-rany-elhousieny-phd%E1%B4%AC%E1%B4%AE%E1%B4%B0" rel="noopener noreferrer"&gt;Micro Frontends From Beginner to Expert&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/pulse/micro-frontends-what-why-how-rany-elhousieny-phd%E1%B4%AC%E1%B4%AE%E1%B4%B0/" rel="noopener noreferrer"&gt;Micro Frontends What, Why, and How&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/nerd-for-tech/micro-front-ends-hands-on-project-63bd3327e162" rel="noopener noreferrer"&gt;Understanding Micro Frontends With a Hands-On Example Using React, Webpack 5, and Module Federation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/nerd-for-tech/micro-frontends-hands-on-example-using-react-webpack-5-and-module-federation-adding-a-third-2fe8c61a73f" rel="noopener noreferrer"&gt;Micro Frontends Hands-On Example Using React, Webpack 5, and Module Federation: Adding a third React Micro Frontend&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Books&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.oreilly.com/library/view/building-micro-frontends/9781492082989/" rel="noopener noreferrer"&gt;Building Micro-Frontends by Luca Mezzalira&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.packtpub.com/product/the-art-of-micro-frontends/9781800563568" rel="noopener noreferrer"&gt;The Art of Micro Frontends by Florian Rappl&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;/em&gt; &lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;https://x.com/praveen_sripati&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Map, Set, Go! A Quick Guide to Modern JavaScript Data Structures</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Fri, 27 Jun 2025 13:30:17 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/map-set-go-a-quick-guide-to-modern-javascript-data-structures-o97</link>
      <guid>https://dev.to/praveen-sripati/map-set-go-a-quick-guide-to-modern-javascript-data-structures-o97</guid>
      <description>&lt;p&gt;A quick guide to choosing the right data structure over plain Objects and Arrays.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Map&lt;/code&gt; vs. &lt;code&gt;Object&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Think of a &lt;code&gt;Map&lt;/code&gt; as a &lt;strong&gt;perfect dictionary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With a regular JavaScript &lt;code&gt;Object&lt;/code&gt;, the "words" (keys) you look up can only be simple text (strings). This is limiting.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Map&lt;/code&gt; is better because the keys can be &lt;strong&gt;anything&lt;/strong&gt;—a number, a boolean, or even another object. It remembers the exact key you used. It also reliably keeps everything in the order you inserted it.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;Map&lt;/code&gt; when you need more than just string keys, care about insertion order, or require frequent additions and deletions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ucwjhhldls2dnft72q9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ucwjhhldls2dnft72q9.png" alt="Map Diff" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Map&lt;/code&gt; - Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// --- Creation ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// --- Set / Add ---&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newValue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Object as a key&lt;/span&gt;

&lt;span class="c1"&gt;// --- Get ---&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'newValue'&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// 'value2'&lt;/span&gt;

&lt;span class="c1"&gt;// --- Check Existence ---&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;

&lt;span class="c1"&gt;// --- Delete ---&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;

&lt;span class="c1"&gt;// --- Size ---&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;

&lt;span class="c1"&gt;// --- Iterate ---&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- Clear ---&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;Set&lt;/code&gt; vs. &lt;code&gt;Array&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Think of a &lt;code&gt;Set&lt;/code&gt; as a &lt;strong&gt;members-only guest list&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On a guest list, each person is unique; you can’t be on the list twice. The main purpose is to quickly check if someone is on the list.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Set&lt;/code&gt; does exactly this: it only stores &lt;strong&gt;unique values&lt;/strong&gt;. If you try to add something that's already there, it's simply ignored. Its main strength is being incredibly fast at checking for an item with &lt;code&gt;.has()&lt;/code&gt;, much faster than searching through a large array.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;Set&lt;/code&gt; when you need to store a collection of unique values and perform fast membership checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcjpjr5troob4sxhc00q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcjpjr5troob4sxhc00q.png" alt="Set Diff" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Set&lt;/code&gt; - Quick Reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// --- Creation / Remove Duplicates ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myArr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myArr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Set(4) { 1, 2, 3, 4 }&lt;/span&gt;

&lt;span class="c1"&gt;// --- Add ---&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Ignored, 1 already exists&lt;/span&gt;

&lt;span class="c1"&gt;// --- Check Existence ---&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;

&lt;span class="c1"&gt;// --- Delete ---&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;

&lt;span class="c1"&gt;// --- Size ---&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 4&lt;/span&gt;

&lt;span class="c1"&gt;// --- Iterate ---&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- Clear ---&lt;/span&gt;
&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// --- Convert back to Array ---&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniqueArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;mySet&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// [1, 2, 3, 5]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;/em&gt; &lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;https://x.com/praveen_sripati&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>map</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>How I Built a Multi-Theme System using New Tailwind CSS v4 &amp; React</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Wed, 25 Jun 2025 08:08:36 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/how-i-built-a-multi-theme-system-using-new-tailwind-css-v4-react-27j3</link>
      <guid>https://dev.to/praveen-sripati/how-i-built-a-multi-theme-system-using-new-tailwind-css-v4-react-27j3</guid>
      <description>&lt;p&gt;It all started with a simple, familiar goal: I wanted to add a dark mode to my new React application. It’s practically a requirement these days. But as I started digging into the latest tools, specifically the Vite-powered React and the newly released Tailwind CSS v4, I realized I was thinking too small.&lt;/p&gt;

&lt;p&gt;What if I could go beyond just a light and dark theme? What if users could choose their own accent colors? A deep blue, a vibrant green, maybe even a warm orange?&lt;/p&gt;

&lt;p&gt;This post is the story of that journey. I’ll walk you through how I built a flexible, multi-theme system that’s not only powerful but also surprisingly elegant, thanks to the new CSS-first approach in Tailwind v4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bubwe4pssofll51pwsf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bubwe4pssofll51pwsf.webp" alt="Screenshot of App" width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tailwind v4’s New Philosophy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;My first surprise was that the old way of thinking about Tailwind — heavy on the &lt;code&gt;tailwind.config.js&lt;/code&gt; file—has fundamentally changed. With Tailwind v4 and its Vite plugin, the configuration becomes incredibly minimal. The real power has moved directly into my CSS file.&lt;/p&gt;

&lt;p&gt;The hero of this new approach is a tool that’s been in our browsers all along: &lt;strong&gt;CSS Custom Properties (Variables)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of defining colors in a JavaScript object, I could now define them as native CSS variables. This felt right. It meant my theming logic would live where it belongs — in the CSS — and could be dynamically controlled by my React application.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Laying the Foundation:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Everything starts in &lt;code&gt;src/index.css&lt;/code&gt;. This single file became the heart of the entire theming system.&lt;/p&gt;

&lt;p&gt;First, I added a professional-grade font, “Inter,” from Google Fonts to give the UI a clean, modern feel.&lt;/p&gt;

&lt;p&gt;Then, I defined all my theme colors inside a &lt;code&gt;@layer base&lt;/code&gt; block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* src/index.css */&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;import&lt;/span&gt; &lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&amp;amp;display=swap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;layer&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* 1. Default (Light) Theme */&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;FFFFFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;020817&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;FFFFFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;020817&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;E40AF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;EFF6FF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;F1F5F9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;muted&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;64748&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;E2E8F0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 2. Dark Theme */&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;020817&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;F8FAFC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;F172A&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cm"&gt;/* ...and so on for all dark mode variables */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/* 3. Accent Themes */&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="nx"&gt;A34A&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;F0FDF4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="nx"&gt;C55E&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;052&lt;/span&gt;&lt;span class="nx"&gt;E16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic here is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;:root&lt;/code&gt; holds the default (light theme) variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;.dark&lt;/code&gt; class overrides these variables when it's applied to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Other classes like &lt;code&gt;.theme-green&lt;/code&gt; can be applied alongside &lt;code&gt;.dark&lt;/code&gt; or &lt;code&gt;.light&lt;/code&gt; to specifically change the &lt;code&gt;--primary&lt;/code&gt; colors, giving me an independent accent system.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final piece of the CSS puzzle was connecting these variables to Tailwind. This is done with the &lt;code&gt;@theme&lt;/code&gt; directive. It tells Tailwind, "Hey, when you see &lt;code&gt;bg-primary&lt;/code&gt;, I want you to use my &lt;code&gt;--primary&lt;/code&gt; variable."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* src/index.css (at the bottom) */&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;The&lt;/strong&gt; &lt;code&gt;useTheme&lt;/code&gt; Hook
&lt;/h2&gt;

&lt;p&gt;With my CSS engine ready, I needed a way to control it from my React app. I created a custom hook, &lt;code&gt;useTheme&lt;/code&gt;, to act as the central brain for all theme-related logic.&lt;/p&gt;

&lt;p&gt;Its jobs are simple but crucial:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Keep track of the current theme (&lt;code&gt;light&lt;/code&gt;/&lt;code&gt;dark&lt;/code&gt;) and accent color in a React state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read the user’s last choice from &lt;code&gt;localStorage&lt;/code&gt; so the theme persists.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply or remove the correct classes (&lt;code&gt;.dark&lt;/code&gt;, &lt;code&gt;.theme-green&lt;/code&gt;) on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element whenever the state changes.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/hooks/useTheme.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAccent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle light/dark mode&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle accent color&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Add any other themes here&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevTheme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;changeAccent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newAccent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setAccent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newAccent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toggleTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changeAccent&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hook gave me a clean API — &lt;code&gt;toggleTheme()&lt;/code&gt; and &lt;code&gt;changeAccent()&lt;/code&gt;—that I could use anywhere in my app.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Building an Interface&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, I wanted to build a UI that truly showcased the power of this system. I designed a simple “Appearance” settings page. Instead of basic buttons, I created custom, interactive components.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ThemeOption&lt;/code&gt; component is a clickable card that shows a color swatch of the theme and a checkmark when it’s selected.&lt;/p&gt;

&lt;p&gt;Here’s a glimpse of the final &lt;code&gt;App.jsx&lt;/code&gt;, where I put everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/App.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useTheme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./hooks/useTheme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThemeOption&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/ThemeOption&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ... other imports&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toggleTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changeAccent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min-h-screen bg-background text-foreground ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-center mb-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-5xl font-extrabold ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Appearance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mt-3 text-lg text-muted-foreground ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Customize&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;look&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;feel&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rounded-lg border border-border bg-card ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Theme Mode Setting */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... UI for toggling light/dark mode ... */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Accent Color Setting */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-xl font-semibold ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Accent&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid grid-cols-2 sm:grid-cols-3 gap-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ThemeOption&lt;/span&gt;
              &lt;span class="nx"&gt;themeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="nx"&gt;accentColor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#60A5FA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="nx"&gt;isSelected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="nx"&gt;onSelect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;changeAccent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
            &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ThemeOption&lt;/span&gt;
              &lt;span class="nx"&gt;themeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="nx"&gt;accentColor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#22C55E&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="nx"&gt;isSelected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="nx"&gt;onSelect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;changeAccent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
            &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was a clean, responsive, and intuitive settings page where users can instantly see their changes reflected across the entire application.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;App Screenshots&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkaiz8jfd1klmiagtsuc6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkaiz8jfd1klmiagtsuc6.webp" alt="Screenshot 1" width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesfpjqfoeteavitgn8n1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesfpjqfoeteavitgn8n1.webp" alt="Screenshot 2" width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmir92smbubi52u8xz4om.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmir92smbubi52u8xz4om.webp" alt="Screenshot 3" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsbme7yedmfiykjgf3iu.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsbme7yedmfiykjgf3iu.webp" alt="Screenshot 4" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final Thoughts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By leveraging native CSS variables, I created a theming system that is not only scalable and maintainable but also incredibly performant. If you’re starting a new project, I highly encourage you to explore this workflow. It might just change the way you think about styling your applications.&lt;/p&gt;

&lt;p&gt;The combination of Vite’s speed, React’s component model, and Tailwind v4’s CSS-first approach makes building complex, dynamic user interfaces more enjoyable than ever.&lt;/p&gt;

&lt;p&gt;You can check out the full source code on my GitHub here 👇:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/praveen-sripati/tailwind-theme-app" rel="noopener noreferrer"&gt;https://github.com/praveen-sripati/tailwind-theme-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading, and feel free to ask any questions in the comments!&lt;/p&gt;

&lt;p&gt;Happy theming!&lt;/p&gt;

&lt;p&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter! &lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;https://x.com/praveen_sripati&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tailwindcss</category>
      <category>themes</category>
      <category>react</category>
    </item>
    <item>
      <title>I Stole This Awesome Ripple Effect from Ant Design (And You Can Too!).</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Mon, 23 Jun 2025 22:15:41 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/i-stole-this-awesome-ripple-effect-from-ant-design-and-you-can-too-2mkg</link>
      <guid>https://dev.to/praveen-sripati/i-stole-this-awesome-ripple-effect-from-ant-design-and-you-can-too-2mkg</guid>
      <description>&lt;p&gt;You know how some websites just feel &lt;em&gt;nice&lt;/em&gt; to use? It’s often the little things. I was on the Ant Design website the other day and noticed their theme toggle. When you click it, it doesn’t just flash — it creates this awesome ripple effect that starts right from your mouse click! I thought that was super cool and decided I had to try building it myself.&lt;/p&gt;

&lt;p&gt;So, here’s a quick rundown of how I did it with React, Tailwind CSS, and the new View Transitions API.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Getting the Basics Down&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First things first, I needed a simple theme toggle that actually worked. I made a new component called &lt;code&gt;ThemeToggle.jsx&lt;/code&gt; to handle everything.&lt;/p&gt;

&lt;p&gt;Inside, I used a &lt;code&gt;useState&lt;/code&gt; hook to keep track of whether the theme was 'light' or 'dark'. Then, I used a &lt;code&gt;useEffect&lt;/code&gt; hook to watch that state. Whenever the theme changed, it would add or remove the &lt;code&gt;.dark&lt;/code&gt; class from the main &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag. That's how Tailwind's dark mode knows what to do!&lt;/p&gt;

&lt;p&gt;Here’s what that first version looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In ThemeToggle.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Sun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Moon&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lucide-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ThemeToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check localStorage to see if the user had a theme set already&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// This effect runs whenever the theme changes&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggleTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Sun&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Moon&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ThemeToggle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;ty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Using View Transitions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Okay, so the toggle worked, but it was still just a sudden flash. This is where the View Transitions API comes in. It’s built to make changes to the page look smooth, and it’s surprisingly easy to use.&lt;/p&gt;

&lt;p&gt;All I had to do was wrap my theme-changing line (&lt;code&gt;setTheme(...)&lt;/code&gt;) inside &lt;code&gt;document.startViewTransition()&lt;/code&gt;. Just like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside the ThemeToggle component...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// If the browser is old and doesn't support the API, just do it the old way&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Wrap our state change in the transition function!&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just by adding that, the ugly flash was gone! The browser automatically replaced it with a nice, gentle fade. It was already a huge improvement, but I still wanted that ripple.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Making the Ripple!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To get the ripple effect, we need to handle the animation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Turn Off the Default Animation:&lt;/strong&gt; The browser’s default fade animation would clash with my ripple, so I had to turn it off. I just added a small &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag in my main &lt;code&gt;App.jsx&lt;/code&gt; file to do this. Putting it there makes sure it's loaded and ready to go from the start.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In App.jsx&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
  }
`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. &lt;strong&gt;Create the Ripple Animation:&lt;/strong&gt; The &lt;code&gt;startViewTransition&lt;/code&gt; function gives you a promise called &lt;code&gt;.ready&lt;/code&gt; that lets you know when it's time to start your own animation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, I grabbed the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates from the mouse click.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then, I figured out how big the ripple needed to be to cover the whole screen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, I used the Web Animations API (&lt;code&gt;element.animate()&lt;/code&gt;) to animate a &lt;code&gt;clip-path&lt;/code&gt; from a tiny circle at the click point to a giant one that fills the screen. And I wrapped it in &lt;code&gt;requestAnimationFrame()&lt;/code&gt; to make sure it runs super smoothly without any glitches.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the final click handler with all the logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside ThemeToggle.jsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hypot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;clipPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;`circle(0px at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;`circle(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;endRadius&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;easing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ease-in-out&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;pseudoElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::view-transition-new(root)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Final output:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7z6tznhcmh2ycdpagzz9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7z6tznhcmh2ycdpagzz9.gif" alt="Final Output Gif" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;And That’s It!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It’s pretty amazing how you can use a few modern web tools to turn a boring feature into a really cool interaction. The View Transitions API is super powerful, and it’s definitely worth checking out for other things, too. Hope this helps you add a little extra flair to your own projects!&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;/em&gt; &lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;https://x.com/praveen_sripati&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Hidden Risk in Installing Dependencies (And How to Avoid It)</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Thu, 19 Jun 2025 14:31:08 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/the-hidden-risk-in-installing-dependencies-and-how-to-avoid-it-1loc</link>
      <guid>https://dev.to/praveen-sripati/the-hidden-risk-in-installing-dependencies-and-how-to-avoid-it-1loc</guid>
      <description>&lt;p&gt;It was a Tuesday afternoon, and the pressure was on. We were in the final sprint of a major feature release, and I had just one piece of the puzzle left: a small, seemingly simple data formatting task. I remembered a utility library I’d used on a personal project a while back. It was perfect — a quick, one-line solution to my problem. Feeling the deadline breathing down my neck, I did what any developer in a hurry would do. I npm intstalled a random formatter package without a second thought, implemented the function in minutes, and pushed my code. A small victory. Or so I thought.&lt;/p&gt;

&lt;p&gt;The next morning, our entire build pipeline was broken. After a frantic investigation, we found the culprit. The simple library I installed had a messy web of its own dependencies. One of these, an old and unmaintained sub-package, conflicted directly with a core library in our application, bringing everything to a halt.&lt;/p&gt;

&lt;p&gt;That hasty install cost us a morning of productivity and delayed our feature release. That chaotic day transformed my approach. Now, I see &lt;code&gt;npm install&lt;/code&gt; as the final step in a crucial evaluation process. Here’s the checklist I developed from that "dependency hell" experience to ensure a new package is a helping hand, not a hidden landmine.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Security&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Assess whether the package introduces any security risks to your application.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scan for Vulnerabilities:&lt;/strong&gt; Use tools like &lt;code&gt;npm audit&lt;/code&gt; or Snyk to check for known security holes in the package and its dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review Source Activity:&lt;/strong&gt; Check the repository for recent, legitimate commits to ensure it’s not hijacked or abandoned.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Evaluate Dependencies:&lt;/strong&gt; Understand that the package’s own dependencies are also being added to your project, each carrying potential risk.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Maintenance &amp;amp; Community&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Verify that the package is actively supported and will be viable long-term.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check Recent Activity:&lt;/strong&gt; Look for recent updates and releases; a package that hasn’t been updated in over a year is a red flag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review Open Issues:&lt;/strong&gt; A high number of unresolved issues and a lack of maintainer responses can signal an abandoned project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gauge Community Size:&lt;/strong&gt; A strong community on platforms like GitHub or Stack Overflow indicates a reliable and trusted package.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Documentation&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Ensure you can understand and implement the package correctly and efficiently.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Look for Clear Examples:&lt;/strong&gt; Good documentation provides practical, copy-paste-ready examples for common use cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check for Completeness:&lt;/strong&gt; A comprehensive API reference is crucial for understanding all the package’s capabilities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirm It’s Up-to-Date:&lt;/strong&gt; The documentation should correspond to the latest version of the package to avoid confusion.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Dependencies&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Be aware of what other packages are being installed transitively.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mind the Dependency Count:&lt;/strong&gt; A package with minimal dependencies is often preferable as it reduces complexity and potential conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Analyze the Dependency Tree:&lt;/strong&gt; Use tools to visualize the entire dependency chain you are about to add to your project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Watch for Bloat:&lt;/strong&gt; Avoid packages that bring in large, unnecessary libraries that can swell your application’s size.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Licensing&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Confirm that you are legally permitted to use the package in your project.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identify the License:&lt;/strong&gt; Quickly find the package’s license (e.g., MIT, Apache 2.0, GPL) in its repository.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ensure License Compatibility:&lt;/strong&gt; Verify that the package’s license works with your project’s own license and business model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check for Commercial Use:&lt;/strong&gt; If your project is commercial, confirm the license doesn’t restrict its use for profit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Performance &amp;amp; Size&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Evaluate the impact the package will have on your application’s speed and resource usage.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check Bundle Size:&lt;/strong&gt; For web development, use tools like BundlePhobia to see how much the package will slow down user load times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consider Performance Overhead:&lt;/strong&gt; Assess if the package’s operations are computationally expensive and could create bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Evaluate Resource Consumption:&lt;/strong&gt; For backend services, consider the potential memory and CPU impact on your servers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;7. Compatibility&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Make sure the package will integrate smoothly with your existing tech stack.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify Environment Support:&lt;/strong&gt; Confirm the package works with your version of Node.js, your target browsers, or your operating system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check Framework Integration:&lt;/strong&gt; If you use a framework like React or Angular, ensure the package is designed to be compatible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anticipate Conflicts:&lt;/strong&gt; Be aware of potential clashes with other libraries you are already using in your project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, building robust software isn’t about avoiding tools, but about choosing them wisely. The pressure to move fast can lead to costly mistakes when we treat package installation as a triviality. By taking just a few moments to run through this checklist, you shift from reactively fixing problems to proactively preventing them. This small investment of time up front pays massive dividends, protecting your project’s stability, security, and long-term health, and solidifying your role as a truly diligent and professional engineer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;/em&gt; &lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;https://x.com/praveen_sripati&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>npm</category>
      <category>javascript</category>
      <category>checklist</category>
    </item>
    <item>
      <title>How I Built an AI That Interviews You using Google Gemini API and Node.js</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Sun, 15 Jun 2025 22:20:44 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/how-i-built-an-ai-that-interviews-you-using-google-gemini-api-and-nodejs-3566</link>
      <guid>https://dev.to/praveen-sripati/how-i-built-an-ai-that-interviews-you-using-google-gemini-api-and-nodejs-3566</guid>
      <description>&lt;p&gt;It all started on a random Saturday afternoon. I was playing around with the Google Gemini API, curious to see what these large language models were really capable of. I wasn’t trying to solve a grand problem or build a startup; I was just having fun and wanted to see if I could make it do something interesting.&lt;/p&gt;

&lt;p&gt;My first thought was simple: “Could I get this thing to ask me questions?” I fed it a prompt for “JavaScript interview questions,” and the response was surprisingly good. That’s when the idea sparked. What if I could wrap a simple UI around this? What if I could build a little web app where an AI could interview &lt;em&gt;me&lt;/em&gt; on any topic I wanted?&lt;/p&gt;

&lt;p&gt;What began as a weekend experiment quickly spiraled into one of the most engaging and educational personal projects I’ve ever tackled. In this post, I’ll walk you through how my “just for fun” idea evolved into a full-blown AI Interviewer app.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Big Idea&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The concept was simple. I wanted a web app where I could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Enter any technical topic (from “React Hooks” to “SQL Joins” to “Docker Networking”).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have an AI ask me a series of relevant, high-quality questions on that topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Crucially, get a &lt;em&gt;different&lt;/em&gt; set of questions every single time to keep me on my toes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the end, receive a detailed, AI-generated report card on my performance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The final result is a sleek, dark-mode app that does exactly this, and I’m incredibly proud of how it turned out.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Tech Deep Dive&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I decided to build the entire application using JavaScript. This allowed me to move quickly and maintain a single language across the entire project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; I went with plain, “vanilla” &lt;strong&gt;HTML, CSS, and JavaScript&lt;/strong&gt;. This was a deliberate choice to keep the frontend light and to really focus on the core logic and UI/UX.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt; I chose &lt;strong&gt;Node.js&lt;/strong&gt; with the &lt;strong&gt;Express.js&lt;/strong&gt; framework. It’s fast, lightweight, and perfect for building the API that would act as the bridge between my frontend and the AI’s brain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The AI:&lt;/strong&gt; The magic comes from the &lt;strong&gt;Google Gemini API&lt;/strong&gt;. I needed a powerful language model that could not only generate questions but also understand my nuanced instructions for creating the final feedback report.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tuning the AI for Creativity&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One of the first problems I hit was repetition. The AI would give me the same great questions for “JavaScript” every single time. The key to solving this was adjusting the AI’s &lt;code&gt;temperature&lt;/code&gt;—its "creativity" dial.&lt;/p&gt;

&lt;p&gt;In my &lt;code&gt;server.js&lt;/code&gt; file, I configured the Gemini model with a high temperature to ensure it would generate unique responses for every request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import the Google AI client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenerativeAI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@google/generative-ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;genAI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenerativeAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define a generation config with a high temperature for unique responses&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize the model WITH the new generation configuration&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;genAI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGenerativeModel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-1.5-flash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Pass our new config here&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;temperature: 0.9&lt;/code&gt; tells the model to take more risks and be more creative, which was the secret to getting fresh, non-repetitive questions every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Instructing the AI&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I quickly learned that working with an LLM isn’t as simple as just asking a question. You have to become a “prompt engineer.” The real art is in crafting the perfect set of instructions to get the exact output you need.&lt;/p&gt;

&lt;p&gt;For the final feedback report, I wanted a beautiful, interactive accordion. This meant I had to instruct the AI to format its entire response in a very specific HTML structure. My final prompt for the analysis looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A simplified look at the prompt sent to the AI&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
You are a helpful and experienced tech interview coach.
Your entire response MUST be a single block of HTML content suitable for placing inside a &amp;lt;div&amp;gt;.

1.  Begin with a "&amp;lt;div&amp;gt;" with the class "overall-summary".
2.  After the summary, provide a series of accordion items using this exact HTML structure:
    &amp;lt;div class="accordion-item"&amp;gt;
      &amp;lt;button class="accordion-header"&amp;gt;...&amp;lt;/button&amp;gt;
      &amp;lt;div class="accordion-content"&amp;gt;...&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

IMPORTANT: Do not include any tags like &amp;lt;html&amp;gt;, &amp;lt;style&amp;gt;, or &amp;lt;script&amp;gt;.

Here is the interview data: ...
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting this prompt right took more trial and error than any other part of the project!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Safely Rendering AI-Generated HTML&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;My biggest struggle was handling the AI’s response on the frontend. Sometimes, the AI would “helpfully” wrap its perfect HTML inside Markdown code blocks, or even worse, include its own &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags! This would break my UI completely.&lt;/p&gt;

&lt;p&gt;My initial attempts to clean this with simple string replacement were flaky. This led me to the real, industry-standard solution: using the browser’s built-in &lt;code&gt;DOMParser&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This was a game-changer. My final JavaScript code in the &lt;strong&gt;script.js&lt;/strong&gt; &lt;code&gt;finishInterview&lt;/code&gt; function now safely parses and validates the response before it ever touches the live page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// script.js&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;finishInterview&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadingText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Analyzing your performance...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;switchScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ANALYZE_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;interviewTopic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;qa_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;interviewData&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server responded with status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// THIS IS THE FINAL, ROBUST PARSING AND VALIDATION LOGIC&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analysis&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analysis&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanedHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;*|&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```$/g, "").trim();
      const parser = new DOMParser();
      const doc = parser.parseFromString(cleanedHtml.trim(), 'text/html');

      // Sanity Check: Does the AI response contain the HTML we actually want?
      if (doc.querySelector('.accordion-item') || doc.querySelector('.overall-summary')) {
        const desiredElements = doc.body.childNodes;
        display.feedbackContent.innerHTML = ''; // Clear previous content
        desiredElements.forEach((node) =&amp;gt; {
          display.feedbackContent.appendChild(node.cloneNode(true));
        });
        setupAccordion();
      } else {
        // This block runs if the AI sends back garbage (like just ```&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;html&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```)
        throw new Error('The AI returned an empty or invalid response.');
      }
    } else {
      throw new Error('Received invalid or empty analysis string from server.');
    }
  } catch (error) {
    console.error('Analysis failed:', error);
    display.feedbackContent.innerHTML = `&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Sorry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;AI&lt;/span&gt; &lt;span class="nx"&gt;was&lt;/span&gt; &lt;span class="nx"&gt;unable&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;provide&lt;/span&gt; &lt;span class="nx"&gt;feedback&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;interview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Please&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="nx"&gt;again&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;more&lt;/span&gt; &lt;span class="nx"&gt;detailed&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;`&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defensive approach ensures that no matter what the AI sends back, my application will never crash or display a broken UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4vfgkgocbo91fqhs751.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4vfgkgocbo91fqhs751.png" alt="Topic Selection Screen" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsh9pye6wzws6446joi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsh9pye6wzws6446joi2.png" alt="Interview Question Screen" width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04fawrartqdux68gytt9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04fawrartqdux68gytt9.png" alt="Final AI Feedback Screen" width="800" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Final Thoughts&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Building the AI Interviewer was an incredible learning experience. It taught me the practical realities of integrating with large language models, the importance of robust error handling, and the art of prompt engineering. If you’re a developer looking for a project that touches on every part of the modern tech stack, I can’t recommend building your own AI-powered tool enough.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can check out the full source code on my GitHub here 👇:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/praveen-sripati/ai-interviewer" rel="noopener noreferrer"&gt;https://github.com/praveen-sripati/ai-interviewer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;br&gt;
&lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;Follow @x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>interview</category>
    </item>
    <item>
      <title>Practical Prompt Engineering with Google Gemini and Node.js</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Sat, 14 Jun 2025 09:27:19 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/practical-prompt-engineering-with-google-gemini-and-nodejs-346i</link>
      <guid>https://dev.to/praveen-sripati/practical-prompt-engineering-with-google-gemini-and-nodejs-346i</guid>
      <description>&lt;p&gt;Welcome back, developers! If you’ve just come from my previous post on &lt;a href="https://dev.to/praveen-sripati/stop-wasting-time-master-ai-prompt-engineering-now-488a"&gt;&lt;strong&gt;As a Developer, Struggling with AI Prompts? Master AI Prompt Engineering Now&lt;/strong&gt;&lt;/a&gt;, you’ve got the foundational knowledge down. You understand &lt;em&gt;why&lt;/em&gt; clear instructions, context, and iterative refinement are crucial for getting desired responses from AI.&lt;/p&gt;

&lt;p&gt;Now, it’s time to get our hands dirty. This post is your practical guide, a “codebook” filled with specific Node.js examples demonstrating how to apply those prompt engineering principles using the Google Gemini API. We’ll set up our environment and then dive into various techniques, showing you exactly how to craft effective prompts and integrate AI responses into your applications.&lt;/p&gt;

&lt;p&gt;Let’s build!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Getting Started: Gemini &amp;amp; Node.js Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First things first, you’ll need a Google Cloud Project with the Gemini API enabled and an API key.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable the Gemini API:&lt;/strong&gt; Head over to the &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt; or the Google Cloud Console. Follow the steps to create an API key for the Gemini API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install the SDK:&lt;/strong&gt; Open your terminal and run:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @google/generative-ai dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@google/generative-ai&lt;/code&gt;: This is the official Google Gemini Node.js SDK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;dotenv&lt;/code&gt;: A handy package to securely load your API key from a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Create a&lt;/strong&gt; &lt;code&gt;.env&lt;/code&gt; &lt;strong&gt;file:&lt;/strong&gt; In the root of your project, create a file named &lt;code&gt;.env&lt;/code&gt; and add your API key like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY=YOUR_GEMINI_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; Never commit your API keys directly into your source code!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Your Base Node.js Code:&lt;/strong&gt; Create a file (e.g., &lt;code&gt;gemini-codebook.js&lt;/code&gt;) and paste the following setup. This &lt;code&gt;callGeminiApi&lt;/code&gt; function will be our wrapper for all the examples that follow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// gemini-codebook.js
require('dotenv').config(); // Load environment variables

const { GoogleGenerativeAI } = require('@google/generative-ai');

const API_KEY = process.env.API_KEY;

if (!API_KEY) {
    console.error("API_KEY not found in .env file. Please create a .env file with API_KEY=YOUR_GEMINI_API_KEY");
    process.exit(1);
}

const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-pro" }); // Using gemini-pro for text tasks

/**
 * Calls the Google Gemini API with a given prompt and optional model parameters.
 * @param {string} prompt The text prompt to send to the AI.
 * @param {object} modelParams Optional parameters for the AI model (e.g., temperature, maxOutputTokens).
 * @returns {Promise&amp;lt;string&amp;gt;} The text response from the Gemini API.
 */
async function callGeminiApi(prompt, modelParams = {}) {
    try {
        const generationConfig = {
            temperature: modelParams.temperature || 0.7, // Default temperature
            maxOutputTokens: modelParams.maxOutputTokens || 2048, // Default max tokens
            // Add other parameters like topP, topK if needed here
        };

        console.log("\n--- Gemini API Call ---");
        console.log(`Prompt sent:\n'${prompt}'`);
        console.log(`Model Parameters: ${JSON.stringify(generationConfig)}`);

        const result = await model.generateContent(prompt, generationConfig);
        const response = await result.response;
        const text = response.text();

        console.log(`\nGemini Response:\n${text}`);
        return text;

    } catch (error) {
        console.error("Error calling Gemini API:", error);
        // Log more details if available, like error.response.candidates
        return `Error: ${error.message}`;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s explore individual prompt engineering techniques with runnable Node.js code snippets. You can add each of these &lt;code&gt;async function&lt;/code&gt; blocks to your &lt;code&gt;gemini-codebook.js&lt;/code&gt; file and then call them one by one in your main execution flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Clear and Specific Instructions:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; When I need to distill a large chunk of information into a digestible format, I ensure my prompt is incredibly direct about the desired output. This prevents the AI from giving me a lengthy essay when I just need the highlights.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function exampleClearSpecificInstructions() {
    console.log("\n## 1. Clear and Specific Instructions (Summarization)");
    const longArticle = `Artificial intelligence (AI) has rapidly advanced in recent years, transforming various industries and aspects of daily life. From sophisticated natural language processing models that can generate human-like text to advanced computer vision systems capable of recognizing objects and faces, AI's capabilities continue to expand. However, along with its immense potential, AI also presents significant challenges, including ethical considerations, job displacement, and the need for robust regulatory frameworks. The future of AI hinges on addressing these concerns while harnessing its power for societal benefit.`;
    const promptSummary = `Summarize the following text about AI advancements in exactly 3 concise bullet points.
Text: "${longArticle}"
`;
    await callGeminiApi(promptSummary);
}

// To run this specific example, uncomment the line below:
// exampleClearSpecificInstructions();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;2. Defining Output Format:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; In development, receiving structured data is paramount. If the AI’s response isn’t consistently formatted, it breaks my application. I explicitly ask for JSON so I can easily parse it into a JavaScript object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function exampleDefiningOutputFormat() {
    console.log("\n## 2. Defining Output Format (JSON Example)");
    const promptJson = `
Generate a JSON object for a hypothetical user profile.
The object should have the following keys: 'username' (string), 'id' (number), 'email' (string), and 'is_active' (boolean).
Example Data: Username 'dev_master', ID 12345, Email 'dev@example.com', Active status true.
`;
    const jsonResponse = await callGeminiApi(promptJson);
    try {
        const parsedJson = JSON.parse(jsonResponse);
        console.log("\nParsed JSON:", parsedJson);
        console.log(`Username: ${parsedJson.username}, ID: ${parsedJson.id}`);
    } catch (e) {
        console.error("Failed to parse JSON response:", e);
    }
}

// To run this specific example, uncomment the line below:
// exampleDefiningOutputFormat();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;3. Few-Shot Prompting:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; When I need code or highly specific text output, simply asking isn’t always enough. I provide an example of the desired format — in this case, a JSDoc-commented function — to guide the AI to match that exact style. I also use a &lt;strong&gt;lower&lt;/strong&gt; &lt;code&gt;temperature&lt;/code&gt; here to reduce creativity and increase adherence to the pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function exampleFewShotPrompting() {
    console.log("\n## 3. Few-Shot Prompting (Code Generation Style)");
    const promptFewShotCode = `
Generate a simple JavaScript function for calculating the area of a rectangle.
Here's an example of how you should format the function:

// Example function for calculating circle area
/**
 * Calculates the area of a circle.
 * @param {number} radius The radius of the circle.
 * @returns {number} The area of the circle.
 */
function calculateCircleArea(radius) {
  return Math.PI * radius * radius;
}

// Now, generate the function for rectangle area:
`;
    await callGeminiApi(promptFewShotCode, { temperature: 0.3 }); // Lower temp for more predictable code
}

// To run this specific example, uncomment the line below:
// exampleFewShotPrompting();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;4. Role-Playing / Persona:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; I often need explanations tailored for different audiences. By assigning a persona (e.g., “senior DevOps engineer”), I instruct the AI to adopt a specific tone and knowledge level, ensuring the explanation is appropriate and easy for the target audience to understand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function exampleRolePlayingPersona() {
    console.log("\n## 4. Role-Playing / Persona (Technical Explainer)");
    const promptPersona = `
You are a senior DevOps engineer explaining Docker containers to a junior developer.
Explain what Docker is and why it's useful for deploying applications, using simple, clear language.
`;
    await callGeminiApi(promptPersona);
}

// To run this specific example, uncomment the line below:
// exampleRolePlayingPersona();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;5. Chain-of-Thought (CoT) Prompting&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; For complex problems, especially those involving logic or calculations, I don’t just want the answer; I want to see the reasoning. Asking the AI to “show your step-by-step calculations and reasoning” improves the accuracy of the final result and helps me debug if the answer is off.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function exampleChainOfThought() {
    console.log("\n## 5. Chain-of-Thought (CoT) Prompting (Problem Solving)");
    const promptCoT = `
A train leaves station A at 10:00 AM traveling at 60 km/h.
Another train leaves station B at 11:00 AM traveling at 80 km/h towards station A.
The distance between station A and station B is 400 km.
At what time will the two trains meet? Show your step-by-step calculations and reasoning clearly.
`;
    await callGeminiApi(promptCoT);
}

// To run this specific example, uncomment the line below:
// exampleChainOfThought();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;6. Using Delimiters for Clarity&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; When my prompts become lengthy, containing instructions, input data, and formatting requirements, they can get messy. Using clear delimiters like XML-like tags helps the AI distinguish between different parts of the prompt, ensuring it understands exactly what to do with each section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function exampleDelimitersForClarity() {
    console.log("\n## 6. Using Delimiters for Clarity (Multi-part Request)");
    const rawData = `Product: Laptop, Price: 1200, Category: Electronics, Stock: 50
Product: Keyboard, Price: 75, Category: Peripherals, Stock: 200
Product: Mouse, Price: 30, Category: Peripherals, Stock: 300`;

    const promptDelimited = `
&amp;lt;instructions&amp;gt;
Process the provided product data.
For each product, extract its name, price, and stock.
Then, generate a short summary for products with stock less than 100.
&amp;lt;/instructions&amp;gt;

&amp;lt;data&amp;gt;
${rawData}
&amp;lt;/data&amp;gt;

&amp;lt;output_format&amp;gt;
List the low-stock products in markdown bullet points, formatted as "Product Name (Stock: X)".
&amp;lt;/output_format&amp;gt;
`;
    await callGeminiApi(promptDelimited);
}

// To run this specific example, uncomment the line below:
// exampleDelimitersForClarity();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Next Steps: Integrate and Iterate!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You’ve now got a practical set of examples for applying prompt engineering principles with the Google Gemini API in Node.js. The key from here is &lt;strong&gt;integration&lt;/strong&gt; and &lt;strong&gt;iteration&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integrate:&lt;/strong&gt; Think about where AI can enhance your existing applications. Can it summarize user feedback, generate product descriptions, or provide real-time code suggestions?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterate:&lt;/strong&gt; As you use these prompts in real-world scenarios, you’ll inevitably find room for improvement. Continue to test, evaluate, and refine your prompts based on the AI’s responses and your application’s needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prompt engineering is an evolving skill, and the more you practice, the more intuitive it becomes. Go forth and build amazing things with AI!&lt;/p&gt;

&lt;p&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;br&gt;
&lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;Follow @x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>Stop Wasting Time: Master AI Prompt Engineering Now</title>
      <dc:creator>Praveen Sripati</dc:creator>
      <pubDate>Fri, 13 Jun 2025 12:57:55 +0000</pubDate>
      <link>https://dev.to/praveen-sripati/stop-wasting-time-master-ai-prompt-engineering-now-488a</link>
      <guid>https://dev.to/praveen-sripati/stop-wasting-time-master-ai-prompt-engineering-now-488a</guid>
      <description>&lt;p&gt;When I’m sitting down to integrate an AI into an application, I follow a pretty consistent mental checklist. It’s all about guiding the AI, step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Know Your AI (and Its Quirks)
&lt;/h2&gt;

&lt;p&gt;Before I even type my first prompt, I try to understand the AI model itself. Is it a language model, an image generator, or something else? What kind of data was it trained on? Knowing its strengths (and more importantly, its weaknesses) helps me set realistic expectations. I also dig into the API documentation to see what parameters I can tweak — things like &lt;strong&gt;&lt;code&gt;temperature&lt;/code&gt;&lt;/strong&gt; (how creative or "safe" the response is) or &lt;strong&gt;&lt;code&gt;max_tokens&lt;/code&gt;&lt;/strong&gt; (how long the response can be). These are my knobs and dials for fine-tuning.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat your AI like a junior developer: give it clear instructions, context, and examples, and it’ll surprise you with what it can achieve.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Be Crystal Clear and Super Specific
&lt;/h2&gt;

&lt;p&gt;This is the golden rule I live by. Vague prompts are a recipe for unpredictable results. I always ask myself: “Am I telling the AI exactly what I want?”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Instructions:&lt;/strong&gt; Instead of “Write about climate change,” I’d write, “Summarize the key environmental impacts of climate change in 3 concise bullet points for a general audience.” See the difference? I’m telling it what to do, what topic, how many points, and for whom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define Output Format:&lt;/strong&gt; If I need JSON, I ask for JSON. If I need Markdown, I specify Markdown. This is crucial for me because I’m usually processing the AI’s output programmatically. I’ll often say something like, “Provide a JSON object with name, age, and city fields."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Constraints:&lt;/strong&gt; Need a short response? I’ll add “under 100 words.” No jargon? I’ll explicitly state, “Do not use technical jargon.”&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The best prompts aren’t just questions; they’re precise contracts with the AI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Provide Context and Examples
&lt;/h2&gt;

&lt;p&gt;AI models aren’t mind-readers. The more context I give them, the better they perform.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contextual Information:&lt;/strong&gt; If I’m building a customer support bot, I’ll start the prompt with something like, “You are a customer support agent for a software company. The user is experiencing a login error with code ‘AUTH-001’.” This sets the stage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Few-Shot Prompting:&lt;/strong&gt; This is a powerful technique. I include a few input-output examples directly in my prompt. For instance, if I want a specific translation style, I’ll show it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Translate English to French: 
English: Hello 
French: Bonjour 

English: Thank you 
French: Merci 

English: Good morning 
French: ---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This teaches the AI the pattern I expect.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of few-shot prompting as giving the AI a quick, personalized training session — right inside your prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Leverage Advanced Techniques
&lt;/h2&gt;

&lt;p&gt;For more complex tasks, I pull out some of my more advanced tricks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chain-of-Thought (CoT):&lt;/strong&gt; If I need the AI to reason or solve a multi-step problem, I’ll ask it to “think step by step” or “show your reasoning.” This makes its process transparent and often leads to more accurate answers. For example, “Solve this math problem. Show your step-by-step reasoning: (10 + 5) * 2.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role-Playing:&lt;/strong&gt; Sometimes, I need the AI to adopt a specific persona. If I need a technical explanation, I’ll tell it, “Act as a senior software engineer.” This helps the AI use appropriate language and knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delimiters:&lt;/strong&gt; For longer prompts with different sections (instructions, context, input), I use clear separators like ###, ---, or even XML-like tags (, ). This helps the AI understand the structure of my prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Chain-of-Thought isn’t just about getting the right answer; it’s about making the AI’s logic traceable and debuggable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. My Iterative Workflow (Test, Refine, Repeat!)
&lt;/h2&gt;

&lt;p&gt;This is where the rubber meets the road. Prompt engineering is almost never a one-shot deal.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Draft:&lt;/strong&gt; I start with a simple prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test:&lt;/strong&gt; I send it to the AI API and look at the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate:&lt;/strong&gt; Did it meet my expectations? Is the format right? Is the content accurate?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refine:&lt;/strong&gt; Based on the evaluation, I tweak the prompt. Maybe I need to add more context, clarify an instruction, or change a parameter like temperature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat:&lt;/strong&gt; I keep going back to step 2 until I’m satisfied. It’s an ongoing process of trying, learning, and improving.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Prompt engineering is less about finding the magic words and more about embracing the iterative loop.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. Mind the Ethics and Bias
&lt;/h2&gt;

&lt;p&gt;As a developer, I also keep ethical considerations in mind. AI models can inherit biases from their training data, so I try to craft prompts that encourage fair, inclusive, and unbiased responses. I also think about safety guardrails to prevent the AI from generating inappropriate or harmful content.&lt;/p&gt;

&lt;p&gt;While understanding these principles is crucial, I know that for developers, seeing code in action makes all the difference. That’s why I’ve put together a dedicated follow-up post. In &lt;a href="https://dev.to/praveen-sripati/practical-prompt-engineering-with-google-gemini-and-nodejs-346i"&gt;Practical Prompt Engineering with Google Gemini and Node.js&lt;/a&gt;, I dive deep into practical Node.js examples using the Google Gemini API, showing you exactly how to implement these techniques in your applications. Head over there to start building!&lt;/p&gt;

&lt;p&gt;Want to see a random mix of weekend projects, half-baked ideas, and the occasional useful bit of code? Feel free to follow me on Twitter!&lt;br&gt;
&lt;a href="https://x.com/praveen_sripati" rel="noopener noreferrer"&gt;Follow @x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>promptengineering</category>
    </item>
  </channel>
</rss>
