<?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: Kushal S T</title>
    <description>The latest articles on DEV Community by Kushal S T (@kushalst).</description>
    <link>https://dev.to/kushalst</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F607520%2Fdbed15a5-f742-4dee-889f-fbf5350f8584.jpeg</url>
      <title>DEV Community: Kushal S T</title>
      <link>https://dev.to/kushalst</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kushalst"/>
    <language>en</language>
    <item>
      <title>I Built a Wrapper to Fix the Two Biggest Problems with LocalStorage</title>
      <dc:creator>Kushal S T</dc:creator>
      <pubDate>Sat, 27 Dec 2025 16:19:29 +0000</pubDate>
      <link>https://dev.to/kushalst/i-built-a-wrapper-to-fix-the-two-biggest-problems-with-localstorage-keh</link>
      <guid>https://dev.to/kushalst/i-built-a-wrapper-to-fix-the-two-biggest-problems-with-localstorage-keh</guid>
      <description>&lt;p&gt;We all love &lt;code&gt;localStorage&lt;/code&gt;. It is the easiest way to persist state in a web app. You don't need a database, you don't need a backend, and the API is dead simple: &lt;code&gt;setItem&lt;/code&gt; and &lt;code&gt;getItem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But if you’ve built enough production apps, you know &lt;code&gt;localStorage&lt;/code&gt; has two major flaws that eventually come back to bite you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It’s Immortal: Data stays there forever. Unless you manually write code to delete it, your user’s disk fills up with stale data from three years ago.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It’s Exposed: Open the DevTools "Application" tab, and anyone can read (and edit) your application state in plain text.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I built a lightweight, zero-dependency TypeScript package to solve exactly these problems.&lt;/p&gt;

&lt;p&gt;Meet &lt;a href="https://www.npmjs.com/package/@kushalst/local-storage-expiry" rel="noopener noreferrer"&gt;@kushalst/local-storage-expiry&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with "Forever" Storage
&lt;/h2&gt;

&lt;p&gt;Imagine you cache a user’s profile to save an API call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Standard localStorage
localStorage.setItem('user', JSON.stringify({ name: 'Kushal', role: 'admin' }));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user’s role changes to &lt;code&gt;editor&lt;/code&gt; on the server, but your frontend logic prioritizes &lt;code&gt;localStorage&lt;/code&gt;, the user might see the old admin dashboard for months—until they clear their cache manually.&lt;/p&gt;

&lt;p&gt;To fix this, you usually have to write messy wrapper code to save a timestamp, check it on every read, and handle parsing errors. It’s boilerplate city.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Built-in TTL (Time-To-Live)
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;@kushalst/local-storage-expiry&lt;/code&gt;, expiration is a first-class citizen. You set a duration (in milliseconds), and the library handles the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @kushalst/local-storage-expiry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;The API is designed to feel exactly like the native one, just smarter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { set, get } from "@kushalst/local-storage-expiry";

// Cache this data for exactly 1 hour
set("userProfile", { id: 123, name: "Kushal" }, 60 * 60 * 1000);

// Later...
const profile = get("userProfile"); 
// Returns data if &amp;lt; 1 hour old.
// Returns null (and auto-deletes) if &amp;gt; 1 hour old.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the data is expired, the library automatically cleans it up for you immediately upon access. No more stale UI bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Highlight: Obfuscation
&lt;/h2&gt;

&lt;p&gt;This is where standard wrappers usually stop, but I wanted something better.&lt;/p&gt;

&lt;p&gt;By default, &lt;code&gt;localStorage&lt;/code&gt; saves plain strings. If you walk away from your computer, anyone can open DevTools and see exactly what is stored.&lt;/p&gt;

&lt;p&gt;This library automatically &lt;strong&gt;obfuscates&lt;/strong&gt; your data using a lightweight &lt;strong&gt;XOR + Base64&lt;/strong&gt; algorithm.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before (Standard): &lt;code&gt;"user": "{\"id\":123,\"name\":\"Kushal\"}"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;After (With this library): &lt;code&gt;"lse_user": "eyJ2IjoxLCJlIjoxNzM1M... (gibberish) ..."&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is this military-grade encryption? &lt;strong&gt;No&lt;/strong&gt;. (Never store passwords in localStorage!). But it prevents casual snooping and stops users from manually tampering with specific JSON fields to break your app state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Highlight: The Garbage Collector
&lt;/h2&gt;

&lt;p&gt;"Lazy deletion" (deleting when you try to access the key) is great, but what about keys you never access again? They usually sit there, eating up your 5MB storage quota.&lt;/p&gt;

&lt;p&gt;I included a &lt;code&gt;flushExpired()&lt;/code&gt; utility that scans your storage and bulk-deletes dead keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { flushExpired } from "@kushalst/local-storage-expiry";

// Run this on app startup (e.g., in useEffect or main.ts)
flushExpired();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures your app keeps a small footprint on the user's device, automatically removing junk from previous sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety First: Namespacing
&lt;/h2&gt;

&lt;p&gt;You might be worried: &lt;em&gt;"Will &lt;code&gt;flushExpired()&lt;/code&gt; delete data from other libraries or my other apps?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No. The library automatically prefixes all keys with lse_&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set('token') -&amp;gt; saves to lse_token&lt;/li&gt;
&lt;li&gt;clear() -&amp;gt; only wipes keys starting with lse_&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your other localStorage data is completely safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;If you are looking for a robust, type-safe way to handle client-side caching without pulling in massive dependencies, give this a try. It works in React, Vue, Angular, Svelte, and even vanilla JS.&lt;/p&gt;

&lt;p&gt;📦 NPM: &lt;a href="//npmjs.com/package/@kushalst/local-storage-expiry"&gt;npmjs.com/package/@kushalst/local-storage-expiry&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>performance</category>
    </item>
    <item>
      <title>Guide to Mandatory Screen Resolutions for UI Testing with Google Chrome Developer Tools</title>
      <dc:creator>Kushal S T</dc:creator>
      <pubDate>Tue, 21 Mar 2023 17:44:20 +0000</pubDate>
      <link>https://dev.to/kushalst/guide-to-mandatory-screen-resolutions-for-ui-testing-with-google-chrome-developer-tools-5fl6</link>
      <guid>https://dev.to/kushalst/guide-to-mandatory-screen-resolutions-for-ui-testing-with-google-chrome-developer-tools-5fl6</guid>
      <description>&lt;p&gt;As a web developer, testing your UI on various screen sizes and resolutions is crucial. With Google Chrome Developer Tools, you can easily emulate different devices without owning them physically. In this post, we will list the mandatory screen resolutions required for UI testing using Chrome Developer Tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, buckle up and let's dive in! Here are some of the popular resolutions you should add in your test suite
&lt;/h2&gt;

&lt;p&gt;Desktop Resolutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1920x1080&lt;/strong&gt; - The classic resolution for most desktops. If your website looks good on this, you're halfway there!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2560x1440&lt;/strong&gt; - For those with high-end monitors or who need to see the bigger picture :P&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3840x2160&lt;/strong&gt; - For the ones who like to see things in 4K, but also need a microscope to read the text. lol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1280x720&lt;/strong&gt; - For the users with potato PCs, because someone's gotta test the low-end, right?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mobile Resolutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1080x2400&lt;/strong&gt; - The resolution of the latest Android flagship devices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1080x2340&lt;/strong&gt; - The resolution of the budget-friendly Android devices that flood the market.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2556×1179&lt;/strong&gt; - Resolution of iPhone 14 daddy, the iPhone 14 Pro &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;720x1600&lt;/strong&gt; - The resolution of low-end Android devices that are mostly used for playing Snake.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tablet Resolutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2048x2732&lt;/strong&gt; - The resolution of the iPad Pro, for those who like to carry their desktop on the go.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1280x800&lt;/strong&gt; - The resolution of most budget Android tablets that come with the promise of "unbeatable performance".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2224x1668&lt;/strong&gt; - The resolution of the iPad Air, for those who need a slightly bigger canvas to work on.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bonus Resolution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;300x600&lt;/strong&gt; - For when you want to test your website on a banner ad, because let's face it, we've all been there.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Steps to add these resolutions in the Google Chrome Developer Tools - Emulated Devices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open Google Chrome and navigate to the website you want to test. (Probably your localhost)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the three dots at the top right corner of the browser to open the menu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on "More Tools" and then "Developer Tools" or use the keyboard shortcut "Ctrl + Shift + I" (Windows) or "Cmd + Option + I" (Mac).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Developer Tools window, click on the device icon at the top left corner of the window (it looks like a mobile device and a tablet).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the device you want to emulate from the dropdown list or click on "Add Custom Device" if your desired device is not listed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter the mandatory screen resolution in the appropriate fields. Make sure to enter the correct pixel density and aspect ratio values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on "Add" to add the new custom device to the list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the device is added, you can select it from the dropdown list and test your website on the added mandatory resolution.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iPGWhbrr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anjyep1yzf35b625q1yr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iPGWhbrr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anjyep1yzf35b625q1yr.png" alt="Image description" width="880" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In conclusion, testing your UI on various screen sizes and resolutions is critical,  With these mandatory resolutions, you'll be well-equipped to ensure that your website looks good on any device. &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tutorial</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Setting up Mac for Web Development</title>
      <dc:creator>Kushal S T</dc:creator>
      <pubDate>Sat, 19 Jun 2021 15:21:51 +0000</pubDate>
      <link>https://dev.to/kushalst/setting-up-mac-for-web-development-3l5g</link>
      <guid>https://dev.to/kushalst/setting-up-mac-for-web-development-3l5g</guid>
      <description>&lt;p&gt;You'll need to install the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Homebrew - Package manager for OS X. You'll be able to get it here &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Brew Site&lt;/a&gt; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the following commands: &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;brew update&lt;/code&gt; - Updates &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;brew cask install iterm2&lt;/code&gt; - &lt;a href="https://iterm2.com/" rel="noopener noreferrer"&gt;Iterm2&lt;/a&gt; is macOS terminal replacement. Looks beautiful and is customizable &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;brew install git&lt;/code&gt; - Well, you know why! &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;brew install --cask rectangle&lt;/code&gt; - Run this if you are going to need multiple windows opened at the same time. This &lt;a href="https://rectangleapp.com/" rel="noopener noreferrer"&gt;tool&lt;/a&gt; helps in moving and resizing windows in macOS using keyboard shortcuts or snap areas &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;brew install --cask alfred&lt;/code&gt; - This a replacement for spotlight. You can customize and make &lt;a href="https://www.alfredapp.com/" rel="noopener noreferrer"&gt;Alfred&lt;/a&gt; do a lot of things. Take a look at their site and you'll be blown away. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you are going to need a friendly shell to help you in development. Install &lt;strong&gt;fish&lt;/strong&gt;. It stands for &lt;em&gt;friendly interactive shell&lt;/em&gt;. To install this, run &lt;code&gt;brew install fish&lt;/code&gt; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now change your default shell to &lt;strong&gt;fish&lt;/strong&gt;. To do this, add the line  &lt;code&gt;/usr/local/bin/fish&lt;/code&gt;  to  &lt;code&gt;/etc/shells&lt;/code&gt; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change your default shell with  &lt;code&gt;chsh  -s  /usr/local/bin/fish&lt;/code&gt; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fish ships with the &lt;strong&gt;fish_config&lt;/strong&gt; command, which launches a graphical user interface (GUI) where you can choose different colors and prompt configurations &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopensource.com%2Fsites%2Fdefault%2Ffiles%2Fuploads%2Ffish-colors.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopensource.com%2Fsites%2Fdefault%2Ffiles%2Fuploads%2Ffish-colors.png" alt="UI after running fish_config command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now you'll need a editor to code. Download VS Code from &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open VS Code and enable code prompt by pressing Command + Shift + P or F1 then type &lt;code&gt;Shell&lt;/code&gt; in command palette now you are able to find this option like &lt;code&gt;Shell Command : Install code in PATH&lt;/code&gt; from suggested list in command palette. Select that option. This will enable you to open VS Code directly from terminal &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.stack.imgur.com%2FNg886.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.stack.imgur.com%2FNg886.png" alt="VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Next you need a good font. Install font &lt;strong&gt;Fira Code&lt;/strong&gt;. You'll find instructions &lt;a href="https://github.com/tonsky/FiraCode" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This font contains a set of ligatures for common programming multi-character combinations. In simple words, multi-character symbols look more legible &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After you install this font, update it in VS Code and in Iterm2 to make everything look prettier &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VS Code extensions: Install these to make your life easier. &lt;a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens" rel="noopener noreferrer"&gt;&lt;strong&gt;GitLens&lt;/strong&gt;&lt;/a&gt;, &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets" rel="noopener noreferrer"&gt;JavaScript Code Snippets&lt;/a&gt;&lt;/strong&gt;,  &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt;&lt;/strong&gt; , &lt;a href="https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock" rel="noopener noreferrer"&gt;&lt;strong&gt;Peacock&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight" rel="noopener noreferrer"&gt;&lt;strong&gt;TODO Highlight&lt;/strong&gt;&lt;/a&gt;, &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons" rel="noopener noreferrer"&gt;VS Code Icons&lt;/a&gt;&lt;/strong&gt;.  There are more but you might get overwhelmed. Start with these and eventually fellow developers will suggest you more extensions &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic setup is done. Now you check if you have &lt;a href="https://www.google.com/intl/en_in/chrome/" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; / &lt;a href="https://www.mozilla.org/en-US/firefox/new/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt; installed. What's the point of web development when there's no browser? 😝 &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now run &lt;code&gt;brew install node&lt;/code&gt; to install node.js &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phew! that was a long list. Now, install your favourite framework and start developing 👾👾👾&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>macbook</category>
      <category>settingup</category>
      <category>basics</category>
    </item>
    <item>
      <title>Discord notifications on Google form submission</title>
      <dc:creator>Kushal S T</dc:creator>
      <pubDate>Sat, 03 Apr 2021 15:17:18 +0000</pubDate>
      <link>https://dev.to/kushalst/discord-notifications-on-google-form-submission-1837</link>
      <guid>https://dev.to/kushalst/discord-notifications-on-google-form-submission-1837</guid>
      <description>&lt;h3&gt;
  
  
  Step #1
&lt;/h3&gt;

&lt;p&gt;Get discord webhook. &lt;a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks"&gt;https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks&lt;/a&gt; will help you understand how to get a webhook for your channel in Discord.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #2
&lt;/h3&gt;

&lt;p&gt;Go to the Google form with which you want to integrate Discord. Click on the 3 dot menu on the top right and then click on &lt;strong&gt;Script Editor&lt;/strong&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8spaYbJd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gul9e067genqedx41clh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8spaYbJd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gul9e067genqedx41clh.png" alt="Screenshot 2021-04-03 at 8.14.42 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #3
&lt;/h3&gt;

&lt;p&gt;Code...&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;var&lt;/span&gt; &lt;span class="nx"&gt;POST_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_DISCORD_WEBHOOK_HERE&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="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;discordPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Form Submitted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;embeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rich&lt;/span&gt;&lt;span class="dl"&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;Form Entry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;307506&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItemResponses&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;None&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&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;discordPayload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeds&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;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="nx"&gt;discordPayload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeds&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;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&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;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;POST_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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discordPayload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step #4
&lt;/h3&gt;

&lt;p&gt;Click on &lt;strong&gt;Edit -&amp;gt; Current project's triggers&lt;/strong&gt; in Script Editor.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--66ctuq5p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d6288al783eolkbkegz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--66ctuq5p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d6288al783eolkbkegz4.png" alt="Screenshot 2021-04-03 at 8.19.01 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #5
&lt;/h3&gt;

&lt;p&gt;Create a new trigger for &lt;code&gt;onSubmit&lt;/code&gt; event&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ofvo4xj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1jewj2upl7gunqceazcf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ofvo4xj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1jewj2upl7gunqceazcf.png" alt="Screenshot 2021-04-03 at 8.23.25 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #6
&lt;/h3&gt;

&lt;p&gt;There is no step 6! Go ahead and submit the form and test it. you'll see something like this in your Discord channel 👾 👾 👾&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oaufmny1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zapm5pjjina3ycdsy7ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oaufmny1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zapm5pjjina3ycdsy7ex.png" alt="Screenshot 2021-04-03 at 8.37.59 PM"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discord</category>
      <category>googleforms</category>
      <category>integration</category>
      <category>webhook</category>
    </item>
  </channel>
</rss>
