<?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.us-east-2.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>Building a Cricket Trivia Game Was Easy. Normalising 7,000+ Players Was Hard.</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sun, 21 Jun 2026 14:41:03 +0000</pubDate>
      <link>https://dev.to/j3rry320/building-a-cricket-trivia-game-was-easy-normalising-7000-players-was-hard-c0e</link>
      <guid>https://dev.to/j3rry320/building-a-cricket-trivia-game-was-easy-normalising-7000-players-was-hard-c0e</guid>
      <description>&lt;p&gt;When I started building &lt;a href="https://codemedialabs.in/almanac/stumped" rel="noopener noreferrer"&gt;Stumped!&lt;/a&gt;, a cricketer guessing game, I thought the hard part would be coming up with clever clues.&lt;/p&gt;

&lt;p&gt;I was wrong.&lt;/p&gt;

&lt;p&gt;The real hard part was turning thousands of raw, ball-by-ball cricket scorecards into clean, human-readable player profiles.&lt;/p&gt;

&lt;p&gt;Here is how a simple trivia game helped me learn more about normalising data.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6aqque9r18ghg0e9okr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6aqque9r18ghg0e9okr.webp" alt="Stumped! The Almanac Project By Code Media Labs" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dream vs. The Reality
&lt;/h2&gt;

&lt;p&gt;I wanted to generate rich, dynamic clues for players, like:&lt;/p&gt;

&lt;p&gt;"This batter scored 573 runs in the death overs at a strike rate of 135.8."&lt;/p&gt;

&lt;p&gt;To do that, I turned to the amazing open datasets at &lt;a href="https://cricsheet.org" rel="noopener noreferrer"&gt;Cricsheet&lt;/a&gt;. They provide incredible ball-by-ball archives. But there's a catch: raw match data and game-ready player profiles speak entirely different languages.&lt;/p&gt;

&lt;p&gt;Cricsheet tells you what happened on delivery 4.2 of Match X. It does not tell you a player's career stats. Everything had to be derived from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Turning Matches into Careers
&lt;/h2&gt;

&lt;p&gt;Step one was flipping the data architecture from match-centric to player-centric. I built a pipeline that ingests every single delivery and progressively updates a player’s lifetime accumulator object:&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="nx"&gt;players&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="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;matches&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bat_runs&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bat_balls&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bowl_runs&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bowl_wickets&lt;/span&gt;&lt;span class="dl"&gt;"&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="c1"&gt;// ...you get the idea&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of querying a massive database of matches every time a user plays, we build the careers once beforehand.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd0mmo9ahyvcpt3z3nm0w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd0mmo9ahyvcpt3z3nm0w.png" alt="Stumped! The Almanac Project" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The "Who TF is V Kohli?" Problem
&lt;/h2&gt;

&lt;p&gt;Then came the initials. Cricket scorecards love abbreviations.&lt;/p&gt;

&lt;p&gt;EJG Morgan&lt;/p&gt;

&lt;p&gt;RG Sharma&lt;/p&gt;

&lt;p&gt;V Kohli&lt;/p&gt;

&lt;p&gt;Humans see "V Kohli" and know it's Virat. Computers see it and shrug. Worse, multiple players share the same initials.&lt;/p&gt;

&lt;p&gt;I tried scraping external sports sites to map these messy strings to unique humans, but between rate limits, anti-bot shields, and wildly inconsistent formatting, the scrapers failed hard. Right now, I am back to the drawing board, trying to figure out how to reliably enrich these player profiles without losing my sanity.&lt;/p&gt;

&lt;p&gt;(Pro tip: This is exactly why Stumped asks users to guess surnames. Surnames are way more reliable than ambiguous initials.)&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzlchbu72l47ajt88pjin.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzlchbu72l47ajt88pjin.png" alt="Stumped! The Almanac Project" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Extracting the Spicy Stats
&lt;/h2&gt;

&lt;p&gt;Basic career averages are boring trivia. To make the game fun, I needed to slice and dice the data into highly specific cricket archetypes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Batting Phases&lt;/strong&gt;: Grouping deliveries into Powerplays (overs 0–5), Middle (6–15), and Death (16–20) to find the clutch finishers. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Psychology of the Chase&lt;/strong&gt;: Tracking performance when setting a target vs. chasing one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nemesis vs Favourite Bowler&lt;/strong&gt;: Who dismisses this batter the most? Who do they absolutely smash for fun? (Rule #1 of the pipeline: Your nemesis cannot also be your favourite victim. The logic got messy here, but it made the clues feel remarkably human.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Weird Stuff&lt;/strong&gt;: Tracking golden ducks, diamond ducks, maiden overs, and dot-ball percentages.&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7e9iwyqp3l902jfj9a5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7e9iwyqp3l902jfj9a5.png" alt="Stumped! The Almanac Project by Code Media Labs" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Flattening the Monster
&lt;/h2&gt;

&lt;p&gt;Deeply nested JSON objects are a pain to consume on the frontend. The final step of the pipeline takes all those complex, deep career structures and flattens them into a clean, single-level profile:&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;"bat_runs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bat_average"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;29.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bat_strike_rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;136.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fielding_catches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nemesis_bowler_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;"Hardik Pandya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"favorite_bowler_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;"Umar Gul"&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;Now, generating a clue is as simple as reading a single key-value pair.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowlp25ay1pthx5mydedn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowlp25ay1pthx5mydedn.png" alt="Stumped! The Almanac Project " width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson Learned
&lt;/h2&gt;

&lt;p&gt;Building the game took a fraction of the time it took to clean records and handle weird edge cases. Under the hood, a single clue represents thousands of rows of raw match data processed into something actually readable.&lt;br&gt;
If I started this project again, I'd invest in the name normalisation layer first, before writing/generating a single line of stat-aggregation code.&lt;/p&gt;

&lt;p&gt;We just launched the game! If you want to check out this and other fun side projects we’ve been hacking on, take a look at  &lt;a href="https://codemedialabs.in/almanac" rel="noopener noreferrer"&gt;The Almanac Project&lt;/a&gt; or test your cricket trivia knowledge directly at &lt;a href="https://codemedialabs.in/almanac/stumped" rel="noopener noreferrer"&gt;Stumped!&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And as always Happy Coding! &lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>The Ultimate Next.js Cheat Sheet for Beginners (2026 Edition)</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Thu, 11 Jun 2026 09:25:29 +0000</pubDate>
      <link>https://dev.to/j3rry320/the-ultimate-nextjs-cheat-sheet-for-beginners-2026-edition-3omf</link>
      <guid>https://dev.to/j3rry320/the-ultimate-nextjs-cheat-sheet-for-beginners-2026-edition-3omf</guid>
      <description>&lt;p&gt;If you're learning React in 2026, you've probably heard one piece of advice repeatedly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use Next.js."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But when you first open a Next.js project, it can feel overwhelming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's the &lt;code&gt;app&lt;/code&gt; folder?&lt;/li&gt;
&lt;li&gt;Why are some components running on the server?&lt;/li&gt;
&lt;li&gt;When should I use &lt;code&gt;"use client"&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Do I still need API routes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide answers those questions and gives you a practical cheat sheet you can bookmark and revisit whenever you're building a Next.js application.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Next.js?
&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%2Fwqryd9m9opaewzrzsvjf.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%2Fwqryd9m9opaewzrzsvjf.png" alt="Next.js The turbocharged React framework" width="225" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next.js is a React framework that helps you build fast, production-ready web applications.&lt;/p&gt;

&lt;p&gt;It comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File-based routing&lt;/li&gt;
&lt;li&gt;Server-side rendering&lt;/li&gt;
&lt;li&gt;Static site generation&lt;/li&gt;
&lt;li&gt;API routes&lt;/li&gt;
&lt;li&gt;Image optimization&lt;/li&gt;
&lt;li&gt;SEO support&lt;/li&gt;
&lt;li&gt;Server Components&lt;/li&gt;
&lt;li&gt;Full-stack capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as React + everything you need to build and deploy a modern application.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR: Next.js Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create App&lt;/td&gt;
&lt;td&gt;Create a new Next.js project&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx create-next-app@latest my-app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route&lt;/td&gt;
&lt;td&gt;Create a page&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;app/about/page.tsx&lt;/code&gt; → &lt;code&gt;/about&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic Route&lt;/td&gt;
&lt;td&gt;Create URL parameters&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/blog/[slug]/page.tsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layout&lt;/td&gt;
&lt;td&gt;Shared UI between pages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/layout.tsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server Component&lt;/td&gt;
&lt;td&gt;Default component type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;async function Page()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client Component&lt;/td&gt;
&lt;td&gt;Enables state &amp;amp; events&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"use client"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Link between pages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Link href="/about" /&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Router&lt;/td&gt;
&lt;td&gt;Navigate programmatically&lt;/td&gt;
&lt;td&gt;&lt;code&gt;router.push("/dashboard")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search Params&lt;/td&gt;
&lt;td&gt;Read URL query values&lt;/td&gt;
&lt;td&gt;&lt;code&gt;useSearchParams()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata&lt;/td&gt;
&lt;td&gt;SEO configuration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export const metadata = {}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Fetching&lt;/td&gt;
&lt;td&gt;Fetch server-side data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;await fetch(url)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revalidation&lt;/td&gt;
&lt;td&gt;Refresh cached data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;revalidate: 3600&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No Cache&lt;/td&gt;
&lt;td&gt;Always fetch fresh data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache: "no-store"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server Actions&lt;/td&gt;
&lt;td&gt;Handle forms on the server&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"use server"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loading UI&lt;/td&gt;
&lt;td&gt;Show loading states&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/loading.tsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error UI&lt;/td&gt;
&lt;td&gt;Handle route errors&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/error.tsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not Found&lt;/td&gt;
&lt;td&gt;Custom 404 page&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/not-found.tsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Route&lt;/td&gt;
&lt;td&gt;Create backend endpoints&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/api/users/route.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;Run code before requests&lt;/td&gt;
&lt;td&gt;&lt;code&gt;middleware.ts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;td&gt;Optimized image loading&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;Built-in font optimization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;next/font/google&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment Variables&lt;/td&gt;
&lt;td&gt;Store secrets/config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;process.env&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static Rendering&lt;/td&gt;
&lt;td&gt;Pre-render pages&lt;/td&gt;
&lt;td&gt;Default caching behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic Rendering&lt;/td&gt;
&lt;td&gt;Render on every request&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache: "no-store"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production Build&lt;/td&gt;
&lt;td&gt;Create deployable build&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy&lt;/td&gt;
&lt;td&gt;Deploy instantly&lt;/td&gt;
&lt;td&gt;Vercel, Docker, AWS, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Golden Rule
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need...&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State (&lt;code&gt;useState&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Client Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Handlers&lt;/td&gt;
&lt;td&gt;Client Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser APIs&lt;/td&gt;
&lt;td&gt;Client Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database Access&lt;/td&gt;
&lt;td&gt;Server Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Calls&lt;/td&gt;
&lt;td&gt;Server Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Better SEO&lt;/td&gt;
&lt;td&gt;Server Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smaller Bundle Size&lt;/td&gt;
&lt;td&gt;Server Component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Submission&lt;/td&gt;
&lt;td&gt;Server Action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared Navigation&lt;/td&gt;
&lt;td&gt;Layout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic URLs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[slug]&lt;/code&gt; Routes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Folder Structure Cheat Sheet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── layout.tsx       # Shared layout
├── page.tsx         # Home page
├── loading.tsx      # Loading UI
├── error.tsx        # Error UI
├── not-found.tsx    # 404 page
├── api/
│   └── users/
│       └── route.ts
└── blog/
    └── [slug]/
        └── page.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 5 Things Every Beginner Should Memorise
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Every folder becomes a route.&lt;/li&gt;
&lt;li&gt;Components are Server Components by default.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;"use client"&lt;/code&gt; only when needed.&lt;/li&gt;
&lt;li&gt;Fetch data directly inside Server Components.&lt;/li&gt;
&lt;li&gt;Use Server Actions for forms instead of creating unnecessary API routes.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Create a New Next.js Project
&lt;/h2&gt;

&lt;p&gt;Create a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest my-app &lt;span class="nt"&gt;--typescript&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move into the project:&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="nb"&gt;cd &lt;/span&gt;my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Understanding the App Router
&lt;/h2&gt;

&lt;p&gt;Modern Next.js applications use the &lt;strong&gt;App Router&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A typical project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── layout.tsx
├── page.tsx
├── loading.tsx
├── error.tsx
├── not-found.tsx
└── dashboard/
    └── page.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every folder becomes a route automatically.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/page.tsx
→ /

app/about/page.tsx
→ /about

app/dashboard/page.tsx
→ /dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No routing configuration needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Pages vs Layouts
&lt;/h2&gt;

&lt;p&gt;A page represents a route.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&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="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;p&gt;A layout wraps multiple pages.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&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="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&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="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="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&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;p&gt;Layouts persist between page navigations, making them perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigation bars&lt;/li&gt;
&lt;li&gt;Sidebars&lt;/li&gt;
&lt;li&gt;Footers&lt;/li&gt;
&lt;li&gt;Shared UI&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Dynamic Routes
&lt;/h2&gt;

&lt;p&gt;Need a blog post page?&lt;/p&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/blog/[slug]/page.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/blog/nextjs-cheatsheet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access the slug:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;params&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="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="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;slug&lt;/span&gt; &lt;span class="p"&gt;}&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;params&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="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;
  
  
  5. Server Components (Default)
&lt;/h2&gt;

&lt;p&gt;One of the biggest changes in modern Next.js is that components are &lt;strong&gt;Server Components by default&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&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;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; users found&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&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;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller JavaScript bundles&lt;/li&gt;
&lt;li&gt;Better performance&lt;/li&gt;
&lt;li&gt;Better SEO&lt;/li&gt;
&lt;li&gt;Secure access to databases and APIs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Client Components
&lt;/h2&gt;

&lt;p&gt;Need state, event handlers, or browser APIs?&lt;/p&gt;

&lt;p&gt;Use:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;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="s2"&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="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&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="mi"&gt;0&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="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;p&gt;Use Client Components when you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Event handlers&lt;/li&gt;
&lt;li&gt;Browser APIs&lt;/li&gt;
&lt;li&gt;Custom hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise, stay with Server Components.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Navigation
&lt;/h2&gt;

&lt;p&gt;Link between pages:&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;Link&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/link&lt;/span&gt;&lt;span class="dl"&gt;"&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;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  About
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Programmatic navigation:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;useRouter&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;next/navigation&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;back&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Search Parameters
&lt;/h2&gt;

&lt;p&gt;URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/products?page=2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the value:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;useSearchParams&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;next/navigation&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&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;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Metadata and SEO
&lt;/h2&gt;

&lt;p&gt;Static metadata:&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&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;next&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;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Website&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Built with Next.js&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dynamic metadata:&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateMetadata&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="na"&gt;title&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 Post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically generates SEO-friendly meta tags.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Data Fetching
&lt;/h2&gt;

&lt;p&gt;Simple fetch:&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="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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cache for one hour:&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;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always fetch fresh data:&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;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-store&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next.js has built-in caching and revalidation, which helps improve performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Server Actions
&lt;/h2&gt;

&lt;p&gt;Server Actions let you run server-side code directly from forms without creating API endpoints.&lt;/p&gt;

&lt;p&gt;Create a Server Action:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// save to database&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it inside a form:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createUser&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Create User&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is now the recommended approach for many form submissions in the App Router.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Loading States
&lt;/h2&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/loading.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Loading&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="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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;p&gt;Next.js automatically displays this while data is loading.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Error Handling
&lt;/h2&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/error.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;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;reset&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="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;reset&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="k"&gt;void&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="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="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Try Again
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;
  
  
  14. API Routes
&lt;/h2&gt;

&lt;p&gt;Need an API?&lt;/p&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/api/users/route.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GET example:&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&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="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="na"&gt;success&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;POST example:&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&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;body&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;request&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="k"&gt;return&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="nx"&gt;body&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;
  
  
  15. Image Optimization
&lt;/h2&gt;

&lt;p&gt;Use the built-in Image 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;Image&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/image&lt;/span&gt;&lt;span class="dl"&gt;"&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;Image&lt;/span&gt;
  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/hero.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Hero"&lt;/span&gt;
  &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Lazy loading&lt;/li&gt;
&lt;li&gt;Responsive images&lt;/li&gt;
&lt;li&gt;Better performance&lt;/li&gt;
&lt;li&gt;Automatic optimization&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  16. Fonts
&lt;/h2&gt;

&lt;p&gt;Install fonts directly from Google Fonts:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Inter&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;next/font/google&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;inter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Inter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;latin&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply globally:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  17. Environment Variables
&lt;/h2&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=
NEXT_PUBLIC_API_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server only:&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Available on the client:&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_API_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rule of thumb:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the browser needs access to it, prefix it with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  18. Production Build
&lt;/h2&gt;

&lt;p&gt;Build your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run production mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check for production errors before deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  19. When Should I Use a Server Component?
&lt;/h2&gt;

&lt;p&gt;Use a Server Component when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching data&lt;/li&gt;
&lt;li&gt;Querying databases&lt;/li&gt;
&lt;li&gt;Accessing secrets&lt;/li&gt;
&lt;li&gt;Rendering static content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Products&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;products&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;getProducts&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&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;
  
  
  20. When Should I Use a Client Component?
&lt;/h2&gt;

&lt;p&gt;Use a Client Component when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using state&lt;/li&gt;
&lt;li&gt;Using effects&lt;/li&gt;
&lt;li&gt;Handling clicks&lt;/li&gt;
&lt;li&gt;Accessing browser APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;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="s2"&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="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LikeButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLikes&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="mi"&gt;0&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="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;onClick&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLikes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;likes&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="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;likes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;When you're starting with Next.js, don't try to learn everything at once.&lt;/p&gt;

&lt;p&gt;Focus on these four concepts first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Server Components&lt;/li&gt;
&lt;li&gt;Client Components&lt;/li&gt;
&lt;li&gt;Data Fetching&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once those topics click, the rest of the framework starts making sense.&lt;/p&gt;

&lt;p&gt;Bookmark this cheat sheet, keep building projects, and you'll be productive with Next.js much faster than you think.&lt;/p&gt;

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

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>frontend</category>
      <category>fullstack</category>
    </item>
    <item>
      <title>Building an Immersive Tourism Experience with Next.js and Framer Motion</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Mon, 08 Jun 2026 10:51:49 +0000</pubDate>
      <link>https://dev.to/j3rry320/building-an-immersive-tourism-experience-with-nextjs-and-framer-motion-310i</link>
      <guid>https://dev.to/j3rry320/building-an-immersive-tourism-experience-with-nextjs-and-framer-motion-310i</guid>
      <description>&lt;p&gt;Most tourism websites feel like digital filing cabinets: you click a button, read a fact, and move to the next page. But what if a website felt less like a directory and more like a story?&lt;/p&gt;

&lt;p&gt;Recently, while designing a concept for Odisha Tourism at &lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt;, We wanted to move away from static layouts and toward scrollytelling. In this experience, the user travels through landscapes, culture, and history simply by scrolling.&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%2Flzz4w15dj38ah588a9f5.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%2Flzz4w15dj38ah588a9f5.png" alt="Reimagining Odisha Tourism" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how to build this experience using Next.js and Framer Motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. The Concept: Think in Chapters&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of listing attractions, break your site into narrative chapters. Think of each section as a scene in a movie. Each chapter should have a distinct mood that changes as the user moves through it, using background images and large typography to define the "vibe" of that location.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Use Scroll Progress as Your "Main Switch"&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In Framer Motion, we use useScroll() to track exactly where the user is on the page. Imagine your entire webpage as a long slider from 0 (top) to 1 (bottom).&lt;/p&gt;

&lt;p&gt;You can "map" this scroll position to any animation property using useTransform(). Here is a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This tells the code: when scroll is 0 to 0.5, &lt;/span&gt;
&lt;span class="c1"&gt;// the opacity goes from 0 (invisible) to 1 (visible).&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollYProgress&lt;/span&gt;&lt;span class="p"&gt;,&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="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;1&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="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&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="nx"&gt;opacity&lt;/span&gt; &lt;span class="p"&gt;}&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to the Wilderness&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&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;
  
  
  &lt;strong&gt;3. Cinematic Motion Effects&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You don't need complex code to make a site feel professional. Focus on these three simple Framer Motion techniques:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Parallax (Creating Depth)&lt;/strong&gt;&lt;br&gt;
Move your background images slightly slower than the scroll speed to create a 3D effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Moves the image up by 200px as the user scrolls&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollYProgress&lt;/span&gt;&lt;span class="p"&gt;,&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&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="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/nature.jpg"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;B. Scaling Headlines&lt;/strong&gt;&lt;br&gt;
Make text feel responsive by tying its size to the scroll.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Headline grows from 90% size to 100% as you reach the section&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollYProgress&lt;/span&gt;&lt;span class="p"&gt;,&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="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;style&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="nx"&gt;scale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Into The Untamed&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;4. Performance: Keep it Smooth&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Because these sites are image-heavy, they can easily become laggy. Keep the experience snappy with these habits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animate "Transforms" only&lt;/strong&gt;: Avoid changing width, height, or padding during scroll. Instead, use transform (for moving/scaling) and opacity. These are much faster because they don't force the browser to recalculate the page layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy Loading&lt;/strong&gt;: Don't load high-resolution images for the "Coastline" section if the user is still at the "Dawn" chapter. Use Next.js Image component with loading="lazy".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Share Values&lt;/strong&gt;: If five elements need to fade in at once, create a single opacity variable and pass it to all of them, rather than calculating it five times.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Story Over Tech&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The biggest lesson?&lt;/em&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Motion doesn't create immersion—storytelling does.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Framer Motion and Next.js are just the tools. If the layout is cluttered, no amount of fancy animation will save it. Keep your text minimal, let your visuals carry the emotional weight, and ensure every animation serves the narrative, not just the "cool" factor.&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%2Fq81y0kas65gcsfrvk4al.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%2Fq81y0kas65gcsfrvk4al.png" alt="Dawn" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7um0p6840qk39ib8aiw.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%2Fc7um0p6840qk39ib8aiw.png" alt="Jungle" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpa7zlhlo6x6u4z51ti7.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%2Flpa7zlhlo6x6u4z51ti7.png" alt="Culture" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Takeaway&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Websites don't have to behave like documents. They can behave like experiences. By combining simple, performant animations with a clear narrative, you can transform a standard travel site into a journey that inspires people.&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%2Fbevy6ag9kyz899aucg9z.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%2Fbevy6ag9kyz899aucg9z.png" alt="Reviews" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What kind of story are you planning to tell on your next project?&lt;/p&gt;

&lt;p&gt;You can find the &lt;a href="https://codemedialabs.in/demo/odisha-tourism" rel="noopener noreferrer"&gt;live demo here&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>webdev</category>
      <category>design</category>
      <category>react</category>
      <category>web</category>
    </item>
    <item>
      <title>Government Websites Don't Have to Look Like They're Stuck in 2010</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sun, 31 May 2026 12:56:22 +0000</pubDate>
      <link>https://dev.to/j3rry320/government-websites-dont-have-to-look-like-theyre-stuck-in-2010-n7n</link>
      <guid>https://dev.to/j3rry320/government-websites-dont-have-to-look-like-theyre-stuck-in-2010-n7n</guid>
      <description>&lt;p&gt;This afternoon, I took on a personal redesign challenge: reimagining a government tender portal using modern web design principles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal wasn't to criticise the existing platform. Government systems often prioritise functionality, compliance, and long-term stability. However, many public-facing portals still struggle with usability, discoverability, and visual clarity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I asked a simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if a government procurement platform were designed with the same care as modern SaaS products?&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%2Ftzgo1jb3uy2jtc730202.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%2Ftzgo1jb3uy2jtc730202.png" alt="The Current Platform" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;p&gt;Instead of treating the portal like an administrative dashboard, I redesigned it as a discovery-first experience.&lt;/p&gt;

&lt;p&gt;The new interface focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Clear visual hierarchy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Faster access to opportunities&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mobile-friendly navigation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modern search and filtering experiences&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved accessibility and readability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trust-building through transparency and data visualisation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And out came &lt;a href="https://codemedialabs.in/demo/tender-portal" rel="noopener noreferrer"&gt;this&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz8718bwi89wqwhn90ld.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%2Ftz8718bwi89wqwhn90ld.png" alt="Our Redesign" width="419" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reimagined Hero Section
&lt;/h3&gt;

&lt;p&gt;The homepage now focuses on helping users discover opportunities rather than overwhelming them with navigation and dense information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern Analytics Dashboard
&lt;/h3&gt;

&lt;p&gt;Procurement activity, tender trends, and vendor participation are presented through clean data visualisations instead of traditional tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opportunity-Centric Design
&lt;/h3&gt;

&lt;p&gt;Featured tenders and procurement categories are showcased using modern card layouts that make important information easier to scan and understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistent Design System
&lt;/h3&gt;

&lt;p&gt;Every section follows the same visual language, spacing system, typography scale, and component patterns to create a more cohesive experience.&lt;/p&gt;

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

&lt;p&gt;The redesign was built using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Next.js&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TypeScript&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tailwind CSS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recharts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lucide Icons&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Digital public infrastructure impacts millions of people.&lt;/p&gt;

&lt;p&gt;When government websites are easier to use, citizens spend less time searching for information, vendors can participate more easily in procurement processes, and organisations benefit from increased transparency and engagement.&lt;/p&gt;

&lt;p&gt;Modern UX isn't just about aesthetics. It's about reducing friction.&lt;/p&gt;

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

&lt;p&gt;This project is a concept redesign exploring how public-sector digital experiences can evolve using modern design and development practices.&lt;/p&gt;

&lt;p&gt;Government websites don't need to look flashy.&lt;/p&gt;

&lt;p&gt;They need to be clear, accessible, trustworthy, and pleasant to use.&lt;/p&gt;

&lt;p&gt;And with today's tools and design systems, that's more achievable than ever.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://codemedialabs.in/demo/tender-portal" rel="noopener noreferrer"&gt;demo!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as always, Happy Coding &amp;amp; Designing!&lt;/p&gt;




&lt;p&gt;Designed and developed as a concept by Code Media Labs.&lt;/p&gt;

</description>
      <category>design</category>
      <category>ui</category>
      <category>webdev</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>I Built an AI-Powered Interview Simulator That Runs Entirely in Your Terminal</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Fri, 29 May 2026 15:59:55 +0000</pubDate>
      <link>https://dev.to/j3rry320/i-built-an-ai-powered-interview-simulator-that-runs-entirely-in-your-terminal-nmp</link>
      <guid>https://dev.to/j3rry320/i-built-an-ai-powered-interview-simulator-that-runs-entirely-in-your-terminal-nmp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Interview prep is mostly passive.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You read through question lists, watch videos, or practice with a friend if you can find one. None of it feels particularly real.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://www.npmjs.com/package/interview-coach" rel="noopener noreferrer"&gt;&lt;strong&gt;Interview Coach&lt;/strong&gt;&lt;/a&gt;, a CLI tool that runs mock interviews, scores your answers, and gives you a breakdown of what you did well and where you fell short.&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%2Fj0u5l432hm9gy1oceid8.png" alt=" " width="800" height="800"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;interview-coach start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick a role, an experience level, and how many questions you want. The interview starts immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;★ Question 1/5

Category: React
Difficulty: Medium

How would you optimise a React application experiencing unnecessary re-renders?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After each answer, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A score&lt;/li&gt;
&lt;li&gt;A verdict&lt;/li&gt;
&lt;li&gt;Specific feedback&lt;/li&gt;
&lt;li&gt;What a strong answer would have looked like&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end, a full session report is generated.&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%2Fwnnw6h814pzs2hdep5jv.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%2Fwnnw6h814pzs2hdep5jv.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  It's not just for developers
&lt;/h2&gt;

&lt;p&gt;The questions adapt to the role you choose. A few examples:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content Writer&lt;/strong&gt; — &lt;em&gt;How would you rewrite a technical article for a non-technical audience?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sales Executive&lt;/strong&gt; — &lt;em&gt;A prospect says your product is too expensive. How would you respond?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teacher&lt;/strong&gt; — &lt;em&gt;How would you explain a difficult concept to students with different learning styles?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Same tool, any profession.&lt;/p&gt;




&lt;h2&gt;
  
  
  Under the hood
&lt;/h2&gt;

&lt;p&gt;The stack is intentionally small: Node.js, Commander, Inquirer, Groq, Zod, Chalk, Ora, Boxen, Webpack.&lt;/p&gt;

&lt;p&gt;Sessions are saved locally, so you can review past reports and track how you're improving over time. Nothing leaves your machine except the requests to generate questions and evaluate answers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&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; &lt;span class="nt"&gt;-g&lt;/span&gt; interview-coach
&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;npx interview-coach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/interview-coach" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/interview-coach&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/J3rry320/interview-coach" rel="noopener noreferrer"&gt;https://github.com/J3rry320/interview-coach&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback, bug reports, and contributions are welcome.&lt;br&gt;
And as always, happy coding!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>node</category>
      <category>npm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Reimagined the Cuttack Municipal Corporation Website with Next.js, Tailwind &amp; Recharts</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Thu, 28 May 2026 07:58:45 +0000</pubDate>
      <link>https://dev.to/j3rry320/i-reimagined-the-cuttack-municipal-corporation-website-with-nextjs-tailwind-recharts-gng</link>
      <guid>https://dev.to/j3rry320/i-reimagined-the-cuttack-municipal-corporation-website-with-nextjs-tailwind-recharts-gng</guid>
      <description>&lt;h2&gt;
  
  
  Redesigning a Municipal Website: CMC Cuttack
&lt;/h2&gt;

&lt;p&gt;Government websites are some of the most important digital products people use. People rely on them for taxes, certificates, complaints, licenses, public notices, and emergency information.&lt;/p&gt;

&lt;p&gt;But many municipal portals across India still feel stuck in the early 2010s, with cluttered layouts, poor mobile usability, overwhelming menus, low accessibility, and information overload.&lt;/p&gt;

&lt;p&gt;So I decided to redesign one.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Existing Website
&lt;/h2&gt;

&lt;p&gt;I picked the official Cuttack Municipal Corporation website: &lt;a href="https://cmccuttack.odisha.gov.in/" rel="noopener noreferrer"&gt;cmccuttack.odisha.gov.in&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4v0g6mefyuzewmd5m1yv.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%2F4v0g6mefyuzewmd5m1yv.png" alt="Existing Website" width="800" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CMC already offers several important citizen services, including property tax, trade license, grievance redressal, water and sewerage, tenders and notices. The functionality exists. But the overall experience still feels information-heavy, visually dense, and inconsistent on mobile, especially for non-technical users.&lt;/p&gt;

&lt;p&gt;And honestly, this is not just a Cuttack problem. Most government websites across India still prioritise listing information over designing usable digital experiences.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;The idea was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if a municipal website felt more like a civic operating system instead of a static government portal?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted it to feel alive, responsive, transparent, and approachable, with modern layouts, a citizen-first hierarchy, and real-time data visibility. Instead of treating the site like a document archive, I wanted it to feel like a living platform.&lt;/p&gt;

&lt;p&gt;You can explore the live demo here: &lt;a href="https://www.codemedialabs.in/demo/cmc" rel="noopener noreferrer"&gt;codemedialabs.in/demo/cmc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuiih8tcmihk50u9p35vn.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%2Fuiih8tcmihk50u9p35vn.png" alt="Our Redesign" width="800" height="820"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;The concept is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; — for the app framework and routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; — for the utility-first styling system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lucide React&lt;/strong&gt; — for the icon set throughout the UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recharts&lt;/strong&gt; — for all the data visualisation components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to keep the frontend lightweight, scalable, and production-ready, with a heavy focus on spacing systems, typography hierarchy, responsive behaviour, and accessibility-friendly layouts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Design Direction
&lt;/h2&gt;

&lt;p&gt;One thing I was careful about: not making this feel like a crypto dashboard, an admin panel template, or a startup SaaS clone. Government platforms need a different visual language. They should feel trustworthy, calm, readable, and authoritative.&lt;/p&gt;

&lt;p&gt;So the direction became: &lt;strong&gt;Apple-inspired minimalism + smart city dashboards + civic accessibility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The UI uses large typography, clean spacing, modular cards, muted gradients, and structured information blocks — without becoming visually overwhelming.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  Navbar
&lt;/h3&gt;

&lt;p&gt;The navigation is sticky with a clean white backdrop and subtle blur. On desktop, it shows links for Services, Ward Info, Tenders, Dashboard, and About, alongside Sign In and Pay Online CTAs. On mobile, it collapses into a smooth CSS-animated drawer that locks scroll and closes on outside click.&lt;/p&gt;




&lt;h3&gt;
  
  
  Hero Section
&lt;/h3&gt;

&lt;p&gt;The original CMC portal immediately overwhelms users with dense navigation and competing content. The redesigned hero focuses on three things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A search bar&lt;/strong&gt; — the primary action. Large, prominent, with a blue search button, and popular service tags below it (Property Tax, Trade License, Water Bill, Building Approval), so users don't have to think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Actions card&lt;/strong&gt; — a dark blue panel on the right with six instant-access buttons: Property Tax, Register Complaint, Water Bill, Trade License, Track Application, and Certificates. Each has an icon and lifts slightly on hover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A stats strip&lt;/strong&gt; — anchored below the hero fold, spanning four key numbers: 2.67L+ Citizens Served, 12,458 Complaints Resolved, 95% Water Coverage, and 312 MT Waste Collected Today. Small but impactful — it immediately communicates scale and activity.&lt;/p&gt;




&lt;h3&gt;
  
  
  Citizen Services
&lt;/h3&gt;

&lt;p&gt;Four service cards arranged in a responsive grid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Property Tax&lt;/strong&gt; — pay online quickly and securely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grievance Redressal&lt;/strong&gt; — register and track civic complaints with real-time updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Water &amp;amp; Sewerage&lt;/strong&gt; — apply for new connections and manage utility bills&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building Approval&lt;/strong&gt; — submit and track building plan approval requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each card has a colour-coded icon, a description, and an "Explore Service" link that animates on hover. Clean, scannable, no clutter.&lt;/p&gt;




&lt;h3&gt;
  
  
  Dashboard Preview
&lt;/h3&gt;

&lt;p&gt;This section does two things in a split layout:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Civic Performance chart&lt;/strong&gt; — a Recharts area chart showing service efficiency across six dimensions: Sanitation, Water, Transport, Emergency, Waste Management, and Citizen Services. It has a "LIVE" badge and a styled tooltip with a glossy card. The gradient fill under the line gives it a premium feel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notices &amp;amp; Updates panel&lt;/strong&gt; — a dark blue card with the three most recent notices, each tagged by type (NOTICE, TENDER, NEWS) with colour-coded badges and dates. A "View All" link sits at the top.&lt;/p&gt;




&lt;h3&gt;
  
  
  Know Your Ward + City Highlight
&lt;/h3&gt;

&lt;p&gt;A two-column section with two very different jobs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know Your Ward&lt;/strong&gt; — a dark blue card with a search input for ward number or property ID. Below it are four quick-access buttons: Ward Councillor, Garbage Schedule, Water Supply, and Flood Alerts. This is the hyperlocal civic layer that most government sites completely miss — especially relevant for Cuttack during monsoon seasons and waterlogging situations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cuttack — The Silver City&lt;/strong&gt; — an immersive photo panel with a gradient overlay, showcasing the city's identity. The copy leans into Cuttack's 1000-year heritage, silver filigree craftsmanship, and the Mahanadi river. Four identity tags: Millennium City, Silver Filigree, Mahanadi River, Smart City. Two CTAs: Explore Cuttack and City Dashboard.&lt;/p&gt;




&lt;h3&gt;
  
  
  Governance Intelligence
&lt;/h3&gt;

&lt;p&gt;A full section built around making civic operations visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Four KPI cards at the top:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avg Response Time: 2.4 hrs (down 18%)&lt;/li&gt;
&lt;li&gt;Ward Satisfaction: 91% (up 6%)&lt;/li&gt;
&lt;li&gt;Digital Applications: 18.2K (up 22%)&lt;/li&gt;
&lt;li&gt;Infrastructure Health: 88% (up 9%)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ward Analytics&lt;/strong&gt; — a Recharts RadarChart comparing five wards (W-12, W-18, W-24, W-31, W-42) across three dimensions: Sanitation, Water supply, and Grievance resolution. It gives an immediate sense of which wards are performing and which need attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure Monitoring&lt;/strong&gt; — a dark card with five animated progress bars showing active status for Street Lights (94%), Water Pumps (88%), Drain Sensors (72%), Traffic Signals (91%), and CCTV Network (86%). Below that, three stat blocks: 128 Flood Sensors, 42 Air Quality Nodes, 67 Smart Signals.&lt;/p&gt;




&lt;h3&gt;
  
  
  Live City Monitoring
&lt;/h3&gt;

&lt;p&gt;This was the most technically interesting part of the project.&lt;/p&gt;

&lt;p&gt;A live SVG map of Cuttack with ten ward nodes plotted across a grid background with a stylised Mahanadi river path. Each ward is colour-coded by risk level — green for Normal, amber for Watch, red for Critical — with a halo effect around each node. Hovering a ward shows a tooltip with its drainage percentage, traffic load, and flood risk.&lt;/p&gt;

&lt;p&gt;Above the map, five metric cards update every 2.2 seconds with randomised values simulating live telemetry: Drainage Health, Flood Risk, Field Teams, Traffic Load, and New Complaints. The numbers fluctuate in realistic ranges, so the dashboard feels genuinely alive.&lt;/p&gt;

&lt;p&gt;Below the map, ten ward cards display the static data for each area — Buxi Bazaar, Choudhury Bazaar, Ranihat, College Square, Mangalabag, CDA Sector 6, Badambadi, Jobra, Cantonment, and Nimpur — each with a pulsing dot in its risk colour.&lt;/p&gt;




&lt;h3&gt;
  
  
  Smart Civic Analytics
&lt;/h3&gt;

&lt;p&gt;Three charts that transform raw data into transparency:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grievance Resolution&lt;/strong&gt; — a Recharts BarChart comparing resolved vs. pending complaints from January to May. Dark blue bars for resolved, light blue for pending. The trend is obvious at a glance: resolve rates are climbing, backlogs are shrinking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Budget Allocation&lt;/strong&gt; — a doughnut PieChart showing FY 2025–26 sectoral spending: Roads (35%), Water (25%), Sanitation (20%), Parks (10%), Health (10%). Simple and honest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Water Supply Stability&lt;/strong&gt; — a full-width dark blue LineChart spanning the whole week (Monday to Sunday), showing daily uptime percentages across all wards. Weekly average: 95.6%, shown in a floating stat card above the chart.&lt;/p&gt;




&lt;h3&gt;
  
  
  Leadership
&lt;/h3&gt;

&lt;p&gt;Two profile cards for the Mayor (Shri Subhash Chandra Singh) and the Commissioner (Ms Kirandeep Kaur Sahota, IAS). Each has a photo, role badge, a short bio, and two stat blocks — Active Projects and Civic Drives for the Mayor; Smart Projects and Digital Services for the Commissioner. The cards have a subtle glow that expands on hover.&lt;/p&gt;




&lt;h3&gt;
  
  
  Footer
&lt;/h3&gt;

&lt;p&gt;A full dark footer (&lt;code&gt;#07152d&lt;/code&gt;) with a top CTA bar, a four-column grid (brand + stats, Quick Links, Important Links, Contact), and a bottom bar with copyright and legal links. The contact section shows the CMC address on Link Road, Cuttack, the 1800 345 6767 helpline, and &lt;a href="mailto:cmc@cuttack.gov.in"&gt;cmc@cuttack.gov.in&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Design Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Designing for Trust
&lt;/h3&gt;

&lt;p&gt;This was the most interesting constraint. Civic platforms cannot feel gimmicky, startup-ish, or trend-chasing. The UI needs to balance authority, simplicity, and professionalism while still feeling modern. That balance is much harder than designing a typical SaaS dashboard — and it shaped almost every decision, from the navy palette to the conservative use of animation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Balancing Information Density
&lt;/h3&gt;

&lt;p&gt;Government websites contain large amounts of information by nature. The challenge is not removing it — it's structuring it properly: prioritising actions, improving discoverability, reducing cognitive load through spacing, grouping, and visual rhythm.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Tells Us
&lt;/h2&gt;

&lt;p&gt;Government websites are public infrastructure. Bad UX in civic systems creates confusion, accessibility barriers, lower digital adoption, and reduced trust. When millions of citizens interact with a system, even small usability improvements matter enormously.&lt;/p&gt;

&lt;p&gt;This redesign is an exploration of what that future could look like, a municipal platform that feels transparent, fast, mobile-friendly, and genuinely citizen-first.&lt;/p&gt;

&lt;p&gt;Especially as more public services move online across India, this kind of design thinking deserves to be part of the conversation.&lt;/p&gt;

&lt;p&gt;If you're working on a government platform, civic tech product, or any digital experience that needs to go beyond the template, we'd love to help build it. At Code Media Labs, we design and develop production-ready systems that are fast, accessible, and actually enjoyable to use. Whether you're starting from scratch or modernising something long overdue for a rethink, reach out at &lt;a href="https://codemedialabs.in/contact" rel="noopener noreferrer"&gt;codemedialabs.in&lt;/a&gt;and let's build something worth using.&lt;/p&gt;

&lt;p&gt;And as always, happy coding.&lt;/p&gt;




&lt;p&gt;Built by &lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt;. Live demo: &lt;a href="https://www.codemedialabs.in/demo/cmc" rel="noopener noreferrer"&gt;codemedialabs.in/demo/cmc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>uidesign</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Announcing markdown-parser-react v3.0.0: A Complete Architectural Overhaul</title>
      <dc:creator>Jerry Satpathy</dc:creator>
      <pubDate>Sun, 12 Apr 2026 07:57:51 +0000</pubDate>
      <link>https://dev.to/j3rry320/announcing-markdown-parser-react-v300-a-complete-architectural-overhaul-p2o</link>
      <guid>https://dev.to/j3rry320/announcing-markdown-parser-react-v300-a-complete-architectural-overhaul-p2o</guid>
      <description>&lt;p&gt;Markdown is the backbone of the modern web—from documentation to personal blogs. But for too long, React developers have had to choose between "lightweight but insecure" or "robust but heavy."&lt;/p&gt;

&lt;p&gt;Today, I’m thrilled to announce the launch of markdown-parser-react v3.0.0. This is a ground-up rewrite designed for the security-conscious, performance-driven developer.&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%2Fcw11i0fgw5glu14v8vuk.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%2Fcw11i0fgw5glu14v8vuk.png" alt="Markdown-parser-react v3.0.0" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an Overhaul?
&lt;/h2&gt;

&lt;p&gt;The Core Reason was the Move to Token-Based Rendering&lt;br&gt;
In previous versions, we relied on standard HTML rendering. In v3, we’ve moved entirely away from dangerouslySetInnerHTML.&lt;/p&gt;

&lt;p&gt;The core of the library is now a &lt;strong&gt;Token-based React Renderer&lt;/strong&gt;. Instead of generating a raw HTML string, we parse Markdown into an AST (Abstract Syntax Tree) and convert those tokens directly into React nodes using React.createElement.&lt;/p&gt;
&lt;h2&gt;
  
  
  What does this mean for you?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Security by Design&lt;/strong&gt;: It is inherently protected against most XSS attacks because we aren't injecting raw strings into the DOM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: It integrates perfectly with the React Lifecycle and Virtual DOM, ensuring smoother updates and flawless hydration in SSR environments like Next.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Everything is a Component" Philosophy&lt;/strong&gt;: Because every part of your markdown is now a React node, you can treat it like any other part of your app.&lt;/p&gt;
&lt;h2&gt;
  
  
  What’s New in v3?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Advanced Component Overrides (The Killer Feature)&lt;/strong&gt;&lt;br&gt;
This is where v3 truly shines. You can now swap any standard Markdown element for a custom React component.&lt;/p&gt;

&lt;p&gt;Want to replace a standard &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag with&lt;code&gt;next/link&lt;/code&gt; for client-side navigation? Or maybe turn &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tags into beautifully styled Shadcn/UI headers? Now you can:&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="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="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;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CustomHeader&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CustomHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
      &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;InternalLink&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MySyntaxHighlighter&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MySyntaxHighlighter&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;span class="si"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. GitHub-Style Alerts &amp;amp; Native Footnotes&lt;/strong&gt;&lt;br&gt;
We’ve added native support for blockquote callouts ([!NOTE], [!TIP], etc.) and common markdown footnotes ([^1]). This makes the library perfect for academic writing, technical docs, or citation-heavy blogs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Advanced Math (TeX/LaTeX)&lt;/strong&gt;&lt;br&gt;
Math is no longer an afterthought. With support for inline ($) and block ($$) equations, you can use the onRenderMath hook to integrate engines like KaTeX or MathJax effortlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Semantic DX (Developer Experience)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WCAG Compliant&lt;/strong&gt;: New asArticle, id, and aria props ensure your rendered content is accessible. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strictly Typed&lt;/strong&gt;: Full TypeScript support for a better IDE experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure Defaults&lt;/strong&gt;: sanitizeHtml is now enabled by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Vision: Code Media Labs
&lt;/h2&gt;

&lt;p&gt;With v3, markdown-parser-react officially transitions under the &lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt; branding. Our mission is to build lightweight, high-performance tools that solve real-world problems for the modern web and open-source those tools for everyone to use.&lt;/p&gt;

&lt;p&gt;This package is a core component of the ecosystem we are building. By making markdown rendering safe and extensible, we’re setting the stage for more complex systems.&lt;/p&gt;

&lt;p&gt;What's Next?&lt;br&gt;
Our focus at &lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt; is currently split between infrastructure and intelligence:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://synapse.codemedialabs.in" rel="noopener noreferrer"&gt;Synapse&lt;/a&gt;: Our upcoming "Intelligent Factory Operating System." We are applying the same high-performance standards used in this library to build a robust ERP platform for industrial environments.&lt;/p&gt;

&lt;p&gt;Mira: Our internal intelligence engine.** Mira isn't just about sentiment analysis; it's a multimodal suite**. It combines high-speed demographic analytics with a custom TTS (Text-to-Speech) system built on top of Piper. Whether it's parsing public sentiment or giving that data a voice, Mira is designed for speed and privacy.&lt;/p&gt;

&lt;p&gt;Get Started&lt;br&gt;
If you’re looking for a markdown solution that doesn’t compromise on security or React-native features, give v3 a spin.&lt;/p&gt;

&lt;p&gt;Install it now:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the docs:&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/markdown-parser-react" rel="noopener noreferrer"&gt;Markdown-Parser-React&lt;/a&gt;&lt;br&gt;
I’d love to hear your feedback! Let’s build a better, safer web together. And as always, Happy Coding&lt;/p&gt;

&lt;p&gt;Follow me for more updates on Next.js, TypeScript, and our journey at &lt;a href="https://codemedialabs.in" rel="noopener noreferrer"&gt;Code Media Labs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>npm</category>
    </item>
    <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="799" 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>
  </channel>
</rss>
