<?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: Jerry Satpathy</title>
    <description>The latest articles on DEV Community by Jerry Satpathy (@j3rry320).</description>
    <link>https://dev.to/j3rry320</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%2F238168%2F8c784c55-d9da-443c-9786-895342f95e95.jpeg</url>
      <title>DEV Community: Jerry Satpathy</title>
      <link>https://dev.to/j3rry320</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/j3rry320"/>
    <language>en</language>
    <item>
      <title>Your React App Breaks When the Internet Drops. Here’s a Better Way.</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sat, 28 Feb 2026 10:58:49 +0000</pubDate>
      <link>https://dev.to/j3rry320/your-react-app-breaks-when-the-internet-drops-heres-a-better-way-1l7h</link>
      <guid>https://dev.to/j3rry320/your-react-app-breaks-when-the-internet-drops-heres-a-better-way-1l7h</guid>
      <description>&lt;p&gt;&lt;em&gt;Internet connectivity is outside a developer’s control.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;User experience is not.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a connection drops, most applications either freeze silently, fail API calls without feedback, or leave users staring at broken UI states. &lt;strong&gt;The result is confusion, repeated actions, and unnecessary frustration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React Network Notifier&lt;/strong&gt; exists to solve this problem cleanly. It detects network changes, surfaces clear offline messaging, and manages reconnect transitions without adding dependencies or boilerplate to your application.&lt;/p&gt;

&lt;p&gt;Version 2.0.0 is a complete rewrite focused on performance, flexibility, and modern React compatibility.&lt;/p&gt;
&lt;h2&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%2Fb9grpvz3ox46mrhowczu.png" alt="react-network-notifier" width="800" height="800"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;react-network-notifier&lt;/code&gt; is a zero-dependency React component that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Detects network disconnection&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Displays configurable offline states&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handles reconnect events with smooth transitions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works with React and Next.js&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Supports Server-Side Rendering (SSR)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What’s New in v2.0.0
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Rewritten Build System (Microbundle → Tsup)
&lt;/h3&gt;

&lt;p&gt;The package was rebuilt using Tsup.&lt;/p&gt;

&lt;p&gt;Results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Smaller bundle&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Faster build pipeline&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Final bundle size under 10KB (minified)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  2. Multiple Layout Variants
&lt;/h3&gt;

&lt;p&gt;Three visual modes are now supported:&lt;/p&gt;

&lt;p&gt;variant="toast"&lt;br&gt;&lt;br&gt;
variant="banner"&lt;br&gt;&lt;br&gt;
variant="fullscreen"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;toast&lt;/code&gt;: small floating notification&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;banner&lt;/code&gt;: horizontal alert&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;fullscreen&lt;/code&gt;: immersive offline state (default)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the component usable across dashboards, SaaS products, and internal tools.&lt;/p&gt;


&lt;h3&gt;
  
  
  3. Native Dark Mode Support
&lt;/h3&gt;

&lt;p&gt;Theme configuration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;theme="light"  |  "dark"  |  "system"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;When set to &lt;code&gt;"system"&lt;/code&gt;, the component respects the user’s OS preference automatically.&lt;/p&gt;


&lt;h3&gt;
  
  
  4. Reconnect State Handling
&lt;/h3&gt;

&lt;p&gt;When connectivity returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A success message is shown&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The component transitions out smoothly&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reconnect messages are fully customizable.&lt;/p&gt;


&lt;h3&gt;
  
  
  5. ASCII or SVG Icons
&lt;/h3&gt;

&lt;p&gt;Two icon modes are supported:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iconType="ascii"  
iconType="svg"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This allows the component to fit both retro developer tools and modern UI systems.&lt;/p&gt;


&lt;h3&gt;
  
  
  6. CSS Modules Instead of Inline Styles
&lt;/h3&gt;

&lt;p&gt;Version 2 removes heavy inline style objects and uses injected CSS modules instead.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reduced runtime overhead&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cleaner rendering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved performance consistency&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-network-notifier  
&lt;span class="c"&gt;# or  &lt;/span&gt;
yarn add react-network-notifier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;p&gt;Add it to your root layout:&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="k"&gt;import&lt;/span&gt;  &lt;span class="nx"&gt;NetworkNotifier&lt;/span&gt;  &lt;span class="k"&gt;from&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-network-notifier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;  
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NetworkNotifier&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;/* Your application */&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;/&amp;gt; &lt;/span&gt;&lt;span class="err"&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;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No configuration required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Custom Configuration Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;  &lt;span class="nx"&gt;NetworkNotifier&lt;/span&gt;  &lt;span class="k"&gt;from&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-network-notifier&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;customMessages&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It looks like your router took a coffee break.&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="s2"&gt;Offline mode activated.&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;reconnectMessages&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Connection restored.&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;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="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;NetworkNotifier&lt;/span&gt;  
  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;customMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="nx"&gt;reconnectMessages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reconnectMessages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;toast&lt;/span&gt;&lt;span class="dl"&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="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  
  &lt;span class="nx"&gt;iconType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  
  &lt;span class="nx"&gt;closable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="sr"&gt;/&amp;gt; &lt;/span&gt;&lt;span class="err"&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;
  
  
  Configuration Props
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prop&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;messages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Array of strings displayed when the connection is lost.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reconnectMessages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Array of strings displayed when the connection is restored.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Array of ASCII art strings shown during offline states.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;styles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;React.CSSProperties&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inline styles to override or extend default component CSS.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;variant&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;`"toast" \&lt;/td&gt;
&lt;td&gt;"banner" \&lt;/td&gt;
&lt;td&gt;"fullscreen"`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iconType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;`"ascii" \&lt;/td&gt;
&lt;td&gt;"svg"`&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"ascii"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;theme&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;`"light" \&lt;/td&gt;
&lt;td&gt;"dark" \&lt;/td&gt;
&lt;td&gt;"system"`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;closable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;true&lt;/code&gt;, allows users to manually dismiss the notification.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Technical Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Zero dependencies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under 10KB minified&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TypeScript-first&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSR-safe&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tree-shakeable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No runtime setup required&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;NPM:&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/react-network-notifier" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/react-network-notifier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/J3rry320/react-network-notifier" rel="noopener noreferrer"&gt;https://github.com/J3rry320/react-network-notifier&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Offline handling is often treated as an edge case. In reality, it is a core UX scenario.&lt;/p&gt;

&lt;p&gt;React Network Notifier v2.0 is designed to integrate into modern React systems with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Minimal footprint&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No dependencies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSR safety&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configurable presentation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you care about resilient UI architecture, this is a small but meaningful addition to your stack.&lt;/p&gt;

&lt;p&gt;Contributions and feedback are welcome.&lt;br&gt;
And as always Happy Coding! 🧑🏻‍💻&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Making Your Website Talk (Without Scaring Your Users)</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Fri, 06 Feb 2026 07:30:17 +0000</pubDate>
      <link>https://dev.to/j3rry320/making-your-website-talk-without-scaring-your-users-3299</link>
      <guid>https://dev.to/j3rry320/making-your-website-talk-without-scaring-your-users-3299</guid>
      <description>&lt;p&gt;Let’s be honest: most websites are too quiet. We spend our lives staring at glowing rectangles, reading text like it’s 1995. But your browser has a hidden superpower called the &lt;strong&gt;Web Speech API&lt;/strong&gt;. It can talk. And no, I don't mean those weird auto-playing video ads from 2008.&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%2Fwi2t282b2i5cz1auy3g3.jpg" 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%2Fwi2t282b2i5cz1auy3g3.jpg" alt="Building a TTS Weather App" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today, we’re building a Weather Dashboard that doesn’t just show you the temperature—it summarises the vibes and reads them to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React.js&lt;/strong&gt;: Because we like components and state management that makes sense.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Web Speech API&lt;/strong&gt;: Specifically &lt;code&gt;window.speechSynthesis&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenWeatherMap API&lt;/strong&gt;: For the data (or any weather API of your choice).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. The "Brains": Fetching &amp;amp; Summarising
&lt;/h2&gt;

&lt;p&gt;First, we need something worth saying. A raw JSON response saying &lt;code&gt;{"temp": 273.15}&lt;/code&gt; is great for machines, but humans prefer "It's freezing, stay inside."&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchWeatherSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;city&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;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="s2"&gt;`https://api.openweathermap.org/data/2.5/weather?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;appid=YOUR_API_KEY&amp;amp;units=metric`&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;data&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;// A simple summarizer (You could pipe this to an LLM for more "flavour")&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Currently in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, it's &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; degrees with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. My professional advice? &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wear a hat.&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="s2"&gt;Grab a jacket.&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. The "Voice": Meet SpeechSynthesis
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;window.speechSynthesis&lt;/code&gt; controller is the boss. It manages the queue of "utterances" (the things you want to say).&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;useSpeech&lt;/code&gt; Hook
&lt;/h3&gt;

&lt;p&gt;Don't clutter your UI logic. Let’s wrap the speech engine in a clean React hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;useSpeech&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;voices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setVoices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SpeechSynthesisVoice&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;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;updateVoices&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;setVoices&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;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVoices&lt;/span&gt;&lt;span class="p"&gt;());&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;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onvoiceschanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;updateVoices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;updateVoices&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;speak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;voiceName&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;// Cancel any ongoing chatter&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;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel&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;utterance&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;SpeechSynthesisUtterance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Find the voice or default to the first one&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;voiceName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;voice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;voices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;voiceName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 0.1 to 10&lt;/span&gt;
    &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 0 to 2&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;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;utterance&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;speak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;voices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pause&lt;/span&gt;&lt;span class="p"&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;resume&lt;/span&gt;&lt;span class="p"&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&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;
  
  
  3. The Dashboard Component
&lt;/h2&gt;

&lt;p&gt;Now, let’s glue it together. We’ll create a button that fetches the data and then reads it out loud.&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="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="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;useSpeech&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/useSpeech&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;WeatherDashboard&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;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSummary&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="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;speak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;voices&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSpeech&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;handleWeatherUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;text&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;fetchWeatherSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;London&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;voices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.1&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="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;p-8 max-w-md mx-auto bg-white rounded-xl shadow-md&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-2xl font-bold&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;Weather&lt;/span&gt; &lt;span class="nx"&gt;Talker&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;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;handleWeatherUpdate&lt;/span&gt;&lt;span class="p"&gt;}&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-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600&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;Get&lt;/span&gt; &lt;span class="nx"&gt;Audio&lt;/span&gt; &lt;span class="nx"&gt;Summary&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="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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-4 italic text-gray-600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{summary}&lt;/span&gt;&lt;span class="dl"&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="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;h2&gt;
  
  
  Pro-Tips for the Speech API
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Voices are Lazy&lt;/strong&gt;: Voices don't always load immediately. That &lt;code&gt;onvoiceschanged&lt;/code&gt; event in our hook is crucial; otherwise, your &lt;code&gt;voices&lt;/code&gt; array will be empty on the first render.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Activation&lt;/strong&gt;: Browsers hate noise. You cannot trigger &lt;code&gt;speak()&lt;/code&gt; on page load. It &lt;em&gt;must&lt;/em&gt; be inside a user-triggered event (like a click).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The "Vocalize" Shortcut&lt;/strong&gt;: If you want to skip the boilerplate, there is a handy NPM package called &lt;a href="https://www.npmjs.com/package/vocalize.ts" rel="noopener noreferrer"&gt;vocalize.ts&lt;/a&gt;. It’s a TypeScript-first wrapper for this exact API. Full disclosure: it’s currently in a bit of a dormant state, but the core logic is solid, and an update is coming soon to modernise the internals.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why bother?
&lt;/h2&gt;

&lt;p&gt;Accessibility is the obvious answer. Screen readers are great, but sometimes a specific "Read this for me" feature is exactly what a user needs when they're multitasking or vision-impaired.&lt;/p&gt;

&lt;p&gt;Plus, it's just fun to make your computer talk back to you. Just don't give it a sarcastic personality unless you're prepared for the consequences.&lt;/p&gt;

&lt;p&gt;And as always &lt;strong&gt;Happy coding!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The Day My Linux Server Started Distributing Windows Malware</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Wed, 04 Feb 2026 06:43:47 +0000</pubDate>
      <link>https://dev.to/j3rry320/the-day-my-linux-server-started-distributing-windows-malware-8jg</link>
      <guid>https://dev.to/j3rry320/the-day-my-linux-server-started-distributing-windows-malware-8jg</guid>
      <description>&lt;p&gt;&lt;em&gt;Here's a Minimal &amp;amp; Practical Guide to Server Hygiene&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you run a server long enough, you will eventually open a directory, spot a file you don't recognise, and think:&lt;/strong&gt; &lt;em&gt;“I did not put this here.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had my &lt;em&gt;"moment"&lt;/em&gt; during the &lt;strong&gt;React2shell (CVE-2025-55182) outbreak&lt;/strong&gt;. I found a file named &lt;strong&gt;Agtisx.exe&lt;/strong&gt; sitting in a temp folder. A Windows executable on a Linux box is a red flag big enough to cover a stadium.&lt;/p&gt;

&lt;p&gt;It wasn't just a stray file; it was the payload for a campaign turning my infrastructure into a distribution hub to infect Windows servers. &lt;strong&gt;My clean server had become a staging ground for someone else’s war.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This guide exists so that the moment becomes mildly interesting instead of emotionally devastating. &lt;strong&gt;We aren't aiming for "military-grade" complexity. We want clean, boring, predictable systems that let you sleep at night.&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%2Fzlhcmhp2o9bic0sc1291.jpg" 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%2Fzlhcmhp2o9bic0sc1291.jpg" alt="Boring Predictable Systems" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. SSH: Make Logging In Aggressively Boring
&lt;/h2&gt;

&lt;p&gt;If logging into your server feels exciting, you’ve already failed. Your job isn't to defeat "hackers"; it’s to make your server so unrewarding that they move on to someone else.&lt;/p&gt;

&lt;p&gt;Your config file at &lt;code&gt;(/etc/ssh/sshd_config)&lt;/code&gt; should at the very least enforce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key-based login only.&lt;/strong&gt; No passwords. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No root login.&lt;/strong&gt; Force a standard user. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal attempts&lt;/strong&gt;. Give them three tries, then kick them.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Edit /etc/ssh/sshd_config with these settings:&lt;/span&gt;
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication &lt;span class="nb"&gt;yes
&lt;/span&gt;MaxAuthTries 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works&lt;/strong&gt;: Bots love low-hanging fruit (passwords and root). They hate keys and effort. Be the effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Keys &amp;amp; Secret Rotation is Hygiene, Not Panic
&lt;/h2&gt;

&lt;p&gt;SSH keys live forever unless you actively rotate them. They sit on old laptops, archived backups, CI machines you no longer use, and devices you forgot ever had access.&lt;/p&gt;

&lt;p&gt;Environment variables behave the same way.&lt;/p&gt;

&lt;p&gt;Once a secret exists, it tends to spread. It gets copied into &lt;code&gt;.env&lt;/code&gt; files, pasted into dashboards, shared with teammates, backed up, and sometimes logged by mistake. Over time, you stop remembering where it lives, but it still works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rotating keys and secrets is not a response to an incident. It is routine maintenance. It limits the damage of forgotten access and reduces the blast radius of mistakes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are thinking, “But nothing bad happened,” that is exactly the point. Rotation is what keeps it that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Local Firewall: Trust but Verify
&lt;/h2&gt;

&lt;p&gt;Even if your cloud provider has a &lt;em&gt;"Security Group,"&lt;/em&gt; run a local firewall. It’s your second line of defence against human error—specifically, your own misclicks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Rule:&lt;/strong&gt; Expose SSH, HTTP, HTTPS, and your specific app ports. Block everything else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example using UFW (Uncomplicated Firewall)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default deny incoming
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow ssh
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow http
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow https
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Fail2ban: Outsource the Annoyance
&lt;/h2&gt;

&lt;p&gt;Brute-force attempts are a fact of life. You aren't being targeted; you just exist. Fail2ban watches your logs and quietly jails IPs that behave poorly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check status of blocked IPs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fail2ban-client status sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Audit Your Services
&lt;/h2&gt;

&lt;p&gt;Malware loves persistence, and persistence loves services. Run this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl list-unit-files --state=enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ask one question for every line: &lt;strong&gt;“Do I know why this is here?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If the answer is No: Investigate.&lt;br&gt;
If the answer is still No: Disable it.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  6. The Post-Moment Forensic Routine
&lt;/h2&gt;

&lt;p&gt;When you find a file like Agtisx.exe, don't just delete it. You need to know how it got there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Check the Metadata&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before deleting, look at the file's &lt;em&gt;"birth certificate":&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stat Agtisx.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This tells you the exact second it was created (Birth) or modified. Note the timestamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Check Permissions and Ownership&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Who &lt;em&gt;"owns"&lt;/em&gt; the malware?&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -la Agtisx.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Owned by www-data? Your web app was the entry point (e.g., React2shell).&lt;br&gt;
Owned by root?This implies privilege escalation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Confirm via Logs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Match the stat timestamp against your logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Search web logs for activity around the 'stat' timestamp&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"2025-02-04 10:30"&lt;/span&gt; /var/log/nginx/access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Recovery: The "Burn It Down" Philosophy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Once a server is compromised enough to host attack payloads, you can rarely trust the OS again.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot the evidence&lt;/strong&gt;: Save logs and the malware sample to an isolated folder.&lt;/li&gt;
&lt;li&gt;Rotate everything:

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH Keys: Generate new ones; revoke all old ones.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Secrets: Change DB passwords, API keys, and .env files.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete the instance&lt;/strong&gt;: Terminate the server. Do not try to "clean" it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redeploy Fresh&lt;/strong&gt;: Provision a new instance, apply your "Boring Config," and patch the vulnerability (e.g., update React) before going live.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  8. Predictability Beats Fancy Tools
&lt;/h2&gt;

&lt;p&gt;Your server should only talk to places you expect. Check your active connections:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ss -antp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Normal&lt;/strong&gt;: Web traffic, database connections, updates.&lt;br&gt;
&lt;strong&gt;Not Normal:&lt;/strong&gt; Random IPs, unknown processes, strange ports.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. File Hygiene: Stop the Clutter
&lt;/h2&gt;

&lt;p&gt;Your server is not a downloads folder. Make sure to check if there are &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No random binaries in home directories.&lt;/li&gt;
&lt;li&gt; No world-writable directories. &lt;/li&gt;
&lt;li&gt;No executable permissions unless required.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find world-writable directories&lt;/span&gt;
find / &lt;span class="nt"&gt;-xdev&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-perm&lt;/span&gt; &lt;span class="nt"&gt;-0002&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-perm&lt;/span&gt; &lt;span class="nt"&gt;-1000&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="nt"&gt;-print&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  10. Backups: The Disaster Circuit-Breaker
&lt;/h2&gt;

&lt;p&gt;Backups don’t prevent incidents; they prevent disasters. They turn a &lt;strong&gt;"compromise" into a "recovery."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rule&lt;/strong&gt;: If the thought of restoring your server feels scary, your backups aren't finished yet.&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%2Ff1jqfh4ihm0km64rz7ke.jpg" 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%2Ff1jqfh4ihm0km64rz7ke.jpg" alt="Clean Predictable Systems" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;The Goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A clean server has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A few ways in.&lt;/li&gt;
&lt;li&gt; A few things are running.&lt;/li&gt;
&lt;li&gt;Few places to hide.&lt;/li&gt;
&lt;li&gt;Predictable behaviour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The best compliment a professional server can receive is: &lt;strong&gt;“Nothing interesting happened today.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The goal is to build for boredom &amp;amp; to Sleep better.&lt;br&gt;
And as always &lt;strong&gt;Happy Coding!&lt;/strong&gt; &lt;/p&gt;

</description>
      <category>software</category>
      <category>programming</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Built a Google Maps Scraper to Generate Leads for My New Agency (And Why I Open-Sourced It)</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Fri, 14 Nov 2025 06:19:13 +0000</pubDate>
      <link>https://dev.to/j3rry320/how-i-built-a-google-maps-scraper-to-generate-leads-for-my-new-agency-and-why-i-open-sourced-it-3lad</link>
      <guid>https://dev.to/j3rry320/how-i-built-a-google-maps-scraper-to-generate-leads-for-my-new-agency-and-why-i-open-sourced-it-3lad</guid>
      <description>&lt;p&gt;When you're building a brand-new agency, there’s one problem you can’t escape:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding clients.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we had a portfolio, before we had referrals, before anyone even knew our name — we needed a reliable way to identify businesses that might need websites, branding, software, or digital marketing.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;&lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt;&lt;/strong&gt; — my newly formed agency — that responsibility fell on me.&lt;/p&gt;

&lt;p&gt;And like many new founders, I turned to the simplest place to find businesses:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Google Maps.&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%2F9ya1sgh19w8g0oqz50o1.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%2F9ya1sgh19w8g0oqz50o1.png" alt="Google Maps The OG Lead Generator Machine" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Struggle Every New Agency Knows
&lt;/h2&gt;

&lt;p&gt;If you've ever done manual prospecting, you know the routine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Search “Interior Designers near me”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll, scroll, scroll&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click each listing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the name, phone, rating, URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Paste into a spreadsheet&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat 150+ times&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s slow. It's painful. And it steals hours from the work that actually builds your agency.&lt;/p&gt;

&lt;p&gt;One night — after manually extracting yet another list — I said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Enough. I’m automating this.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that turned into a tool that helped my agency survive its early months.&lt;/p&gt;


&lt;h2&gt;
  
  
  From Internal Hack → Lead Machine
&lt;/h2&gt;

&lt;p&gt;I opened VS Code and wrote a small Puppeteer script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Launch Google Maps&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search for a keyword&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll automatically&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extract business details&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save everything cleanly&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first version was rough. But it worked.&lt;/p&gt;

&lt;p&gt;I could instantly generate lists like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cafes in Cuttack&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Marketing agencies in Bhubaneswar&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retail shops in Saheed Nagar&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manufacturers across Odisha&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a brand-new agency, this sped up our outreach dramatically.&lt;br&gt;&lt;br&gt;
Instead of spending hours gathering data, I could spend hours pitching and closing.&lt;/p&gt;
&lt;h2&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%2Ftplyvynoaorx9fw6d8bz.png" alt="Every Developer Automates Every Single Task" width="800" height="800"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Why I Decided to Open-Source It
&lt;/h2&gt;

&lt;p&gt;Initially, the scraper was private — a purely internal survival tool.&lt;br&gt;&lt;br&gt;
But the more I talked to other freelancers and small agencies, the more I realised:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Everyone is struggling with lead generation, especially early on.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I cleaned up the tool, added a CLI, refined the output, wrote proper documentation, and published it.&lt;/p&gt;

&lt;p&gt;Originally, I wanted to publish it as a scoped package (&lt;code&gt;@cml/google-maps-scraper&lt;/code&gt;), but scoped namespaces are paid.&lt;br&gt;&lt;br&gt;
So I renamed it clean and simple:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;code&gt;gmaps-scraper&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A free, open-source Google Maps scraping tool for everyone.&lt;/p&gt;

&lt;p&gt;NPM: &lt;a href="https://www.npmjs.com/package/gmaps-scraper" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/gmaps-scraper&lt;/a&gt;&lt;br&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/Code-Media-Labs/google-maps-scraper" rel="noopener noreferrer"&gt;https://github.com/Code-Media-Labs/google-maps-scraper&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What the Tool Can Do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;🔍 Scrape &lt;em&gt;any&lt;/em&gt; Google Maps query&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🏪 Extract business details (name, rating, reviews, phone, URL)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔁 Auto-scroll until all results are loaded&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🛡 Automatically deduplicate entries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;📦 Export results to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;results.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;results.xlsx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🧰 Use as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A CLI command&lt;/li&gt;
&lt;li&gt;  A Node.js importable library&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For freelancers and new agencies, this is a huge time-saver.&lt;/p&gt;


&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Global (CLI usage):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; gmaps-scraper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Local (project usage):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;gmaps-scraper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  CLI Usage
&lt;/h2&gt;

&lt;p&gt;Search for all interior designers in Bhubaneswar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gmaps-scraper scrape &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"Interior Designers in Bhubaneswar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Custom output path:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gmaps-scraper scrape &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"Cafes in Cuttack"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; cafes.json 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Headless mode + limited scrolls:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gmaps-scraper scrape &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"Hotels in Puri"&lt;/span&gt; &lt;span class="nt"&gt;--headless&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💻 Library Usage (Node.js)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;scrapeGoogleMaps&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="s2"&gt;gmaps-scraper&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;results&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;scrapeGoogleMaps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;

&lt;span class="na"&gt;searchQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cafes in Cuttack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="na"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cafes.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;maxScrollAttempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="kc"&gt;true&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="s2"&gt;`Found &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; cafes`&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;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Example Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"Urban Café"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"4.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"reviews_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"1,204"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"+91 9876543210"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"maps_link"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"https://www.google.com/maps/place/..."&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real Talk: Google Maps Can Break Your Selectors
&lt;/h2&gt;

&lt;p&gt;Scraping Google Maps isn't &lt;em&gt;“set-and-forget.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;class names&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;container structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;layout&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;nested DOM elements&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your selectors may break.&lt;br&gt;&lt;br&gt;
But fixing them is simple if you know what to look for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Old selector:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;const name = await element.$eval('.business-name', el =&amp;gt; el.textContent);&lt;/code&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Updated selector:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;const name = await element.$eval('.place-title span', el =&amp;gt; el.textContent);&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Whenever Google makes a UI update, just inspect again and adjust your selectors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Use This?
&lt;/h2&gt;

&lt;p&gt;This tool is built for anyone who needs structured business data from Google Maps — especially if you’re trying to grow or automate your workflow. You’ll benefit from this scraper if you are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A new agency&lt;/strong&gt; looking for qualified leads quickly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A freelancer&lt;/strong&gt; who wants to find clients without spending hours searching manually&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A marketer&lt;/strong&gt; needing business lists for outreach, campaigns, or segmentation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A founder&lt;/strong&gt; validating a market or researching local competitors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An SEO specialist&lt;/strong&gt; creating or updating business directories&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A developer&lt;/strong&gt; building automation tools, dashboards, or datasets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A data analyst&lt;/strong&gt; collecting local business information for insights&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re early in your journey and want to save time, remove repetitive work, and scale your lead generation, this tool gives you a massive head start.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming Next
&lt;/h2&gt;

&lt;p&gt;I’m actively adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Address, website, and business hours extraction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anti-detection and rate-limiting features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Region/language support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cleaner error handling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CSV and CRM-friendly output formats&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to contribute, PRs are welcome!&lt;/p&gt;




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

&lt;p&gt;When you're building a new agency, every hour counts.&lt;br&gt;&lt;br&gt;
This tool saved me countless hours of lead generation — and helped bring in our earliest clients.&lt;/p&gt;

&lt;p&gt;That’s why I open-sourced it.&lt;br&gt;&lt;br&gt;
If it helps someone else who’s just starting, it’s worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you’ve built an internal tool that saved you time… consider open-sourcing it. You might help someone who’s exactly where you were.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  And as always, Happy Coding 🧑🏻‍💻
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Built with ❤️ by &lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to Use the CSS aspect-ratio Property for Responsive Layouts</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sun, 28 Sep 2025 14:47:53 +0000</pubDate>
      <link>https://dev.to/j3rry320/how-to-use-the-css-aspect-ratio-property-for-responsive-layouts-20pd</link>
      <guid>https://dev.to/j3rry320/how-to-use-the-css-aspect-ratio-property-for-responsive-layouts-20pd</guid>
      <description>&lt;p&gt;&lt;strong&gt;As front-end developers&lt;/strong&gt;, we’ve all battled with &lt;em&gt;awkwardly stretched images&lt;/em&gt;, mysteriously squashed video embeds, or divs that seem to believe geometry is optional. Enter CSS’s &lt;code&gt;aspect-ratio&lt;/code&gt; property, &lt;em&gt;our knight in shining layout armour.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This property has quickly become a modern CSS favourite. It lets us maintain proportionate layouts without relying on padding hacks, JavaScript workarounds, or &lt;em&gt;obscure "magic numbers."&lt;/em&gt; Let’s dive into how it works and why it changes the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is aspect-ratio?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;aspect-ratio&lt;/code&gt; property &lt;strong&gt;allows you to control the ratio between an element’s width and height.&lt;/strong&gt; Instead of hardcoding both dimensions, you define the relationship, and the browser does the rest.&lt;/p&gt;

&lt;p&gt;Here’s the syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.element&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;9&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;That &lt;code&gt;16 / 9&lt;/code&gt;probably looks familiar. It’s the widescreen video ratio. You can also set it to &lt;code&gt;1 / 1&lt;/code&gt; for a perfect square or &lt;code&gt;21 / 9&lt;/code&gt; if you want something more &lt;em&gt;cinematic.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is aspect-ratio Useful?
&lt;/h2&gt;

&lt;p&gt;Before this property, developers had to rely on clever but &lt;em&gt;hacky tricks&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using padding-top percentages, such as &lt;strong&gt;56.25%&lt;/strong&gt; for a &lt;strong&gt;16:9 ratio (since 9 ÷ 16 = 0.5625).&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Calculating and setting height with &lt;strong&gt;JavaScript&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faking ratios&lt;/strong&gt; with background images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These approaches got the job done, but they were messy and didn’t always play well with responsive design. Now, &lt;em&gt;you can write one clean line of CSS and move on with your life.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does It Actually Work?
&lt;/h2&gt;

&lt;p&gt;Think of aspect-ratio as a rule that says: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“No matter what, keep these proportions.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;If only the &lt;strong&gt;width is set&lt;/strong&gt;, the &lt;strong&gt;height automatically adjusts.&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;If only the &lt;strong&gt;height is set,&lt;/strong&gt; the &lt;strong&gt;width adjusts&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;both are set explicitly&lt;/strong&gt;, &lt;strong&gt;aspect-ratio keeps quiet&lt;/strong&gt; and steps aside.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example with width and ratio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.video-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eee&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;Here, &lt;strong&gt;the width fills the parent, and the height follows along smoothly&lt;/strong&gt; at the correct ratio.&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%2Fxryyybx9i7n6uskriwuz.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%2Fxryyybx9i7n6uskriwuz.png" alt="Aspect ratio to the rescue" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Applications
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Video embeds:&lt;/strong&gt; YouTube and Vimeo iframes that stay perfectly proportioned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image grids:&lt;/strong&gt; Thumbnails that don’t squish or stretch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cards and placeholders:&lt;/strong&gt; Consistent layouts even without real content inside.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brand consistency&lt;/strong&gt;: Great for product photos where the visual style needs to stay uniform.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Combining with Other CSS Properties
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;aspect-ratio&lt;/code&gt; property pairs really well with other layout tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&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;Now you’ve got a consistently shaped card that looks clean and scales gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Support
&lt;/h2&gt;

&lt;p&gt;The best part is that modern browsers have supported it very well &lt;strong&gt;since 2021&lt;/strong&gt;. You don’t need to polyfill or worry about strange quirks. And since Internet Explorer has retired, &lt;em&gt;we can enjoy this feature with a lot less stress&lt;/em&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%2Fzz8qt45v2l1eid48sr4j.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%2Fzz8qt45v2l1eid48sr4j.png" alt="Designing the web" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Analogy
&lt;/h2&gt;

&lt;p&gt;Think of &lt;code&gt;aspect-ratio&lt;/code&gt; as the reliable friend in group photos. They’re never the ones who look unnaturally stretched or squeezed. While others get complicated, &lt;strong&gt;aspect-ratio just keeps everything looking balanced.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;aspect-ratio&lt;/code&gt; property &lt;strong&gt;may feel small at first&lt;/strong&gt;, but it &lt;strong&gt;quietly solves one of the longest-running pain points in web design&lt;/strong&gt;. It saves time, removes the need for hacks, and makes &lt;strong&gt;responsive design&lt;/strong&gt; a little more fun.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you haven’t used it yet, try adding aspect-ratio into your next project and see how much easier it makes layout work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And as always, &lt;strong&gt;&lt;em&gt;Happy Coding!&lt;/em&gt;&lt;/strong&gt; 👾&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>css</category>
      <category>web</category>
    </item>
    <item>
      <title>JSDoc: Your Secret Weapon for Adding Types to JavaScript (Without the Full TypeScript Overhaul)</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sat, 15 Feb 2025 12:01:58 +0000</pubDate>
      <link>https://dev.to/j3rry320/jsdoc-your-secret-weapon-for-adding-types-to-javascript-without-the-full-typescript-overhaul-3n20</link>
      <guid>https://dev.to/j3rry320/jsdoc-your-secret-weapon-for-adding-types-to-javascript-without-the-full-typescript-overhaul-3n20</guid>
      <description>&lt;p&gt;&lt;strong&gt;TypeScript is a fantastic tool for adding static typing to JavaScript&lt;/strong&gt;, catching errors early and making large codebases more manageable.  But let's be honest, sometimes a &lt;strong&gt;full TypeScript migration can feel like climbing Mount Everest&lt;/strong&gt;.  What if you could get most of the benefits of a statically typed system without the complete overhaul?  &lt;strong&gt;Enter JSDoc, your secret weapon for adding types&lt;/strong&gt; (and excellent documentation!) to your JavaScript projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of JSDoc
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JSDoc, those familiar comments above your code&lt;/strong&gt;, can do more than explain what your functions do.  They can also define types! Using &lt;code&gt;@typedef&lt;/code&gt; and &lt;code&gt;@type&lt;/code&gt; you can create complex, &lt;strong&gt;reusable type definitions in your JavaScript files&lt;/strong&gt;.  This allows you to add type safety and improve code readability without the full complexity of TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JSDoc Might Be Right for You
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smaller Projects&lt;/strong&gt;: For smaller projects, the overhead of a full TypeScript setup might be overkill. JSDoc offers a lightweight way to add structure and documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradual Adoption&lt;/strong&gt;: JSDoc can be a fantastic stepping stone towards TypeScript. You can start with JSDoc, generate declaration files, and gradually transition to full TypeScript if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing JavaScript Codebases&lt;/strong&gt;: Migrating a massive JavaScript codebase to TypeScript can be daunting. JSDoc allows you to introduce types incrementally, without rewriting everything at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation as a First-Class Citizen&lt;/strong&gt;: JSDoc emphasizes documentation, which is crucial regardless of your typing system. A well-documented codebase is always a plus.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Defining Types with &lt;code&gt;@typedef&lt;/code&gt; and &lt;code&gt;@type&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here's where the magic happens.  Let's see how you define types using JSDoc:&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;/**
 * @typedef {Object} Point
 * @property {number} x - The x-coordinate.
 * @property {number} y - The y-coordinate.
 */&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Calculates the distance between two points.
 * @param {Point} p1 - The first point.
 * @param {Point} p2 - The second point.
 * @returns {number} The distance between the points.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p2&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;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;.&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;p1&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;.&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;p1&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="k"&gt;return&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;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dy&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @type {Point}
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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="nf"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&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="na"&gt;y&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In this example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@typedef {Object} Point&lt;/code&gt;: &lt;strong&gt;Defines a Point type as an object with x and y properties, both numbers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@param {Point} p1&lt;/code&gt;: &lt;strong&gt;Uses the Point type to specify the type of the p1 parameter&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@param {Point} p2&lt;/code&gt;: &lt;strong&gt;Uses the Point type to specify the type of the p2 parameter&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@type {Point}&lt;/code&gt;: &lt;strong&gt;Annotates the originPoint constant with the Point type&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Generating &lt;code&gt;.d.ts&lt;/code&gt; Files
&lt;/h2&gt;

&lt;p&gt;The real &lt;strong&gt;power of JSDoc&lt;/strong&gt; types comes when you &lt;strong&gt;generate TypeScript declaration files (.d.ts)&lt;/strong&gt;.  The TypeScript compiler (tsc) can parse your JSDoc comments and create these files for you.  This means you get type-checking and code completion in your IDE, even while writing plain JavaScript!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Here's the basic &lt;code&gt;tsconfig.json&lt;/code&gt; configuration:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2020"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"declaration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"emitDeclarationOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ONLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;emit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.d.ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./src/**/*.js"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Then, run &lt;code&gt;npx tsc&lt;/code&gt; (or &lt;code&gt;tsc&lt;/code&gt; if installed globally) in your project directory.  This will generate the &lt;code&gt;.d.ts&lt;/code&gt; files in your outDir (./dist in this example). Below you can see the declaration file tsc generates for you&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%2Ftes4w6xy3gyr36i3cyjc.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%2Ftes4w6xy3gyr36i3cyjc.png" alt="The generated declaration file for our example" width="800" height="920"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best of Both Worlds
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JSDoc&lt;/strong&gt; with type generation &lt;strong&gt;provides a fantastic middle ground&lt;/strong&gt;. You can enjoy the benefits of type safety and improved documentation without the full overhead of TypeScript.  It's a great option for projects where &lt;strong&gt;a complete TypeScript migration isn't feasible or necessary&lt;/strong&gt;, allowing you to gradually introduce types and improve your code quality over time.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;next time you're thinking about adding types to your JavaScript&lt;/strong&gt;, don't immediately jump to a full TypeScript conversion. &lt;strong&gt;Consider JSDoc&lt;/strong&gt; – it might be exactly what you need.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And as always&lt;/em&gt; &lt;br&gt;
&lt;strong&gt;Happy Coding&lt;/strong&gt; 🧑🏻‍💻&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Effortlessly Generate Professional Videos with AI-Powered Clip-Creator CLI</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sat, 01 Feb 2025 05:18:17 +0000</pubDate>
      <link>https://dev.to/j3rry320/create-professional-videos-in-minutes-with-ai-powered-clip-creator-cli-413g</link>
      <guid>https://dev.to/j3rry320/create-professional-videos-in-minutes-with-ai-powered-clip-creator-cli-413g</guid>
      <description>&lt;p&gt;In today's digital age, &lt;strong&gt;video content is king&lt;/strong&gt;. Whether you're a content creator, marketer, educator, or social media enthusiast, producing high-quality videos that engage and captivate your audience is crucial. However, the process of creating these videos &lt;em&gt;can often be time-consuming and complex&lt;/em&gt;. Enter Clip-Creator CLI - a groundbreaking command-line interface tool that leverages the power of AI to &lt;strong&gt;simplify and revolutionize video creation&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%2Foieq9k7i3buh825hitjn.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%2Foieq9k7i3buh825hitjn.png" alt="Clip-Creator CLI" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating high-quality video content has never been easier and cheaper&lt;/strong&gt;, thanks to the innovative Clip-Creator CLI. This open-source command-line tool leverages the power of LLMs to generate engaging video clips, complete with audio and scripts, in just a few simple steps. Whether you are a content creator, marketer, or educator, Clip-Creator CLI is here to &lt;em&gt;transform your video production process&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Clip-Creator CLI?
&lt;/h2&gt;

&lt;p&gt;In a world where video content reigns supreme, standing out requires both quality and efficiency. Clip-Creator CLI provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI-Powered Script Generation&lt;/strong&gt;: Let advanced AI models handle the scriptwriting, ensuring engaging and relevant content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Video Creation&lt;/strong&gt;: Customize video parameters to suit your needs, from duration to content category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diverse Content Categories and Tones&lt;/strong&gt;: Match your video’s style and message with a wide range of categories and tones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-Quality Audio&lt;/strong&gt;: Enhance your videos with premium audio tracks sourced from FreeSound.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-Definition Visuals&lt;/strong&gt;: Access a vast library of stunning visuals from Pexels to make your content visually appealing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fact-Checking Support&lt;/strong&gt;: Keep your content accurate and trustworthy with built-in fact-checking features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-Friendly Web Interface&lt;/strong&gt;: Seamlessly navigate through the video creation process with an intuitive web UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Clip-Creator CLI Can Automate Your Social Media Video Generation
&lt;/h2&gt;

&lt;p&gt;Clip-Creator CLI isn't just about creating standalone videos; it can be integrated with other services to automate your social media video generation process. Here are some ways to get the most out of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text-to-Speech (TTS) Services: Integrate with TTS APIs to automatically add realistic voiceovers to your videos. Whether it's a tutorial or a marketing video, TTS can bring your scripts to life.&lt;/li&gt;
&lt;li&gt;Social Media APIs: Use APIs to automatically post your videos to social media platforms. Schedule posts, track engagement, and streamline your social media strategy without lifting a finger.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Free and Efficient Content Creation
&lt;/h2&gt;

&lt;p&gt;One of the best things about Clip-Creator CLI is that you can &lt;strong&gt;generate videos for free using Pexels and FreeSound APIs&lt;/strong&gt;. As long as you respect the rate limits of these services, you can access a wealth of high-quality visuals and audio tracks without any cost. Additionally, &lt;strong&gt;leveraging large language models (LLMs) within the usage limits&lt;/strong&gt; allows you to keep the content creation process efficient and cost-effective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Total Video Creation Capacity
&lt;/h2&gt;

&lt;p&gt;On average, you can create approximately &lt;strong&gt;1600-10,000&lt;/strong&gt; &lt;em&gt;videos per month for free&lt;/em&gt; using Clip-Creator CLI. This estimate assumes balanced usage across all APIs and adherence to their respective rate limits.&lt;/p&gt;

&lt;p&gt;Unleash your creativity and start producing a substantial volume of high-quality videos effortlessly!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To start creating amazing video content with Clip-Creator CLI, follow these simple steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Clip-Creator CLI:&lt;/strong&gt;&lt;br&gt;
Install the package using npm or yarn or npx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; clip-creator
&lt;span class="c"&gt;#or &lt;/span&gt;
yarn add global clip-creator 
&lt;span class="c"&gt;#or &lt;/span&gt;
npx clip-creator &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configure Your API Keys:&lt;/strong&gt;&lt;br&gt;
To get started, create a configuration file containing your API keys along with any other default or advanced configurations you wish to pass to the package. This ensures that the video creation process is seamless and tailored to your needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"freeSoundApiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"groqApiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pexelsApiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Generate Your First Video Clip:&lt;/strong&gt; &lt;br&gt;
Customize your video creation by specifying parameters that suit your requirements. Use the configuration file you created to streamline the process, or you can pass the configuration interactively via the CLI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clip-creator create &lt;span class="nt"&gt;--category&lt;/span&gt; &lt;span class="s2"&gt;"Educational"&lt;/span&gt; &lt;span class="nt"&gt;--tone&lt;/span&gt; &lt;span class="s2"&gt;"Professional"&lt;/span&gt; &lt;span class="nt"&gt;--topic&lt;/span&gt; &lt;span class="s2"&gt;"Rise of AI"&lt;/span&gt; &lt;span class="nt"&gt;--duration&lt;/span&gt; 30 &lt;span class="nt"&gt;--config&lt;/span&gt; /path/to/config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Tip: Remember, the order of precedence is: Command Line Interface (CLI) -&amp;gt; Config File -&amp;gt; Interactive Prompt. This means that any parameters specified in the CLI will override those in the config file, and those in the config file will override interactive prompts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Interactive Configuration&lt;/strong&gt;&lt;br&gt;
If you prefer not to create a configuration file or pass parameters via the CLI, you can directly run the clip-creator create command to interactively input all necessary configurations. This mode will prompt you for each required detail, ensuring a smooth and guided setup process.&lt;/p&gt;

&lt;p&gt;Simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clip-creator create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will walk you through setting up your video creation parameters interactively, making it convenient for users who prefer a hands-on approach without pre-setting configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch the Web Interface
&lt;/h2&gt;

&lt;p&gt;To start the web interface and enjoy a user-friendly experience, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start the Web Interface:&lt;/strong&gt;&lt;br&gt;
Run the following command to launch the web interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clip-creator web &lt;span class="nt"&gt;--port&lt;/span&gt; 3003
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Open Your Browser:&lt;/strong&gt; &lt;br&gt;
After starting the web interface, open your web browser and navigate to:&lt;br&gt;
&lt;a href="http://localhost:3003/index.html" rel="noopener noreferrer"&gt;http://localhost:3003/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will bring up the web-based UI, where you can visualize and manage your video creation process seamlessly. The web interface provides a clean and intuitive environment, allowing you to see the output and make any necessary adjustments interactively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Involved
&lt;/h2&gt;

&lt;p&gt;We invite the community to contribute, improve, and use Clip-Creator CLI. It’s open-source and available under the MIT license. For professional inquiries or to hire the author, visit my &lt;a href="https://www.linkedin.com/in/jerrythejsguy/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Clip-Creator CLI is your go-to tool for creating professional-grade videos with minimal effort&lt;/strong&gt;. Leveraging AI and seamless integration with top content sources, it has never been easier to produce stunning videos. Get started with Clip-Creator CLI today and elevate your content creation to new heights!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Introducing Markdown Parser React v2.0.0: Your Go-To Markdown Rendering Solution</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Mon, 20 Jan 2025 11:01:40 +0000</pubDate>
      <link>https://dev.to/j3rry320/introducing-markdown-parser-react-v200-your-go-to-markdown-rendering-solution-1ii5</link>
      <guid>https://dev.to/j3rry320/introducing-markdown-parser-react-v200-your-go-to-markdown-rendering-solution-1ii5</guid>
      <description>&lt;p&gt;If you've ever worked with Markdown in a React or a Next.js application, you know how important it is to have a robust and customizable parser. That’s why, I’m thrilled to introduce the newest version of Markdown Parser React—a feature-rich, easy-to-use, and highly customizable React component for rendering Markdown content in your applications. &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%2F0h9v6cfybc92ozlfa65x.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%2F0h9v6cfybc92ozlfa65x.png" alt="markdown-parser-react" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Markdown Parser React? 🤔
&lt;/h2&gt;

&lt;p&gt;Whether you’re building a blog, a documentation platform, or an interactive dashboard, Markdown Parser React offers everything you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full Markdown Support&lt;/strong&gt;: From headers and links to tables and code blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable Styling&lt;/strong&gt;: Tailor the look and feel to match your brand or project theme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: Designed with ARIA attributes for better screen reader support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended Features&lt;/strong&gt;: Task lists, definition lists, syntax highlighting, math equations, and more!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline and Block Rendering&lt;/strong&gt;: Handle both inline Markdown (e.g., bold text, math equation, underlined text) and block elements (e.g., tables and lists).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight and Fast&lt;/strong&gt;: Optimized for performance to keep your app snappy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started 📦
&lt;/h2&gt;

&lt;p&gt;You can install Markdown Parser React in your project using npm, yarn, or pnpm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;markdown-parser-react

&lt;span class="c"&gt;# or&lt;/span&gt;

yarn add markdown-parser-react

&lt;span class="c"&gt;# or&lt;/span&gt;

pnpm add markdown-parser-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Usage 🛠️
&lt;/h2&gt;

&lt;p&gt;Rendering Markdown content is as easy as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;markdown-parser-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;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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;"#&lt;/span&gt; &lt;span class="na"&gt;Hello&lt;/span&gt; &lt;span class="na"&gt;World&lt;/span&gt;
&lt;span class="na"&gt;This&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt; &lt;span class="err"&gt;**&lt;/span&gt;&lt;span class="na"&gt;Markdown&lt;/span&gt;&lt;span class="err"&gt;**!"&lt;/span&gt; &lt;span class="p"&gt;/&amp;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;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Features 🔥
&lt;/h2&gt;

&lt;p&gt;Markdown Parser React shines when you want to customize your Markdown rendering. Here’s how you can configure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Markdown&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;markdown-parser-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;function&lt;/span&gt; &lt;span class="nf"&gt;BlogPost&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;markdownContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
# Welcome to My Blog  

This is a _formatted_ paragraph with a [link](https://example.com).

- [x] Task 1
- [ ] Task 2

&lt;/span&gt;&lt;span class="se"&gt;\`\`\`&lt;/span&gt;&lt;span class="s2"&gt;javascript
console.log("Hello, Markdown!");
&lt;/span&gt;&lt;span class="se"&gt;\`\`\`&lt;/span&gt;&lt;span class="s2"&gt;

| Column 1 | Column 2 |
|----------|----------|
| Cell 1   | Cell 2   |
`&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;markdownContent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;customClasses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;headings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog-heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;paragraphs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog-paragraph&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;customStyles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;headings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2c3e50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Georgia, serif&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;linkTarget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sanitizeHtml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxNestingLevel&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="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"blog-content"&lt;/span&gt;
    &lt;span class="p"&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;h2&gt;
  
  
  Features Highlight:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom CSS Classes&lt;/strong&gt;: Assign specific CSS classes to headings, paragraphs, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom CSS Styles&lt;/strong&gt;: Assign specific styles to headings, paragraphs, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe HTML Rendering&lt;/strong&gt;: Protects your app from potential security threats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessible by Default&lt;/strong&gt;: Includes ARIA labels for better UX.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Built for Modern Frameworks 🏗️
&lt;/h2&gt;

&lt;p&gt;If &lt;strong&gt;you’re using Next.js&lt;/strong&gt;, avoid server-client rendering mismatches by dynamically importing the Markdown component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/dynamic&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;Markdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;markdown-parser-react&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;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;content&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Who Should Use Markdown Parser React? 🌟
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Developers building blogs or documentation sites.&lt;/li&gt;
&lt;li&gt;Content creators who need Markdown support in their React projects.&lt;/li&gt;
&lt;li&gt;Teams, requiring a secure and accessible Markdown rendering solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Join the Community
&lt;/h2&gt;

&lt;p&gt;Markdown Parser React is open-source and MIT-licensed. If you find it useful, consider giving it a ⭐️ on &lt;a href="https://github.com/J3rry320/markdown-parser-react" rel="noopener noreferrer"&gt;Github&lt;/a&gt;. Contributions and feedback are always welcome!&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out Today!
&lt;/h2&gt;

&lt;p&gt;Don’t miss out on making your Markdown content shine in React. Install &lt;a href="https://www.npmjs.com/package/markdown-parser-react" rel="noopener noreferrer"&gt;Markdown Parser React&lt;/a&gt; today and supercharge your projects. &lt;/p&gt;

&lt;p&gt;What feature are you most excited about? &lt;em&gt;&lt;strong&gt;Let me know in the comments or share your thoughts on GitHub!&lt;/strong&gt;&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Happy Coding 🧑🏻‍💻&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How “Silicon Valley” Inspired Me to Create a Photo Compressor CLI for Web Developers</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Wed, 15 Jan 2025 11:56:21 +0000</pubDate>
      <link>https://dev.to/j3rry320/i-created-a-photo-compressor-cli-tool-18gl</link>
      <guid>https://dev.to/j3rry320/i-created-a-photo-compressor-cli-tool-18gl</guid>
      <description>&lt;p&gt;Hey there! So I was binging the famous &lt;em&gt;HBO TV series Silicon Valley&lt;/em&gt; and got inspired to build something related to compression. And here's a thought – have you ever struggled with optimizing images for your website or project? If so, After hacking around for half a day I've got something exciting: &lt;strong&gt;photo-compressor&lt;/strong&gt;. It's a command-line tool designed to compress and convert images to the super-light .webp format seamlessly.&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%2F8m81x6bmjt3rf7jyjdfx.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%2F8m81x6bmjt3rf7jyjdfx.png" alt="Photo Compressor" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tool is powered by &lt;strong&gt;Sharp&lt;/strong&gt;, which makes it fast and reliable. It helps you optimize media like a pro. Whether you’re working with local files or cloud-hosted images, &lt;strong&gt;photo-compressor&lt;/strong&gt; has your back. I understand it's just a wrapper around sharp but we will be adding features and further optimization down the line&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use photo-compressor?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Here’s why you’ll love it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimize Local Images&lt;/strong&gt;: Compress images in a local directory effortlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimize Cloud Images&lt;/strong&gt;: Process images directly from URLs or even a directory of image URLs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Naming Headaches&lt;/strong&gt;: It’ll automatically handle file name conflicts for you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Detailed Logs&lt;/strong&gt;: Get insights on savings and processing time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation: Get Started in No Time
&lt;/h2&gt;

&lt;p&gt;Installing photo-compressor is a breeze. Choose your favourite package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; photo-compressor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you’re a Yarn fan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn global add photo-compressor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t want to install anything globally? No problem! Run it directly using npx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx photo-compressor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Use It: Commands and Options
&lt;/h2&gt;

&lt;p&gt;Using photo-compressor is straightforward. Here’s the rundown of its commands and options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Options
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-h, --help&lt;/code&gt;: Display help information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-V, --version&lt;/code&gt;: Check the current version of the package.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Commands
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1. Optimize Local Images&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Easily compress images from a local directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;photo-compressor &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nt"&gt;--dir&lt;/span&gt; &amp;lt;path_to_directory&amp;gt; &lt;span class="nt"&gt;--output&lt;/span&gt; &amp;lt;path_to_output_directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Options&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-d, --dir &amp;lt;path&amp;gt;&lt;/code&gt;: Directory to scan for images (required).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-o, --output &amp;lt;path&amp;gt;&lt;/code&gt;: Output directory for optimized images (default: &lt;code&gt;./optimized&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2. Optimize Cloud Images&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Do you have images hosted online? Compress them like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;photo-compressor cloud &lt;span class="nt"&gt;--url&lt;/span&gt; &amp;lt;image_url_OR_array_of_images&amp;gt; &lt;span class="nt"&gt;--output&lt;/span&gt; &amp;lt;path_to_output_directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Options&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-u, --url &amp;lt;url&amp;gt;&lt;/code&gt;: URL of the image or an array of image URLs (required).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-o, --output &amp;lt;path&amp;gt;&lt;/code&gt;: Output directory for optimized images (default: &lt;code&gt;./optimized&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real-World Examples
&lt;/h3&gt;

&lt;p&gt;Here are some practical examples to get you started:&lt;/p&gt;

&lt;h4&gt;
  
  
  Optimize Local Images
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;photo-compressor &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nt"&gt;--dir&lt;/span&gt; ./images &lt;span class="nt"&gt;--output&lt;/span&gt; ./optimized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Optimize Cloud Images
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;photo-compressor cloud &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com/image1.jpg'&lt;/span&gt;, &lt;span class="s1"&gt;'https://example.com/image2.jpg'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; ./optimized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Quick Backstory
&lt;/h3&gt;

&lt;p&gt;Why did I create this tool? Honestly, I was binge-watching &lt;strong&gt;Silicon Valley&lt;/strong&gt; and got inspired to build something related to compression. It turns out, it’s a neat solution for web developers who need to quickly optimize images for better web performance. (P.S. I’m still a noob, so feedback is always welcome!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Join the Fun
&lt;/h3&gt;

&lt;p&gt;The development community is all about collaboration. If you’ve got ideas to improve this tool or find any bugs, head over to our &lt;a href="https://github.com/J3rry320/photo-compressor" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and contribute!&lt;/p&gt;

&lt;p&gt;Happy Coding! Let me know in the comments what you think about the package.  &lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Themeify: A Simple Tool to Beautify Your React and Next.js Projects</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Wed, 08 Jan 2025 14:23:04 +0000</pubDate>
      <link>https://dev.to/j3rry320/themeify-a-simple-tool-to-beautify-your-react-and-nextjs-projects-329</link>
      <guid>https://dev.to/j3rry320/themeify-a-simple-tool-to-beautify-your-react-and-nextjs-projects-329</guid>
      <description>&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%2Fslyp5dnz4ipr80hb8b69.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%2Fslyp5dnz4ipr80hb8b69.png" alt="Themeify" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Are you &lt;strong&gt;tired of spending endless hours tweaking fonts and colour palettes&lt;/strong&gt; for your React and Next.js projects? Meet Themeify, your new best friend for effortlessly applying stunning themes!&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Themeify All About?
&lt;/h2&gt;

&lt;p&gt;Themeify is a &lt;strong&gt;simple CLI tool&lt;/strong&gt; designed to help you quickly &lt;strong&gt;apply custom fonts and colour palettes&lt;/strong&gt; to your projects. With support for popular UI frameworks like MUI, Tailwind, ShadCN, and Bootstrap, you’ll never have to worry about manually setting up your themes again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Themeify?
&lt;/h2&gt;

&lt;p&gt;We know how tedious it can be to get your UI just right. Themeify takes away the hassle, letting you focus more on building awesome features and less on fiddly design tweaks. It’s perfect for those who want a cohesive, professional-looking interface without the fuss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apply Themes&lt;/strong&gt;: Easily apply predefined or custom themes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview Themes&lt;/strong&gt;: Get a sneak peek of your themes with specified colour palettes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List Palettes and Themes&lt;/strong&gt;: Quickly see all available options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleanup&lt;/strong&gt;: Keep your project neat by removing generated files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Installation is a breeze! Just use npm, yarn, or npx:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using npm&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; themeify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using yarn&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn global add themeify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using npx (no installation required)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx themeify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Use Themeify
&lt;/h2&gt;

&lt;p&gt;Applying a theme is as simple as running a command. Here’s an example of applying a custom theme to a React project using Tailwind CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;themeify apply &lt;span class="nt"&gt;--path&lt;/span&gt; ./my-react-app &lt;span class="nt"&gt;--framework&lt;/span&gt; Tailwind &lt;span class="nt"&gt;--theme&lt;/span&gt; vibrantPastel &lt;span class="nt"&gt;--palette&lt;/span&gt; vibrant &lt;span class="nt"&gt;--font&lt;/span&gt; Poppins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you’re like me and prefer a guided setup, just run the:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;themeify apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You’ll get a series of prompts to help you choose the right framework, theme, palette, and font. Easy peasy!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Built Themeify
&lt;/h2&gt;

&lt;p&gt;We created Themeify to make the lives of developers easier. We understand the struggle of making a UI look perfect and how much time it can consume. With Themeify, you can achieve a polished look in minutes, freeing up your time to work on what matters — &lt;strong&gt;building great features&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Join the Community
&lt;/h2&gt;

&lt;p&gt;We’d love for you to join the Themeify community! If you have any issues, or suggestions, or just want to say hi, head over to our &lt;a href="https://github.com/J3rry320/themeify/issues" rel="noopener noreferrer"&gt;GitHub Issues page&lt;/a&gt;. Your feedback is super valuable to us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contribute to Themeify
&lt;/h2&gt;

&lt;p&gt;If you’re interested in &lt;a href="https://github.com/J3rry320/themeify/pulls" rel="noopener noreferrer"&gt;contributing&lt;/a&gt;, we welcome you with open arms! Check out our Pull Request guidelines and join us in making Themeify even better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Connect
&lt;/h2&gt;

&lt;p&gt;If you like what I’m doing and want to collaborate or have any projects in mind, feel free to reach out! You can find me on &lt;a href="https://www.linkedin.com/in/jerrythejsguy/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;This project is licensed under the MIT License. See the &lt;a href="https://github.com/J3rry320/themeify/blob/main/LICENSE" rel="noopener noreferrer"&gt;LICENSE&lt;/a&gt; file for more details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;We hope you find Themeify as useful as we do. Happy theming, and more importantly Happy Coding!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>npm</category>
    </item>
    <item>
      <title>Introducing Vocalize.ts: Simplify Voice Interaction in Your Web Application</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Wed, 21 Aug 2024 16:55:23 +0000</pubDate>
      <link>https://dev.to/j3rry320/introducing-vocalizets-simplify-voice-interaction-in-your-web-application-4c0n</link>
      <guid>https://dev.to/j3rry320/introducing-vocalizets-simplify-voice-interaction-in-your-web-application-4c0n</guid>
      <description>&lt;p&gt;In today's world, voice interaction is becoming an increasingly important part of user experience. From virtual assistants to voice-activated devices, the ability to interact with technology through speech is not just a luxury—it's becoming an expectation. However, implementing voice recognition and text-to-speech (TTS) features in web applications can be complex and time-consuming. That's where Vocalize.ts comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Vocalize.ts?&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%2F3pgyjvs6owr4vyvr6ibf.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%2F3pgyjvs6owr4vyvr6ibf.png" alt="Image description" width="500" height="500"&gt;&lt;/a&gt;&lt;br&gt;
Vocalize.ts is a lightweight TypeScript library designed to make it easier than ever to integrate speech recognition and synthesis into your web applications. Whether you want to add simple voice commands to control your app or create a fully interactive voice-driven interface, Vocalize.ts provides the tools you need with minimal setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Vocalize.ts?&lt;/strong&gt;&lt;br&gt;
When I started working on Vocalize.ts, I noticed that while there were plenty of libraries for voice interaction, most were either too complex, heavy, or lacked proper documentation. My goal with Vocalize.ts was to create a library that is easy to use, well-documented, and optimized for modern web development practices.&lt;/p&gt;
&lt;h3&gt;
  
  
  Here are some of the key benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Use:&lt;/strong&gt; Vocalize.ts is designed with simplicity in mind. With just a few lines of code, you can set up voice commands and start recognizing user input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable TTS:&lt;/strong&gt; The library allows you to customize the text-to-speech output for each command, providing options for volume, rate, pitch, and even voice selection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preset Moods:&lt;/strong&gt; With Vocalize.ts, you can easily set the tone of your app’s responses by using predefined mood settings like happy, calm, sad, angry, surprised, and neutral. These moods adjust the TTS settings automatically to match the desired emotional tone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight:&lt;/strong&gt; Built with microbundle, Vocalize.ts is optimized for performance and small bundle sizes, making it a perfect fit for modern web applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Browser Compatibility:&lt;/strong&gt; Vocalize.ts leverages the Web Speech API, ensuring compatibility across all modern browsers that support speech recognition and synthesis.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;How Does It Work?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Vocalize.ts abstracts the complexities of the Web Speech API, allowing you to focus on building your application rather than dealing with the nuances of voice recognition and synthesis. Here’s a quick example to show how easy it is to get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Vocalize&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="s2"&gt;vocalize.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize Vocalize with default options&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vocalize&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;Vocalize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;recognitionOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;continuous&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;interimResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;ttsOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;volume&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="na"&gt;rate&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="na"&gt;pitch&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;presetMood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;happy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onCommandRecognized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&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;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="s2"&gt;`Command recognized: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;phrase&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onCommandUnrecognized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&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;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="s2"&gt;`Command not recognized: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;phrase&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Speech recognition error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Register a command&lt;/span&gt;
&lt;span class="nx"&gt;vocalize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerCommands&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&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="na"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;happy&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="c1"&gt;// Start listening for commands&lt;/span&gt;
&lt;span class="nx"&gt;vocalize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startListening&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, when the user says "hello world," the application responds with a cheerful greeting, thanks to the happy mood setting.&lt;/p&gt;

&lt;p&gt;Use Cases for Vocalize.ts&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Voice-Activated Interfaces: Create hands-free interfaces for users with accessibility needs or for scenarios where touch or keyboard interaction isn't practical.&lt;/li&gt;
&lt;li&gt;Interactive Tutorials: Guide users through your application with voice commands and responses, making tutorials more engaging and interactive.&lt;/li&gt;
&lt;li&gt;Smart Home Applications: Integrate Vocalize.ts into your web-based smart home dashboard to control devices through voice commands.&lt;/li&gt;
&lt;li&gt;Educational Tools: Build educational apps that interact with users using voice, making learning more immersive and effective.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Getting Started with Vocalize.ts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ready to add voice interaction to your web app? Getting started is easy. Simply install Vocalize.ts via npm or yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vocalize.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add vocalize.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For detailed documentation, examples, and more, check out the Vocalize.ts &lt;a href="https://github.com/j3rry320/vocalize.ts" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Contributing to Vocalize.ts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Vocalize.ts is an open-source project, and contributions are welcome!&lt;/strong&gt;&lt;/em&gt; Whether you have ideas for new features, want to improve the documentation, or want to report a bug, your input is valuable. Head over to the GitHub repo to get involved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voice interaction is the future, and with Vocalize.ts, adding this capability to your web applications has never been easier.&lt;/strong&gt; Whether you're building the next big voice-activated app or want to add some cool voice features to your existing project, Vocalize.ts is here to help.&lt;/p&gt;

&lt;p&gt;Try it out today, and bring your web applications to life with voice!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Server-Side Rendering: A Comprehensive Guide for Modern Web Developers</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Mon, 24 Apr 2023 12:03:32 +0000</pubDate>
      <link>https://dev.to/j3rry320/server-side-rendering-a-comprehensive-guide-for-modern-web-developers-24kp</link>
      <guid>https://dev.to/j3rry320/server-side-rendering-a-comprehensive-guide-for-modern-web-developers-24kp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;As web developers, we always strive to create fast, efficient, and user-friendly websites. &lt;/p&gt;
&lt;/blockquote&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%2Ftahom3st8477r0r9vwdp.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%2Ftahom3st8477r0r9vwdp.jpeg" alt="What is server side rendering?" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One essential aspect of delivering a great user experience is ensuring that our content loads quickly and efficiently. Server-side rendering (SSR) is a technique that can help us achieve this goal. In this article, we will discuss the basics of server-side rendering, its benefits, and how to implement it in various popular web development frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Server-Side Rendering?
&lt;/h2&gt;

&lt;p&gt;Server-side rendering is the process of generating the final HTML on the server and sending it to the client's browser. With SSR, the server pre-renders the content into HTML and then sends it to the browser, which only has to display the pre-rendered content.&lt;/p&gt;

&lt;p&gt;This approach contrasts with client-side rendering (CSR), where the browser receives an empty or minimal HTML shell and uses JavaScript to fetch and display the content. While CSR offers some benefits, such as faster subsequent page loads, it can also lead to slow initial loading times and negatively impact SEO.&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%2Fgux2o4fo6zqhqynmdnk8.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%2Fgux2o4fo6zqhqynmdnk8.png" alt="What is server side rendering" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Server-Side Rendering
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Improved performance&lt;/em&gt;&lt;/strong&gt;: Since the browser receives pre-rendered HTML from the server, it can start displaying the content almost immediately. This leads to faster initial page load times and a better user experience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Better SEO&lt;/strong&gt;&lt;/em&gt;: Web crawlers like Googlebot can have difficulty indexing websites that rely heavily on client-side rendering. With SSR, search engines can easily crawl and index your content, leading to better search rankings.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Enhanced social sharing&lt;/strong&gt;&lt;/em&gt;: When a user shares a link to your website on social media platforms, these platforms often fetch the content and display a preview. SSR makes it easier for these platforms to fetch the required information and create a visually appealing preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Server-Side Rendering
&lt;/h2&gt;

&lt;p&gt;Now that we understand the benefits of server-side rendering, let's explore how to implement it in some popular web development frameworks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React&lt;/strong&gt;&lt;br&gt;
To implement SSR in a React application, you can use the popular Next.js framework. Next.js is a powerful, production-ready framework that allows you to create server-rendered React applications easily. It provides a built-in development server, automatic code splitting, and static site generation capabilities.&lt;/p&gt;

&lt;p&gt;To get started with Next.js, you can follow the official &lt;a href="https://nextjs.org/docs/getting-started" rel="noopener noreferrer"&gt;Getting Started guide.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vue.js&lt;/strong&gt;&lt;br&gt;
For server-side rendering with Vue.js, you can use the Nuxt.js framework. Nuxt.js is a versatile framework that offers various features, such as automatic code splitting, static site generation, and a development server with hot module replacement.&lt;/p&gt;

&lt;p&gt;To start using Nuxt.js, check out the official &lt;a href="https://nuxtjs.org/docs/get-started/installation/" rel="noopener noreferrer"&gt;Getting Started guide.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular&lt;/strong&gt;&lt;br&gt;
Angular Universal is the official solution for server-side rendering in Angular applications. It allows you to pre-render your Angular application on the server and includes support for both Node.js and .NET Core.&lt;/p&gt;

&lt;p&gt;To implement SSR in an Angular app, follow the official &lt;a href="https://angular.io/guide/universal" rel="noopener noreferrer"&gt;Angular Universal guide.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Server-side rendering is an essential technique for modern web developers, as it helps improve performance, search engine optimisation, and social sharing capabilities. By leveraging popular frameworks such as Next.js, Nuxt.js, and Angular Universal, you can quickly and easily implement server-side rendering in your applications.&lt;/p&gt;

&lt;p&gt;So, take the time to explore these frameworks and give your users the fast and efficient browsing experience they deserve!&lt;/p&gt;

&lt;p&gt;Happy coding!🧑🏽‍💻&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>react</category>
    </item>
  </channel>
</rss>
