<?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: TK Vishal</title>
    <description>The latest articles on DEV Community by TK Vishal (@tkvishal).</description>
    <link>https://dev.to/tkvishal</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%2F476814%2F08c979ac-38e5-4630-9298-b217070ee77c.jpeg</url>
      <title>DEV Community: TK Vishal</title>
      <link>https://dev.to/tkvishal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tkvishal"/>
    <language>en</language>
    <item>
      <title>Wrighter (β) - A Powerful Markdown Blogger &amp; A Writing Companion ⚡</title>
      <dc:creator>TK Vishal</dc:creator>
      <pubDate>Fri, 02 Sep 2022 10:45:18 +0000</pubDate>
      <link>https://dev.to/tkvishal/introducing-wrighter-a-powerful-markdown-blogger-a-writing-companion-26gl</link>
      <guid>https://dev.to/tkvishal/introducing-wrighter-a-powerful-markdown-blogger-a-writing-companion-26gl</guid>
      <description>&lt;h3&gt;
  
  
  Open &lt;a href="https://wrighter.vercel.app/"&gt;Wrighter&lt;/a&gt; 🚀
&lt;/h3&gt;

&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;I have used almost all of the markdown editors out there, they were lacking the most important features that &lt;strong&gt;&lt;em&gt;I&lt;/em&gt;&lt;/strong&gt; needed the most.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I wanted a distraction-free markdown editor &lt;strong&gt;on the web&lt;/strong&gt; that is usable in any form factor. Most markdown editors are either &lt;em&gt;too cluttered or too minimal(hard to find things)&lt;/em&gt;. There are only two extremes. I wanted an app that sits in the middle, it should be distraction-free while also having the ability to easily provide all of the application's functionality with a few key taps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A markdown editor that isn't just a textbox, it should be reactive to the content that you are typing and context-sensitive. The markdown syntax can disturb your flow while you are reading in the editor.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IAnoqRkU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/HA1ztKC.jpg%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IAnoqRkU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/HA1ztKC.jpg%2520align%3D%2522center%2522" alt="ideas can come up anywhere at anytime, be sure to capture them" width="600" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Ideas can come up anywhere at any time!&lt;/em&gt; You should be able to capture and organize the chaos. There should be very less friction between turning your ideas into articles that you are writing seamlessly. The process of &lt;code&gt;gathering/organizing ideas -&amp;gt; write -&amp;gt; share it to the world&lt;/code&gt; should all be on the same platform, so there is less friction between the &lt;em&gt;ideation phase&lt;/em&gt; and writing. These ideas and blogs would be organized in a way that is easily accessible and searchable, which could essentially act as a second brain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is still a lot of friction around ideation &amp;amp; writing blogs on the web &amp;amp; publishing them. &lt;em&gt;Wrighter has the greater aim to solve them!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  About Wrighter
&lt;/h1&gt;

&lt;p&gt;Wrighter is THE full package! It consists of a &lt;strong&gt;powerful &lt;a href="https://en.wikipedia.org/wiki/WYSIWYM"&gt;WYSIWYM&lt;/a&gt; (what you see is what you mean) markdown editor&lt;/strong&gt; supporting GFM and KaTeX. It has proven to be the best way to edit markdown!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sdl5RE0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/eeUt7Uw.gif%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sdl5RE0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/eeUt7Uw.gif%2520align%3D%2522center%2522" alt="the wrighter editor" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wrighter allows you to write the best way and publish the fastest way with &lt;em&gt;configurable SEO&lt;/em&gt; settings. Wrighter also has the ability to gather and organize ideas in any form factor quickly(they are called &lt;em&gt;bites&lt;/em&gt;), which you can later use in your blogs while writing them.&lt;/p&gt;

&lt;p&gt;Wrighter is built upon a tagging system that can be used in both your blogs and also your ideas. Never forget where you saved an idea or a blog, just open up the tag tree and search for keywords!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SsOwLRAt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/kZeWNuf.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SsOwLRAt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/kZeWNuf.png%2520align%3D%2522center%2522" alt="product banner - apple style" width="880" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also includes a super smart context-based Command Bar, Invoke it with &lt;strong&gt;⌘/Ctrl + Shift + P&lt;/strong&gt;. The command bar basically provides you with suggestions based on the current page/context. You can go mouse free with the command bar, with all your wrighter functionalities being just a few keystrokes away!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p5GLP3il--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/yRtgITM.gif%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p5GLP3il--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/yRtgITM.gif%2520align%3D%2522center%2522" alt="blazingly fast command bar" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The best and my favorite feature of all? It is optional signup! You can get all of wrighter's features(except publishing) without even giving away your personal details. All your data will be there as long as you use the same browser. In fact, you can just open up &lt;a href="https://wrighter.vercel.app/new"&gt;this link&lt;/a&gt; and start &lt;em&gt;wrighting&lt;/em&gt; right now!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wz4W2e6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/afw2wCw.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wz4W2e6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/afw2wCw.png%2520align%3D%2522center%2522" alt="Offline and Online Mode" width="880" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another amazing feature is that you can copy from anywhere on the internet. when you paste it in wrighter, it automagically converts it to markdown, which happened to be a &lt;em&gt;huuuuuuge&lt;/em&gt; time saver.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;...and even more features that I will explain in detail in each section ⤵️&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Making of WYSIWYM Markdown Editor
&lt;/h1&gt;

&lt;p&gt;The wrighter editor is built on top of &lt;a href="https://codemirror.net/"&gt;codemirror&lt;/a&gt; and &lt;a href="https://bytemd.netlify.app/"&gt;bytemd&lt;/a&gt;. codemirror is the go-to choice when it comes to flexible/hackable text editing and bytemd provides a nice wrapper for codemirror using react with some extra functionalities. I wanted to create a fork of bytemd that includes all the WYSIWYM features that I built for wrighter, but it was out of scope and takes too much time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ulTEs4NP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/FeCo0ZP.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ulTEs4NP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/FeCo0ZP.png%2520align%3D%2522center%2522" alt="difference between WYSIWYG vs WYSIWYM" width="522" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wrighter uses some clever techniques to make sure that markdown semantics and syntax are muted while the content takes the most focus. It also pushes the heading syntax far left outside of the editor so that the &lt;em&gt;headings look like actual headings&lt;/em&gt;, it also indents the lists to the right, giving some &lt;em&gt;degree of separation&lt;/em&gt; between different content types while typing. It comes with a focus mode so that you can focus on the content alone, hiding all of the editor buttons and gizmos.&lt;/p&gt;

&lt;p&gt;It supports almost all of the common text editor shortcuts that you use every day. If you forget any of it, just launch the command bar(⌘/Ctrl + Shift + P) and search for whatever you want. The editor autosaves the entire context to the browser's IndexedDB regularly so you don't have to worry about data loss. If you are logged in, it autosaves with the &lt;strong&gt;Planetscale DB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rD_XG31f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/egPKOvb.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rD_XG31f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/egPKOvb.png%2520align%3D%2522center%2522" alt="how wrighter editor manages autosaves" width="880" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;under the hood, the markdown is parsed by the &lt;a href="https://unifiedjs.com/"&gt;unified&lt;/a&gt; remark and rehype processors, which in turn under the hood manipulate the markdown + HTML as an &lt;a href="https://www.twilio.com/blog/abstract-syntax-trees"&gt;AST&lt;/a&gt;, which gives a lot of flexibility on parsing and rendering markdown. The editor uses them as plugins, which allows me to pick the features and inject them into the editor, one such injectable feature is the custom-made &lt;em&gt;"copy from anywhere &amp;amp; paste as markdown"&lt;/em&gt; feature.&lt;/p&gt;

&lt;p&gt;The editor and the markdown renderer are used in multiple parts of wrighter with selective features removed/added all with the help of the flexible plugin system.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Wrights aka Your Blogs
&lt;/h1&gt;

&lt;p&gt;I wanted to create consistent branding in wrighter, hence the name &lt;em&gt;wrights/wrightups&lt;/em&gt;. wrights are just labeled synonyms for the &lt;em&gt;writeups/blogs&lt;/em&gt; that you write. You can create a wright by just clicking the &lt;strong&gt;Create Wright&lt;/strong&gt; button or by using the command bar or by visiting &lt;a href="https://wrighter.vercel.app/new"&gt;https://wrighter.vercel.app/new&lt;/a&gt;. I wanted the initial onboarding to be as seamless as possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0PoQoOBQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/cPyS8Uu.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0PoQoOBQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/cPyS8Uu.png%2520align%3D%2522center%2522" alt="wrights homepage" width="880" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No signups, no button clicks, no BS. Just visit the URL and you are ready to jot down your thoughts. This is definitely inspired by Google's way of onboarding/creating new docs using a &lt;a href="https://doc.new/"&gt;quick link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are a logged-in user, you would have the option to keep your wright private or public visibility. You can also modify SEO settings like &lt;a href="https://yoast.com/slug/"&gt;slugs&lt;/a&gt; and &lt;a href="https://yoast.com/image-seo/"&gt;meta image&lt;/a&gt; that would show up when you share your link on social media. Other SEO metadata is managed by wrighter to provide your wright the best search engine visibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_K4SJYwK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/9Z9mb5R.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_K4SJYwK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/9Z9mb5R.png%2520align%3D%2522center%2522" alt="wright settings" width="646" height="781"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The published wrights make use of vercel's server-side rendering capabilities. This means that after the initial load, your wright is SEO compatible and loads almost instantly!&lt;/p&gt;

&lt;h1&gt;
  
  
  The Bites aka Your Ideas
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x7ZZ5yIh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/o7XRRh2.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x7ZZ5yIh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/o7XRRh2.png%2520align%3D%2522center%2522" alt="ideas can come anywhere, at any time" width="880" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm not a "great" writer, but I have a lot of ideas. Some good ones and many dumb ones. I might have missed at least a dozen of good ideas because I didn't note them down/I lost the chain of thoughts. That's exactly why I made &lt;em&gt;Bites&lt;/em&gt;. They are used to jot down ideas of any form factor.&lt;/p&gt;

&lt;p&gt;Is it a cool link that you found on the internet? or is it an awesome reference image that might be useful later? or a code snippet? or just a miscellaneous blob of text? &lt;em&gt;bites&lt;/em&gt; has got you covered! You can add tags to the bites to organize and filter them accordingly. The bites page has a calendar and range date pickers which you can use to go up/down memory lane.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nFoZ0o3b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/bUm3Wfr.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nFoZ0o3b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/bUm3Wfr.png%2520align%3D%2522center%2522" alt="your bites page" width="880" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To reduce the friction in creating bites, the command bar provides a way to create a bite at any place at any time within wrighter. You can also create one by using the shortcut &lt;code&gt;c + b&lt;/code&gt;. This means that you can create a bite while writing your blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the use of Bites?
&lt;/h2&gt;

&lt;p&gt;The major use case is that it helps you to "bite" down on small ideas that might pop up. The other awesome use case is that you can use the bites in the blogs/wrights that you are writing. Launch the command bar and select the bite to attach and the contents of the bite just appends to wherever the cursor is preset.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Attaching bites in wrights is one of my favorite features because, When I think of ideas or see a cool website while on my mobile. I just save it as a bite on mobile. While writing the blog, I'll just search and attach the stuff I had saved on my mobile. This magically improved the &lt;code&gt;ideation -&amp;gt; writing&lt;/code&gt; phase.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QE7_Nr78--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/z9Yp70o.gif%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QE7_Nr78--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/z9Yp70o.gif%2520align%3D%2522center%2522" alt="Quickly add your ideas into wrights" width="600" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A more narrowed-down use case would be &lt;em&gt;reference image gathering for artists&lt;/em&gt;. Digital artists usually have a huge local folder called &lt;code&gt;ref&lt;/code&gt; or &lt;code&gt;inspiration&lt;/code&gt;. This becomes very hard to manage. Bites would provide the ability to tag/filter/search any way they prefer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KnXE0NCR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/c5f34Js.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KnXE0NCR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/c5f34Js.png%2520align%3D%2522center%2522" alt="create bite modal" width="844" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There were some generic use cases too. The other day, I found some cool wallpapers for my new PC while on my mobile and found some on my personal laptop. I just made them as bites and tagged them with &lt;code&gt;#wallpaper&lt;/code&gt;. As soon as I got home, I filtered the bites page with the tags, and boom! all of my collections in my PC.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Since the bites can be of any form factor, you can also collect code snippets/KaTeX formulas as markdown with full syntax highlighting,  which you can later use in your wrights while writing!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RXHALaie--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/PWn0E74.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RXHALaie--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/PWn0E74.png%2520align%3D%2522center%2522" alt="using bites to collect code snippets" width="590" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Tags
&lt;/h1&gt;

&lt;p&gt;All your content(wrights &amp;amp; bites) is tagged via a central tagging system over which you have full control. You can open up the &lt;strong&gt;tag tree&lt;/strong&gt; to view/search all of the tags. You can also view all the wrights &amp;amp; bites under a specific tag. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HrYOAKrY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://imgur.com/UmGAcS5.gif%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HrYOAKrY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://imgur.com/UmGAcS5.gif%2520align%3D%2522center%2522" alt="the tagtree" width="600" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can bulk detach and delete a tag right from the tag tree itself! These tags also serve as SEO keywords for published wrights, so make sure you tag them with relevant keywords when publishing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Other Features
&lt;/h1&gt;

&lt;p&gt;There are still a lot of features all over wrighter, this blog would grow huge if I had to cover them in a single blog...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ability to export your wright in markdown, whenever you want. This might come in handy if you are going to use wrighter as your markdown editor and other platforms like &lt;em&gt;hashnode/devto&lt;/em&gt; as your blogging platform.&lt;/li&gt;
&lt;li&gt;Full mobile compatibility!&lt;/li&gt;
&lt;li&gt;write in one tab and preview in another tab.&lt;/li&gt;
&lt;li&gt;Dark and Light themes all over wrighter.&lt;/li&gt;
&lt;li&gt;copy from anywhere and paste as markdown. Wrighter recognizes and parses almost all of the common semantic content from the web ranging from text styles and images to code and HTML tables.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r71jmNp0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://imgur.com/wc76WBl.gif%2520align%3D%2522center%2522" alt="copy from StackOverflow with markdown syntax" width="800" height="454"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.github.com/gfm/"&gt;GFM&lt;/a&gt; and &lt;a href="https://katex.org/"&gt;KaTeX&lt;/a&gt;(very similar to LaTeX) suppport on both the wright editor and bites.&lt;/li&gt;
&lt;li&gt;Instant switch from offline to online modes and vice-versa.&lt;/li&gt;
&lt;li&gt;SEO controls for wrights like editing URL slugs and the OG image for the wright.&lt;/li&gt;
&lt;li&gt;extremely clean and minimal UI with simplistic UX and maximum functionality ~&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Technical Challenges
&lt;/h1&gt;

&lt;p&gt;Working on this project has been a huge learning experience for sure. &lt;strong&gt;PlanetScale&lt;/strong&gt; was surely the best of the best in terms of developer experience. I migrated my schema and data from local to the cloud within seconds, the bites and wrights features are tested in separate branches before promoting them to prod. It was a unique DX that I'd never seen before and Prisma was the cherry on top!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The real technical challenge was making the editor, markdown parser and renderer align to the global application context.&lt;/em&gt; This meant that I had to learn how markdown processors worked under the hood.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A month ago I had no idea how markdown parsers, rendering, and text editors work, now I have a slight idea of how they work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There were a lot of technical difficulties I faced while making wrighter. The tech stack was new for me(except nextjs) and it's been a very looooong time since I deployed a monolithic server, so I am also out of touch. Here's the tech stack I've used&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nextjs - Frontend&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.fastify.io/"&gt;Fastify&lt;/a&gt; Nodejs - Backend&lt;/li&gt;
&lt;li&gt;MySQL &lt;a href="https://planetscale.com/"&gt;PlanetScale&lt;/a&gt; - Remote DB&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt; - ORM &lt;/li&gt;
&lt;li&gt;IndexedDB(&lt;a href="https://dexie.org/"&gt;dexiejs&lt;/a&gt;) - Local + Cache DB&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt; - Frontend builds and deployments&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://render.com/"&gt;Render&lt;/a&gt; - Backend builds and deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest challenge was mostly handling IndexedDB queries and managing the intersection between the code for guest users and logged-in users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Wrighter offline and React Query
&lt;/h2&gt;

&lt;p&gt;If you haven't heard of &lt;a href="https://tanstack.com/query/v4/"&gt;react query&lt;/a&gt;, it's basically an async state management tool. Before this project, I used react query for fetching and caching API calls and that's about it. But soon I found out that, it can also be used with &lt;strong&gt;&lt;em&gt;any&lt;/em&gt;&lt;/strong&gt; kind of async calls. The best part is that it's a global state, So I can inspect and use a query from any component just by using a hook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--36lv26OV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/GWwTqnC.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--36lv26OV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/GWwTqnC.png%2520align%3D%2522center%2522" alt="react query as global state manager" width="586" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The whole of wrighter's frontend is built around this idea. React query manages the state for both guest users and logged-in users with a single query. This solves the problem of managing two different state updates on the component level. The ground-level components don't need to care about where the data is coming from(either IndexedDB or PlanetScale), it just needs to understand the data structure, and the rest of it works magically! react query saves the day(yet again) ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinventing the wheel with IndexedDB
&lt;/h2&gt;

&lt;p&gt;IndexedDB is supposed to be a minimal database solution in the browser for persistent data storage. Though it does not have the conventional "tables" that you see in databases, it does have object stores(mongodb-like), which means you can store any kind of data in a collection(arrays, blobs, array of objects, etc..). But on the other hand, PlanetScale uses MySQL. It is a full-fledged &lt;em&gt;relational&lt;/em&gt; database. You can join tables and perform all the fancy queries. As you can see, these two are both extremes of data modeling!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9JTkAiJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/D2iWRBb.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9JTkAiJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/D2iWRBb.png%2520align%3D%2522center%2522" alt="prisma data model for MySQL DB" width="815" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem is that I had to manage two different schemas for the data. In order to sync the data with both IndexedDB and MySQL DB in the future. Although this was a non-existent problem and for the sake of future-proofing, I had to replicate the same schema for IndexedDB too. I created relational collections that hold &lt;strong&gt;foreign keys&lt;/strong&gt; of other collections in the IndexedDB schema. It's kinda funny to think about it, but it works. &lt;em&gt;Here's the same data model as IndexedDB dexie schema.&lt;/em&gt;&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;IDB&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Dexie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;wrights&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WrightIDB&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;editorContext&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WrightIDB&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;tagWright&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TagWright&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;bites&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Bite&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;tagBite&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TagBite&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;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrighter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;wrights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, title, head, createdAt, updatedAt, userId, content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;editorContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, title, head, createdAt, updatedAt, userId, content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, name, color, userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tagWright&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, tagId, wrightId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bites&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, title, content, type, createdAt, updatedAt, userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tagBite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, tagId, biteId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This essentially means that I have to do &lt;strong&gt;joins and cascade delete/updates on code&lt;/strong&gt;, for IndexedDB operations, which was a unique experience. Here's the code to delete a bite, that also cascade deletes the tag relations for it. This also applies to getting queries too. To GET something, I have to perform a &lt;strong&gt;fake inner join&lt;/strong&gt; between two collections using code and then return the data.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteBite&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;biteId&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;indexedDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bites&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;biteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// faking cascade delete&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;indexedDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagBite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;biteId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;biteId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works flawlessly every time, so it was a satisfying problem to solve. This approach makes me worry less if I have to sync the whole IndexedDB and PlanetScale in the future. If you are trying to do the same, do not reinvent the wheel as I did. use something like &lt;a href="https://www.npmjs.com/package/dexie-relationships"&gt;dexie-relationships&lt;/a&gt; library instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wY86AxbI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/26cLnXx.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wY86AxbI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/26cLnXx.png%2520align%3D%2522center%2522" alt="if it works, don't touch it" width="491" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Some Hiccups...
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--38irbSYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/yS9KQ6s.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--38irbSYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/yS9KQ6s.png%2520align%3D%2522center%2522" alt="solving imaginary problems" width="440" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to use something new for wrighter and that's why I used fastify. I was more of an &lt;a href="https://expressjs.com/"&gt;express&lt;/a&gt; guy before. The ability to add validator schemas was new to me in node backends, combining this with &lt;a href="https://github.com/colinhacks/zod"&gt;zod&lt;/a&gt; and typescript became confusing. There were several situations where I was missing fields for responses because I forgot to add them to the schema. I blame myself for that lol&lt;/p&gt;

&lt;p&gt;The react Bytemd editor was meant to be a hackable markdown editor. I think it probably means about the different configurations it provides. But I wanted it even more hackable, &lt;strong&gt;&lt;em&gt;I had to literally hack bytemd's context to use it globally&lt;/em&gt;&lt;/strong&gt;. I got the editor's context and put it inside &lt;code&gt;window&lt;/code&gt; object to manipulate the editor from different components. After the editor unmounts I just remove the &lt;code&gt;editorContext&lt;/code&gt; from &lt;code&gt;window&lt;/code&gt; by simply doing &lt;code&gt;window["editorContext"] = null&lt;/code&gt; on the effect cleanup.&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// provides editor's context &lt;/span&gt;
  &lt;span class="c1"&gt;// assume this runs as useEffect() hook for the editor&lt;/span&gt;
  &lt;span class="nx"&gt;editorEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ByteMdEditorContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;shouldInjectToWindow&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;injecting context into window&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editorContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&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;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="nb"&gt;window&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editorContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;destroyed editor context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// window.d.ts&lt;/span&gt;
&lt;span class="c1"&gt;// type declaration for editor context&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;editorContext&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;BytemdEditorContext&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was hacky but it worked. &lt;strong&gt;This is how the command bar knows about the editor's current context&lt;/strong&gt;. When a command is selected, it used the window object to apply different formatting styles and perform actions on the editor.&lt;/p&gt;




&lt;h1&gt;
  
  
  Closing Notes
&lt;/h1&gt;

&lt;p&gt;This has got to be one of the most satisfying projects I've ever worked on. It taught me many things like &lt;em&gt;markdown parsers, AST, rehype, faux relational IndexedDB, etc..&lt;/em&gt; that I never would've learned elsewhere.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This blog that you are reading right now is &lt;a href="https://wrighter.vercel.app/wright/introducing-wrighter-a-powerful-markdown-blogger-and-a-writing-companion-6J96hd6t0pyy8wDFlkZUI0"&gt;fully written with wrighter and published with wrighter&lt;/a&gt;. I just love how good it feels to use your own tools to create something cool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I might be biased, but &lt;strong&gt;I highly recommend wrighter for any kind of blogs/scientific writing/brainstorming.&lt;/strong&gt; It has greatly improved my existing workflow!&lt;/p&gt;

&lt;p&gt;Wrighter was the project that I started in order to improve the workflow of &lt;code&gt;writing articles -&amp;gt; getting reviews -&amp;gt; publishing&lt;/code&gt;. But while working on it for a week, I found out that the &lt;code&gt;writing articles&lt;/code&gt; stage in itself can be improved, so I went back to the drawing board and situated my USP around the &lt;em&gt;ease of writing and brainstorming&lt;/em&gt;. That was the best decision that I took because it worked out so well! This tweet below by &lt;a href="https://twitter.com/mattwensing/status/1552136584224509954"&gt;Matt Wensing&lt;/a&gt; captures the story of how wrighter was born ⭐&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U_a61V0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/Os0wxgM.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U_a61V0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/Os0wxgM.png%2520align%3D%2522center%2522" alt="tweet about how wrighter was born" width="460" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Important Links
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Read this same blog on Wrighter - &lt;a href="https://wrighter.vercel.app/wright/introducing-wrighter-a-powerful-markdown-blogger-and-a-writing-companion-6J96hd6t0pyy8wDFlkZUI0"&gt;Here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wrighter Homepage - &lt;a href="https://wrighter.vercel.ap/"&gt;Wrighter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Contribute to Wrighter - &lt;a href="https://github.com/sanvishal/wrighter"&gt;Wrighter GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sanvishal"&gt;My GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/tk_vishal_tk"&gt;Follow me on twitter&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please try out wrighter or contribute to wrighter, I would love to get your feedback ❤️&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Optimizing Your Web App for Maximum Runtime Performance and Premature Optimization 🦄</title>
      <dc:creator>TK Vishal</dc:creator>
      <pubDate>Mon, 16 May 2022 10:51:12 +0000</pubDate>
      <link>https://dev.to/tkvishal/optimizing-your-web-app-for-maximum-runtime-performance-and-premature-optimization-3loj</link>
      <guid>https://dev.to/tkvishal/optimizing-your-web-app-for-maximum-runtime-performance-and-premature-optimization-3loj</guid>
      <description>&lt;p&gt;&lt;em&gt;This blog is originally posted at &lt;a href="https://vishaltk.hashnode.dev/optimizing-your-web-app-for-maximum-runtime-performance-and-premature-optimization"&gt;hashnode&lt;/a&gt; for the &lt;a href="https://townhall.hashnode.com/the-epic-hashnode-writeathon"&gt;writethon&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Websites nowadays fail to perform well on user inputs and actions. Poorly optimized frontend code can very easily break the user experience and the adoption rate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Your web application could have high user volumes, built to be delivered to the browser by a CDN for faster loading/caching, and designed with resilient architectures, well-performing backends, and disaster recovery mechanisms.&lt;/li&gt;
&lt;li&gt;  Your web application could also load blazingly fast within 1s and could have the prettiest looking UI anyone has ever seen with lazy loading, code splitting, and all other load time optimizations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conversely, your application might have a poorly performing runtime frontend code, which breaks the entire experience for end-users in the long run. If your application is highly dynamic/real-time and relies mostly on user actions, there is a high chance that your application is client-side rendered(CSR) with technologies like React, Angular, or Vue. Hence, it becomes very crucial to optimize the front end to deliver a seamless user experience.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kjY8sEU4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydpxffl87r1k7fh5k72z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kjY8sEU4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydpxffl87r1k7fh5k72z.png" alt="How performance affects user experience in simple use case" width="880" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How performance affects user experience in a simple use case (&lt;a href="https://input-delay.glitch.me/"&gt;How annoying is too annoying?&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A well-performing frontend should provide instant feedback for the action performed. Users expect a native feel to the web applications that they use in any form factor(desktop, mobile) as the line between native apps and standard web applications is becoming thinner day by day through Progressive Web Apps(PWA). Optimizing your app can have a drastic impact on your conversion rate and click-through rates.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RttTzjFg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gg2fpabm97xrsh382suq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RttTzjFg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gg2fpabm97xrsh382suq.png" alt="E-commerce conversion rate by On-Click Rendering Performance Category (desktop)" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;E-commerce conversion rate by On-Click Rendering Performance Category (desktop) from &lt;a href="https://simplified.dev/performance/impact-of-web-performance"&gt;The Impact of Web Performance&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Caring About Performance Too Early or Too Late 🐌
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“&lt;strong&gt;move fast, break things”&lt;/strong&gt;&lt;/em&gt; is a common motto around fast-moving projects. Although this is a good approach to ship “working“ products fast, It becomes very easy to forget about writing manageable performant code. Developers would be more focused on delivering the results first and caring about performance later. Depending on the application, the performance tech debt piles on and becomes unmanageable.&lt;/p&gt;

&lt;p&gt;Hacky/patchy fixes would be made to critical parts of the application to fix the performance issues at the very end of the project. It can often lead to various unknown side effects on other parts of the project that no one in your team has ever seen before. Initially, developers write straightforward code that is easy to understand and takes less time to write. Thus, writing optimized code has a cost(time and resources) attached to it. Without proper documentation, the code base becomes complex with cryptic performance hacks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.&lt;/p&gt;

&lt;p&gt;Yet we should not pass up our opportunities in that critical 3%.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;- Donald Knuth&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This does not mean that every line of code that you write should have a performance-saving gimmick.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;a proper performance fix is implemented only when it can be measured&lt;/strong&gt;. Unmeasured performance fixes can very often lead to unexpected bugs and issues.&lt;/li&gt;
&lt;li&gt;caring about optimizing the non-critical part of your application is a huge waste of time and resources.&lt;/li&gt;
&lt;li&gt;fixing performance issues at the wrong time in your development cycle can also have a negative outcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While starting on a task or a project, &lt;em&gt;some good premature optimization could be…&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restructuring your files and folders, breaking your code into functions/components.&lt;/li&gt;
&lt;li&gt;Enforcing the usage of types on dynamically typed languages (optimizing workflow)&lt;/li&gt;
&lt;li&gt;The flow of data to and fro parent and child components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;and some bad premature optimization could be…&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using profilers and fixing minor issues frequently without any feedback from your users.&lt;/li&gt;
&lt;li&gt;Using complex data structures and algorithms where a simple Array and inbuilt sort function would do the job.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When starting, it is necessary to think big. It should be less about &lt;em&gt;“should I use a for or forEach loop?“&lt;/em&gt; and more about &lt;em&gt;“should I break down this huge component into sub-components to reduce unnecessary re-renders?“.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Measuring your frontend performance ⏲️
&lt;/h2&gt;

&lt;p&gt;Runtime performance is a tricky problem to solve. The trickier part is measuring the performance and sniffing out the heavy components. Though there are various tools available to measure the frontend performance. It is always helpful to identify the main pain points of the application manually by clicking around. Identify components/pages taking most of the load and use it as a starting point. There can be various ways to measure performance depending on your app’s use case and complexity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually testing&lt;/li&gt;
&lt;li&gt;Stress testing with &lt;a href="https://umaar.com/dev-tips/88-cpu-throttling/"&gt;devtools CPU throttling&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://nira.com/chrome-developer-tools/"&gt;Using Chrome Devtools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Measuring performance on the code level

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;console.time()&lt;/code&gt; , &lt;code&gt;console.timeEnd()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;performance.measure()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react-addons-perf&lt;/code&gt; (&lt;a href="https://reactjs.org/docs/perf.html"&gt;more on react performance&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using a profiler

&lt;ul&gt;
&lt;li&gt;React Devtools profiler&lt;/li&gt;
&lt;li&gt;Angular Devtools profiler&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After an initial round of testing, you might get an idea of where and how to start optimizing your app. This blog assumes you have the prerequisite knowledge on how to read &lt;a href="https://www.brendangregg.com/flamegraphs.html"&gt;flame graphs&lt;/a&gt; and to get insights from the browser profiler.&lt;/p&gt;
&lt;h2&gt;
  
  
  Ways to Optimize 🤔
&lt;/h2&gt;

&lt;p&gt;There are plenty of different ways to optimize your application depending on the tech stack that you use, frequency and shape of the data that you get from the server, use case of your application, and so on. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;This write-up covers some of the methods in the increasing level of complexity. As the complexity increases, the performance fix becomes very narrow and context-specific to your application.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Caching and Memoisation&lt;/li&gt;
&lt;li&gt;Layout Reflow &amp;amp; Thrashing&lt;/li&gt;
&lt;li&gt;Virtualization&lt;/li&gt;
&lt;li&gt;Delay and Debounce rendering&lt;/li&gt;
&lt;li&gt;Thinking outside the box

&lt;ul&gt;
&lt;li&gt;Offloading to web workers&lt;/li&gt;
&lt;li&gt;Offloading to canvas&lt;/li&gt;
&lt;li&gt;Offloading to GPU/GPGPU (experimental)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Caching and Memoization 🗂️
&lt;/h2&gt;

&lt;p&gt;By definition caching is a &lt;strong&gt;technique that stores a copy of a given resource and serves it back when requested.&lt;/strong&gt; Memoization is a type of caching where expensive calculations are stored in a cache to avoid frequent recalculations. In a nutshell, your code memorizes the previously calculated results and serves when requested from the memory instead of bothering the CPU.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vet1_E9u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qnhqtp5d81noj105pr06.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vet1_E9u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qnhqtp5d81noj105pr06.gif" alt="remembering = memorization" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Choosing the right data type
&lt;/h3&gt;

&lt;p&gt;This is where your good ol' data structures and algorithms knowledge plays a vital role. Consider a case where the server returns a list of users in an array of objects with a unique identifier &lt;code&gt;userId&lt;/code&gt;. To perform lookup operations (which you might do frequently), it would take &lt;em&gt;O(n)&lt;/em&gt; time where n is the number of users in the array. If you group the users by &lt;code&gt;userId&lt;/code&gt; once and convert it to a key-value pair map. It can drastically reduce the lookup time to &lt;em&gt;O(1)&lt;/em&gt;. (&lt;a href="https://www.freecodecamp.org/news/big-o-notation-why-it-matters-and-why-it-doesnt-1674cfa8a23c/"&gt;more on the big-O notation&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;You’ve basically &lt;strong&gt;indexed&lt;/strong&gt; your local data for faster access. Trading some space in the heap memory for easier lookups instead of relying on the CPU for frequent operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the array way 🚫&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ted Mosby&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;architect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;barney&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Barney Stinson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;robin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ribon Scherbatsky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;news anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;// straight forward way to lookup/search in O(n) worst case&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usersArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ted&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;Hashmaps/key-value pairs have constant time retrieval, lookups, searching, insertion and deletion. You can easily generate key-value maps from an array of objects by using lodash’s &lt;code&gt;_.keyBy(usersArray, 'userId')&lt;/code&gt;. This makes it the perfect data structure if the data is constantly being used inside for loops and blocking 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="c1"&gt;// the hashmap way ✅&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersMap&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="s1"&gt;ted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ted Mosby&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;architect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;barney&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;barney&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Barney Stinson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;robin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;robin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ribon Scherbatsky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;news anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// efficient way to lookup/search O(1) worst case&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usersMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ted&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;Here, &lt;code&gt;Array.indexOf()&lt;/code&gt; could be magnitude slower than object reference-based lookup and it looks a lot cleaner to read. That being said, the performance difference between both methods depends on your access patterns and the size of the array/object.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝  What's the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using hashmaps should not always be your prime solution. Why so? because &lt;em&gt;(&lt;a href="https://stackoverflow.com/a/17295727/14806010"&gt;JS is weird&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The way JS engine works is different on each browser, they have their own implementation of managing the heap memory. (V8 on Chrome, Spidermonkey on Firefox, and Nitro for Safari)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Object lookups could be sometimes faster on chrome and slower on firefox given the same data. It can also depend on the size and shape of the data inside your data structure (array/hashmap)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always measure if the applied change has an impact on the final performance.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Function Level Memoization
&lt;/h3&gt;

&lt;p&gt;Functional memorization is a frequently used technique in &lt;a href="https://www.geeksforgeeks.org/dynamic-programming/"&gt;Dynamic Programming&lt;/a&gt;. It can memorize the function’s output and inputs so that when the caller calls the function again with the same inputs, it returns from its memory/cache instead of re-running the actual function.&lt;/p&gt;

&lt;p&gt;A memorized function in JS consists of 3 major components…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A higher-order function wrapper that wraps the expensive function in a closure. &lt;/li&gt;
&lt;li&gt;An expensive pure function that returns the same outputs for the same inputs under any conditions. &lt;a href="https://dev.to/sanspanic/pure-vs-impure-functions-50aj"&gt;Pure functions&lt;/a&gt; should not have any side effects nor should depend on any values outside their own scope. &lt;/li&gt;
&lt;li&gt;A &lt;code&gt;cache&lt;/code&gt; hashmap that acts as our memory and memorizes the input-outputs and key-value pairs.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L3MGlTI7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95ogxjrmvudm6y6b4w3u.png" alt="difference between pure and impure functions" width="880" height="283"&gt;
&amp;gt; difference between pure and impure functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the memoize higher-order function implemented in typescript. It takes in a function and returns the memoized function. The expensive function(to be memorized) can have any number of arguments. The cache keys are transformed into primitive data types like &lt;code&gt;string&lt;/code&gt; or &lt;code&gt;number&lt;/code&gt; using the second argument in the higher-order function - &lt;code&gt;transformKey&lt;/code&gt;. &lt;em&gt;It is also fully typesafe! ✨&lt;/em&gt;&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AnyFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;memo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fn&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;AnyFn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transformKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;string&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;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fn&lt;/span&gt;&lt;span class="o"&gt;&amp;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;// transform arguments into a primitive key&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transformKey&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// return from cache if cache hit&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;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;cache&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;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="c1"&gt;// recalulate if cache miss&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// populate cache with result&lt;/span&gt;
    &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&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;memoizedExpensiveFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expensiveFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&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;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;args&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;Memoization is very well suited for recursive operations to cut whole chunks of redundant operations down the recursion tree. It is also helpful in functions where there are frequently repeated inputs giving out the same outputs. Instead of reinventing the wheel, you could use battle-tested memorize wrappers provided by libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useMemo()&lt;/code&gt; in react&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_.memoize()&lt;/code&gt; in lodash&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@memoize&lt;/code&gt; decorators&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 What's the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Memoization is not a solution to every expensive calculation problem, you should &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; opt into memoization when…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the expensive function depends on or affects some other value outside its own scope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the input is highly fluctuating and irregular or where you don’t know the range of inputs. Since we are trading some space on heap memory for CPU cycles, erratic inputs can create a huge cache object and the use of memoization here would become useless.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Component Level Memoization and Preventing Unnecessary Rerenders
&lt;/h3&gt;

&lt;p&gt;In the context of how React works, the component only rerenders with props or the state of a component has changed. When a parent component rerenders, all of its children rerender too. Rerendering is the process of calling the function/render method, Hence this is the perfect place to use our memoization techniques.&lt;/p&gt;

&lt;p&gt;Before diving into memoizing our component, it is essential to optimize the component’s state first. A common mistake that most React devs make is misusing the &lt;code&gt;useState&lt;/code&gt; hook to store constant mutating variables that do not reflect on the UI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useState()&lt;/code&gt; is a better choice if the UI depends on the value else it is better to use &lt;code&gt;useRef()&lt;/code&gt; or &lt;code&gt;useMemo()&lt;/code&gt; for mutable variables instead.&lt;/li&gt;
&lt;li&gt;when passing functions from the parent to child component, it is better to use wrap that function with &lt;code&gt;useCallback()&lt;/code&gt; instead of passing the functions themselves. Passing raw functions to memorized components would still trigger a rerender even when the props haven’t changed, since the parent component is rerendered, it created a new reference to the function and passed it to children, hence the rerender.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// passing raw functions ℹ️&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;ParentComponent&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="nx"&gt;handleToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// do something&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SomeExpensiveComponent&lt;/span&gt; &lt;span class="na"&gt;onToggle&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleToggle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// using useCallback() to pass functions ✅&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;ParentComponent&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="nx"&gt;handleToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCallback&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;// do something&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SomeExpensiveComponent&lt;/span&gt; &lt;span class="na"&gt;onToggle&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleToggle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the preliminary steps, your component should have fewer rerenders now!&lt;/p&gt;

&lt;p&gt;React decides to re-render the children whenever the parent component rerenders. If a child component is memorized, React first checks if the props have changed by doing a shallow comparison of props. If you have a complex object in your props, it only compares the object reference to the old and new props (&lt;code&gt;a===b&lt;/code&gt;). The best part is that you have full control over this equality function to govern when to rerender the component based on old and new props.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ExpensiveChildComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;state&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="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;state&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MemoizedExpensiveChildComponent&lt;/span&gt; &lt;span class="o"&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;memo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ExpensiveChildComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newProps&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;// do custom validation on old and new props, return boolean&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;ParentComponent&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;someState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSomeState&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="p"&gt;({})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MemoizedExpensiveChildComponent&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;someState&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;What’s the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If your component depends on &lt;code&gt;useContext()&lt;/code&gt;, &lt;code&gt;React.memo()&lt;/code&gt; is useless. &lt;a href="https://github.com/facebook/react/issues/15156"&gt;The component would rerender irrespective if the memo.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;use useCallback() only when the preformance benifit is quanifiable, useCallback() can become a source of performance issue if not used at the &lt;a href="https://kentcdodds.com/blog/usememo-and-usecallback"&gt;right place&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;use useMemo() in the component only when the component re-renders often with the same props.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes custom comparing the props can consume more CPU cycles than &lt;a href="https://dmitripavlutin.com/use-react-memo-wisely/"&gt;actually rerendering the component&lt;/a&gt;, so measure first then apply the performance fix.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Layout Reflow &amp;amp; Thrashing 🌊
&lt;/h2&gt;

&lt;p&gt;Layout reflow is when the browser calculates the dimensions, position, and depth of an element in a webpage. A reflow would occur when...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;getting/setting measurements of elements' metrics using &lt;code&gt;offsetHeight&lt;/code&gt;, &lt;code&gt;scrollWidth&lt;/code&gt;, &lt;code&gt;getComputedStyle,&lt;/code&gt; and &lt;a href="https://gist.github.com/paulirish/5d52fb081b3570c81e3a"&gt;other DOM functions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;adding/inserting or removing an element in the DOM tree.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://csstriggers.com/"&gt;changing CSS styles&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;resizing browser window or iframe window.&lt;/li&gt;
&lt;li&gt;basically, any operation that would need the browser to modify the presented UI on the screen.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vRP6Sbw9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0st0zfbg4vd3nntepwf.png" alt="very high-level overview of the browser rendering pipeline" width="880" height="322"&gt;
&amp;gt; very high-level overview of the browser rendering pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a reflow happens, the browser would synchronously(blocking code) recalculate the dimensions and positions of elements on the screen. As you might have guessed, reflowing is a very expensive job for the render pipeline, so the browser tries to queue and batch the updates so that it can reflow the whole UI at once instead of blocking the main thread with frequent reflows.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tY3VBle1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gsvpxu59bhupdp0b5z82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tY3VBle1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gsvpxu59bhupdp0b5z82.png" alt="The recalculate style in the performance chart means that the browser is reflowing" width="813" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;&lt;a href="https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/"&gt;recalculate style&lt;/a&gt;&lt;/strong&gt; in the performance chart means that the browser is reflowing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The performance impact due to reflowing depends on the complexity of the reflow. A call to &lt;code&gt;getBoundingClientRect()&lt;/code&gt; on a smaller DOM tree would have a lesser impact on performance than calling the same on a larger nested DOM tree. Reflow in itself is an essential part of the rendering process and it is acceptable on lower margins.&lt;/p&gt;

&lt;p&gt;Consider the following piece of 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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;listItems&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="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;listItems&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&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;Here, the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;offsetHeight&lt;/code&gt; are being read or written inside a for-loop for all the items inside a list. Assume there are 500 list items and is getting called every time there is a new list item. There is an obvious performance hit when these properties are called too frequently, the browser keeps adding those calls to the queue to process them later. At one point when the browser flushes the queue, the browser struggles to optimize and batch the reflows, but it cannot since the code is requesting &lt;code&gt;clientHeight&lt;/code&gt; in quick successions inside a for-loop, which triggers layout → reflow → repaint synchronously on every iteration.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bNOjYd5D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i8828fbtbithnb4jqdz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bNOjYd5D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i8828fbtbithnb4jqdz.gif" alt="spongebob-computer.gif" width="220" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When this happens, the page freezes for some seconds and this is called &lt;strong&gt;&lt;em&gt;Layout Thrashing.&lt;/em&gt;&lt;/strong&gt; This is a minor hiccup on desktops and laptops but has severe browser-crashing consequences on lower-end mobiles.&lt;/p&gt;

&lt;p&gt;This is a very common mistake that many developers make, lucky for us the solution is very simple and right before your eyes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Caching outside the loop
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;We cache the&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;reflow-triggering&lt;/strong&gt;&lt;/em&gt; &lt;strong&gt;value outside any kind of loop.&lt;/strong&gt; So, we just calculate the height/width only one time allowing the browser to optimize it on its own.&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;listContainerHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;listItems&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="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;listItems&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listContainerHeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&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;h3&gt;
  
  
  Read &amp;amp; Write Pattern
&lt;/h3&gt;

&lt;p&gt;We learned that browser tries to batch and optimize subsequent reflow layout calls into one single reflow. &lt;a href="http://blog.wilsonpage.co.uk/preventing-layout-thrashing/"&gt;We can use this to our advantage&lt;/a&gt;. The code example illustrates better…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// "read - write - read - write - read - write" pattern ❌&lt;/span&gt;
&lt;span class="c1"&gt;// read&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem1Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write (triggers layout)&lt;/span&gt;
&lt;span class="nx"&gt;listItem1Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem1Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// read (reflows layout)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write (triggers layout)&lt;/span&gt;
&lt;span class="nx"&gt;listItem2Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// read (reflows layout)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem3Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write (triggers layout)&lt;/span&gt;
&lt;span class="nx"&gt;listItem3Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem3Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="c1"&gt;/// "read - read - read - write - write - write" pattern ✅&lt;/span&gt;
&lt;span class="c1"&gt;// read (browser optimizes)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem1Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write (triggers layout)&lt;/span&gt;
&lt;span class="nx"&gt;listItem1Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem1Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;listItem2Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;listItem3Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem3Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// reflow just one time and its seamless&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using &lt;code&gt;window.requestAnimationFrame()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;window.requestAnimationFrame()&lt;/code&gt; or rAF is used to tell the browser that you are going to perform animations, Hence it calls the callback inside rAF before the next repaint. This allows us to batch all the DOM writes(reflow triggering code) inside rAF guaranteeing that browser runs everything on the next frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// read&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem1Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write&lt;/span&gt;
&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;listItem1Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem1Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// read &lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write&lt;/span&gt;
&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;listItem2Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem2Height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// read&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listItem3Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// write&lt;/span&gt;
&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;listItem3Height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listItem3eight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// browser calls rAF on the next frame hence reflows smoothly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Virtualization 👁️
&lt;/h2&gt;

&lt;p&gt;Games tend to have highly detailed 3D models, huge textures, huge open-world maps, and complex shaders that fill in an immersive environment around the player. How do they optimize all those complex models into a limited compute GPU and still get &lt;strong&gt;60+ FPS&lt;/strong&gt;? &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h80Ugw4---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx0z8zvl3pu2ysoy3vrv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h80Ugw4---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx0z8zvl3pu2ysoy3vrv.gif" alt="The blue cone is the view frustum of the player and it renders resources only inside it" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The blue cone is the view frustum of the player and it only renders the resource inside it, shown behind the scenes in Horizon Zero Dawn(&lt;a href="https://twitter.com/noclipvideo/status/1354169521326542848?lang=en"&gt;more about it&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They use a technique called &lt;strong&gt;&lt;a href="https://www.reddit.com/r/gaming/comments/7v85ff/heres_whats_happening_in_horizon_zero_dawn_every/"&gt;Frustum Culling&lt;/a&gt;.&lt;/strong&gt; Frustum culling is the process of removing objects that lie completely outside the viewing frustum(POV) of the player. It removes everything that’s outside the player’s POV and spends all the computing power to render only the resources that the player is looking at. This technique was invented many years ago and it is still one of the major(default) ways to boost the runtime performance in games.&lt;/p&gt;

&lt;p&gt;We can use this same old technique on our apps too! The web folks call it &lt;strong&gt;Virtualization.&lt;/strong&gt; Imagine a large list or an infinite(pannable, zoomable) canvas or a huge(horizontally and vertically scrollable) grid of items. Optimizing the runtime on these kinds of use-cases could be a hard problem to tackle.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CBSC6pev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eute8jj2t0liosxwaks3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CBSC6pev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eute8jj2t0liosxwaks3.gif" alt='virtualizing a &amp;lt;ul&amp;gt; list, items highlighted blue are rendered, grey is not rendered via "Brian Vaughn"' width="324" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;virtualizing a &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; list, items highlighted blue are rendered, grey is not rendered, and outside user's view (via &lt;a href="https://bvaughn.github.io/forward-js-2017/#/12/5"&gt;Brian Vaughn&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lucky for us, there is a react library (react-window) that handles the virtualization logic for you. Virtualization works by implementing 3 core ideas…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Having a viewport container DOM element that acts as your scroll container.&lt;/li&gt;
&lt;li&gt;Having a smaller element that contains your viewable items of a list.&lt;/li&gt;
&lt;li&gt;Absolutely positioning the list items based on current scroll position, width, and height of scroll container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the browser spends all of its compute power on rendering what the user is currently seeing, you would gain a huge performance boost very easily.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KabjCHEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bj5zzwrkcy7nyrujy4p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KabjCHEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bj5zzwrkcy7nyrujy4p4.png" alt="performance upgrade with and w/o virtualization" width="640" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;performance upgrade with and w/o virtualization (via &lt;a href="https://www.patterns.dev/posts/virtual-lists/"&gt;List Virtualization&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;react-window&lt;/code&gt; provides easy-to-use components that make implementing virtualization into your apps a piece of cake. &lt;a href="https://react-window.vercel.app/#/examples/list/fixed-size"&gt;&lt;strong&gt;react-window&lt;/strong&gt;&lt;/a&gt; wraps your list item in a parent component that would handle all the virtualization logic under the hood. &lt;code&gt;react-window&lt;/code&gt; expects a fixed height for the parent scroll container and precalculated height for the list item.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bniopZI---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gc5wzjhw9zdagaeqr4z6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bniopZI---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gc5wzjhw9zdagaeqr4z6.png" alt="Illustrating react-window FixedSizeList API" width="880" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Illustrating &lt;strong&gt;react-window&lt;/strong&gt; &lt;code&gt;FixedSizeList&lt;/code&gt; API&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the height of all the list items is known and calculated, you can use the &lt;code&gt;FixedSizeList&lt;/code&gt;. If the height of each list item depends on the content of the item, then you can precalculate heights with a function and pass it to a &lt;code&gt;VariableSizeList&lt;/code&gt; in the &lt;code&gt;itemSize&lt;/code&gt; prop. You can also use the &lt;code&gt;overscanCount&lt;/code&gt; to render a specific number of items outside the scroll area if your list items need to prefetch image assets or to catch the focus of the user.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rowHeights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&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;map&lt;/span&gt;&lt;span class="p"&gt;(()&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="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&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;getItemSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rowHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&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;ListItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&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;&amp;lt;&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="nx"&gt;style&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Row &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HugeList&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VariableSizeList&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;150&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;itemSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getItemSize&lt;/span&gt;&lt;span class="si"&gt;}&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;300&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;ListItem&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;VariableSizeList&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;&lt;code&gt;react-window&lt;/code&gt; also supports &lt;a href="https://react-window.vercel.app/#/examples/grid/variable-size"&gt;grid-based&lt;/a&gt; UI where there’s both horizontal and vertical scrolling(think of large e-commerce websites or an excel sheet) with variable item heights or widths. &lt;a href="https://github.com/bvaughn/react-window-infinite-loader"&gt;react-window-infinite-loader&lt;/a&gt; package that supports infinite loading and lazy load content outside the scroll area &lt;strong&gt;&lt;em&gt;and&lt;/em&gt;&lt;/strong&gt; also provides virtualization capabilities.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HugeGrid&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VariableSizeGrid&lt;/span&gt;
    &lt;span class="na"&gt;columnCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;columnWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getColumnWidth&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// takes in current index&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;150&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;rowCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;rowHeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getRowHeight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// takes in current index&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;300&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;GridItem&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;VariableSizeGrid&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;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;What’s the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It goes without saying that, do not use virtualization for lists with less number of items, measure first, then go for virtualization. Since there is a CPU overhead in running the virtualization algorithm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the list item has highly dynamic content and constantly resizing based on the content, virtualization could be a pain to implement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the list items are added dynamically with sorts, filters, and other operations, &lt;code&gt;react-window&lt;/code&gt; might produce odd bugs and blank list items.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Delay and Debounce Rendering ⛹🏼
&lt;/h2&gt;

&lt;p&gt;Delaying and Debouncing rendering is a common practice to reduce unnecessary re-renders on frequent data changes. Some modern web apps process and render tons of complex data arriving at extreme speeds through WebSockets or HTTP long polling. Imagine an analytics platform catering real-time analytics to users through the data arriving at the frontend using WebSockets at the rate of &lt;strong&gt;&lt;em&gt;15 messages per second&lt;/em&gt;&lt;/strong&gt;. Libraries like react, and angular is not built for rerendering a complex DOM tree at that rate and humans can’t perceive data changes at rapid intervals.&lt;/p&gt;

&lt;p&gt;Debouncing is a common practice used in search inputs where each &lt;code&gt;onChange()&lt;/code&gt; event triggers an API call. Debouncing prevents sending an API request for each letter change, instead, it waits for the user to finish typing for a specified amount of time and then sends an API request. We can use this technique for rendering too!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---oXgFOev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4a9vcfjw1fvl6ic60cr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---oXgFOev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4a9vcfjw1fvl6ic60cr9.png" alt="Illustration of how debouncing can optimize unnecessary API requests" width="880" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Illustration of how debouncing can optimize unnecessary API requests&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I won’t go too deep into how to implement debouncing on API requests. We’ll concentrate on how we could debounce renders using the same method. &lt;strong&gt;&lt;em&gt;Imagine you have a stream/burst of messages coming through a single WebSocket channel. You would like to visualize said messages in a line graph.&lt;/em&gt;&lt;/strong&gt; There are 3 main steps to debounce the renders…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A local buffer that would hold your WebSocket/frequently changing data outside of React/angular context (&lt;code&gt;useRef()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A WebSocket listener that takes in the messages from the network, parses, transforms them into an appropriate format, and puts them in the local buffer.&lt;/li&gt;
&lt;li&gt;A debounce function that when triggered would flush the buffer data to the component’s state to trigger a rerender.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FrequentlyReRenderingGraphComponent&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;dataPoints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDataPoints&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;TransformedData&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;dataPointsBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TransformedData&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;debouceDataPointsUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;debounce&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;// use the buffer&lt;/span&gt;
            &lt;span class="c1"&gt;// update the latest state with buffer data however you want!&lt;/span&gt;
            &lt;span class="nx"&gt;setDataPoints&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;dataPoints&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;dataPoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataPointsBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

            &lt;span class="c1"&gt;// flush the buffer&lt;/span&gt;
            &lt;span class="nx"&gt;dataPointsBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;// sets the state only when websocket messages becomes silent for 900ms&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;useWebsocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;onmessage&lt;/span&gt;&lt;span class="p"&gt;:&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="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="na"&gt;transformedData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransformedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transformAndParseData&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="c1"&gt;// push into buffer, does not rerender&lt;/span&gt;
            &lt;span class="nx"&gt;dataPointsBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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="nx"&gt;transformedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// important part of debouncing!!!&lt;/span&gt;
            &lt;span class="nx"&gt;debouceDataPointsUpdate&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineGraph&lt;/span&gt; &lt;span class="na"&gt;dataPoints&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dataPoints&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here’s a high-level implementation of debouncing the render. You can change the &lt;code&gt;useRef()&lt;/code&gt; buffer setter in the WebSocket message event and flushing logic during debounce however you want that is efficient depending on the shape of data.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VCGRaa_u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12mdnn4uqli2ci704a1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VCGRaa_u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12mdnn4uqli2ci704a1p.png" alt="Illustration of how debouncing can optimize unnecessary re-renders" width="880" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Illustration of how debouncing can optimize unnecessary re-renders&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many libraries that provide debounce functions out of the box…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://rxjs.dev/api/operators/debounce"&gt;RxJS&lt;/a&gt; &lt;code&gt;debounce()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://lodash.com/docs/#debounce"&gt;lodash&lt;/a&gt; &lt;code&gt;_.debounce()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;custom &lt;a href="https://usehooks.com/useDebounce/"&gt;react hook&lt;/a&gt; &lt;code&gt;useDebounce()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;What’s the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Debouncing should only be used on components that are frequently re-rendering or does any expensive job frequently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Buffering on real-time data can introduce race-condition bugs if not done carefully since you are maintaining two sources of truth at any point in time (buffer data and actual data)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Debounce only when you surely know there is a breathing time to flush the buffer and set the state, else the component won’t rerender, and the buffer would indefinitely grow in size!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Thinking Out of the Box 🧠
&lt;/h2&gt;

&lt;p&gt;Sometimes, any kind of optimization that you do internally in your codebase wouldn’t be enough. That’s when fixing a performance issue is not just a bottleneck to the UX, it becomes a bottleneck to the solution your web app is providing. Hence, we must find clever ways to think outside of the existing ecosystem in search of making our web app “usable”.&lt;/p&gt;

&lt;p&gt;Do you think apps like &lt;em&gt;&lt;strong&gt;Figma&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;&lt;strong&gt;Google Docs&lt;/strong&gt;&lt;/em&gt; are just made up of DOM elements? These apps exit out of the native approach to provide better solutions to users. &lt;strong&gt;At this point, it is not about fixing a performance&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;Bug&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;, it is more about adding an innovative&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/em&gt; &lt;strong&gt;to your web app.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Offloading to Web Workers 🧵
&lt;/h3&gt;

&lt;p&gt;Javascript is famously known to be single-threaded. Since it is single-threaded, we don't have to think about complex scenarios like deadlocks. Since it is single-threaded, It can only run one task at a time(&lt;strong&gt;synchronous&lt;/strong&gt;). To queue all these tasks for the CPU to execute, it uses a mechanism called an event loop.&lt;/p&gt;

&lt;p&gt;The OS and your browser have access to any number of threads your CPU provides. That’s why the browser can handle multiple tabs at once parallelly. What if we could somehow get access to another thread to do some of our complex operations?&lt;/p&gt;

&lt;p&gt;That’s exactly why &lt;strong&gt;Web Workers&lt;/strong&gt; are made.&lt;/p&gt;

&lt;p&gt;Imagine you have a huge React app with a fairly complex DOM tree that updates frequently on network changes. You are asked to perform a huge image processing/mathematical operation with huge images or inputs. Usually, when done in a normal way would fill in the main thread pool &lt;strong&gt;blocking&lt;/strong&gt; other essential operations like event listeners, rendering, and painting of the whole page. Hence, we use a Web Worker process to offload the work to a separate thread and get back with results(&lt;strong&gt;asynchronous&lt;/strong&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//// main.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// send complex operation inputs to worker.js&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&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="c1"&gt;// receive data from a worker.js&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//// worker.js&lt;/span&gt;
&lt;span class="c1"&gt;// receive data from main.js&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do complex operation here&lt;/span&gt;
  &lt;span class="c1"&gt;// send results to main.js&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worker API is very simple, you would post a message to the worker. The worker would have the code to process and reply back with the results to the listeners. To make it even easier Google has created the &lt;a href="https://github.com/GoogleChromeLabs/comlink"&gt;comlink&lt;/a&gt; library.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uG-Ohwlk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kkeidbiuk68i7no8ncc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uG-Ohwlk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kkeidbiuk68i7no8ncc2.png" alt="illustration of web workers" width="566" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;illustration of web workers(via &lt;a href="https://bitsofco.de/web-workers-vs-service-workers-vs-worklets/"&gt;Web workers vs Service workers vs Worklets&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is important to note that the web workers operate under a separate context, so your global/local variables applied on your main codebase won’t be available in the worker.js file. So, you would need to use specific bundling techniques to preserve the context between workers and main files. If you would like to integrate web workers with React’s &lt;code&gt;useReducer()&lt;/code&gt; hook, the &lt;code&gt;use-workerized-reducer&lt;/code&gt; package provides a simple way to do so. Thus, you can also process heavy state processing and also control react’s component lifecycle based on web worker’s results.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WorkerComponent&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;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;busy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useWorkerizedReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// reducer name in worker.js&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// reducer intial state&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;addTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dispatch&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;add_todo&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;todo&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;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;busy&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;Loading&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoList&lt;/span&gt; &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&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="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;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;What’s the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web workers have a separate context from your main application, so it is necessary to bundle your code with webpack loader like &lt;code&gt;worker-loader&lt;/code&gt; (&lt;a href="https://mmazzarolo.com/blog/2021-09-03-loading-web-workers-using-webpack-5/"&gt;more info&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;You cannot use web workers to manipulate DOM or add styles to the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Offloading to Canvas 🎨
&lt;/h3&gt;

&lt;p&gt;This is essentially a hacky way to render the UI, In some cases, the WebSocket messages would be coming at rapid speeds with no breathing time. In such cases, debouncing won’t solve the problem. These use-cases can be seen on trading and crypto platforms where there is a high volume of changes. &lt;a href="https://pro.coinbase.com/trade/BTC-USD"&gt;CoinBase&lt;/a&gt; solves the problem elegantly by using a canvas in the middle of a reactive DOM UI. It performs very well under rapid data changes and looks seamless with the native UI.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RijXXgEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2y49i6zavtfqfi0tt19.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RijXXgEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2y49i6zavtfqfi0tt19.gif" alt="rules are made to be broken" width="244" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s how the UI updates compared to the WebSocket messages in the network tab…&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XHofu_tw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zvpi9al8mesbn23bsjbn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XHofu_tw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zvpi9al8mesbn23bsjbn.gif" alt="The speed at which the data updates in the coinbase webapp" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The speed at which the data updates in the &lt;a href="https://pro.coinbase.com/trade/BTC-USD"&gt;CoinBase&lt;/a&gt; web app&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The whole table is just a canvas, but note that I can still hover over each row and get a hover highlight effect. This is by simply overlaying a DOM element on top of the canvas, but the canvas handles all the heavy lifting of rendering the text and alignment.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f5-9DrT2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ncild2qpf6360m96101.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f5-9DrT2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ncild2qpf6360m96101.png" alt="It's just long canvas underneath" width="355" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's just a long canvas underneath!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Offloading the work to canvas is very common when working with highly dynamic data like rich text editing, infinite dynamic grid content, and rapidly updating data. Google has adopted canvas as their main rendering pipeline in &lt;strong&gt;&lt;em&gt;Google Docs and Sheets&lt;/em&gt;&lt;/strong&gt; to have more control over primitive APIs and most importantly to have greater control over performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;What’s the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a hacky procedure to use a canvas inside a reactive DOM tree, there will be lots of &lt;em&gt;“reinventing the wheel“&lt;/em&gt; situations for basic functionalities like text alignment and fonts.&lt;/li&gt;
&lt;li&gt;Native DOM accessibility is lost in canvas-based data/text rendering.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Offloading to GPU/GPGPU (Experimental) 🔬
&lt;/h3&gt;

&lt;p&gt;This is where the write-up gets experimental and there’s very less chance that you’d use this technique on a real project. &lt;strong&gt;Imagine you’d have to train a neural network or batch process hundreds of images parallel or perform complex mathematical operations with a stream of numbers&lt;/strong&gt;. You might fall back to using a web worker thread to do the job(which would still work). But the CPU only has limited threads and a very limited number of cores. This means that it can process data faster with low latency but it cannot handle fast parallel operations very well.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KzQZ12Ci--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n9g1piwjvq3ibk4czyc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KzQZ12Ci--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n9g1piwjvq3ibk4czyc.gif" alt="CPU cooking 👅" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s why GPUs are made! Games and video encoding/decoding requires parallel processing on individual pixels on the screen for faster rendering at 60+FPS. GPUs have thousands of cores and are specially made to handle heavy parallel processing tasks. Using a CPU for these kinds of tasks would work but it would be too slow and would severely hog the CPU blocking other OS jobs.&lt;/p&gt;

&lt;p&gt;The tradeoff is that interfacing the GPU(&lt;a href="https://learnopengl.com/Getting-started/Shaders"&gt;GLSL Shaders&lt;/a&gt;) with the JS environment is the hardest part. GPUs are made to handle textures/images in a particular data structure. Doing trivial calculations with GPU requires hacky techniques to upload and download data from GPU. The GPUs performing these kinds of non-specialized CPU-related calculations are called &lt;a href="https://www.gigabyte.com/Glossary/gpgpu"&gt;GPGPU&lt;/a&gt;(General Purpose GPU).&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gVJnMsyf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u52czfntbzd6a5ds02pf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gVJnMsyf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u52czfntbzd6a5ds02pf.png" alt="The NVIDIA GEFORCE RTX 3090 GPU 🏇🏼" width="594" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The NVIDIA GEFORCE RTX 3090 GPU 🏇🏼&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// generates input matrices&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateMatrices&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="nx"&gt;matrices&lt;/span&gt; &lt;span class="o"&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&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;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;matrices&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;push&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
        &lt;span class="nx"&gt;matrices&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="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&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;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;matrices&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;y&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="nx"&gt;matrices&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="nx"&gt;y&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&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="nx"&gt;matrices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// create a kernel(function on GPU) &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gpu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GPU&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;multiplyMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gpu&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createKernel&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;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thread&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="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&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;setOutput&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// call the kernel&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matrices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateMatrices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;multiplyMatrix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matrices&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;matrices&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the real-world test results from &lt;a href="https://gpu.rocks/#/"&gt;GPU.js&lt;/a&gt;, notice that you don’t see any difference in computing time until the 512x512 matrix operation. After that point, the compute time for CPUs increases exponentially!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--re8SXVjT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fib9okdqryjtgbdei3p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--re8SXVjT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fib9okdqryjtgbdei3p6.png" alt="Comparing CPU vs CPU performance on matrix multiplication" width="880" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Comparing CPU vs CPU performance on matrix multiplication (via &lt;a href="https://gpu.rocks/#/"&gt;GPU.js&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;What’s the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPU-based general computation should be done with care, GPUs have configurable floating-point precisions and low-level arithmetics.&lt;/li&gt;
&lt;li&gt;Do not use GPGPU as a fallback for every single heavy calculation, as shown in the graph about it provides a boost in performance only if the calculations are highly paralleled.&lt;/li&gt;
&lt;li&gt;GPUs are best suited for image-based processing and rendering.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;~ That is it, at least for now, ~&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did I write this very long blog?
&lt;/h2&gt;

&lt;p&gt;Without doubt! This is the longest blog I’ve ever written. It is a culmination of raw experience and learnings from my previous projects. It's been on my mind bugging me for a very long time. We developers tend to work fast on features, push working code and call it a day. This looks good from a deliverable and management perspective. But, it is absolutely necessary to think about the end-users situation while you are working on a feature. Think about the type of device they would be using, and how frequently the user would be interacting. I’ve learned most of the web development on a 2GB RAM laptop with a Pentium processor, so I know the pain T_T. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SIyFWx9u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/bCfpwMjfAi0AAAAC/cat-typing.gif%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SIyFWx9u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/bCfpwMjfAi0AAAAC/cat-typing.gif%2520align%3D%2522center%2522" alt="cat typing" width="498" height="280"&gt;&lt;/a&gt;&lt;br&gt;
There is no right way to measure the performance, attach a deadline to the performance fix or quantify everything beforehand. &lt;strong&gt;&lt;em&gt;It is a continuous process that requires reconnaissance skills&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Although it is very hard to include/quantify a performance budget on every feature in a fast-moving project. Think how a particular feature addition would affect your application in the long run and document it. It is the individual developer’s responsibility to think big and try to write performant code from the ground up.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;~ ciao 🌻 ~&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VwL83ET4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x38oeajb8evt13chcqyx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VwL83ET4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x38oeajb8evt13chcqyx.gif" alt="that's all!" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;if you want to get in touch for a chat, you can follow me on Twitter &lt;a href="https://twitter.com/tk_vishal_tk"&gt;@tk_vishal_tk&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>performance</category>
    </item>
    <item>
      <title>Is Math Essential for Software Developers? - A Short Tutorial on Basic Math</title>
      <dc:creator>TK Vishal</dc:creator>
      <pubDate>Wed, 28 Jul 2021 12:31:25 +0000</pubDate>
      <link>https://dev.to/tkvishal/essential-math-for-developers-and-designers-1ngj</link>
      <guid>https://dev.to/tkvishal/essential-math-for-developers-and-designers-1ngj</guid>
      <description>&lt;p&gt;I've heard many people say that as a developer you don't necessarily need to know math to become a great developer. That is right, I completely agree with that. To become a great developer all that is required to know is your tools, software best practices, and stuff related to this. Even when math is involved, it won't go beyond basic arithmetic. Some fancy npm package will save you from reinventing the wheel.&lt;/p&gt;

&lt;p&gt;but hey...&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  &lt;em&gt;Math is Beautiful!&lt;/em&gt; 🌻
&lt;/h1&gt;
&lt;h6&gt;
  
  
  (once you get it)
&lt;/h6&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn't find the need for me to dive into math(partially because I was bad at it in school). But when I actually put my head into it, The results were definitely worth the time spent.&lt;/p&gt;

&lt;p&gt;Just the basic level of math knowledge can produce some interesting and pretty-looking results in no time.&lt;/p&gt;

&lt;p&gt;I began learning JS only through &lt;a href="https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw"&gt;Coding Train&lt;/a&gt; videos. I got my mind blown away into pieces after watching how basic math can wonders.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can change the way you handle problems in general, you will get a new perspective on ideas when you are trying to animate an element or trying to position elements in a certain way.&lt;/li&gt;
&lt;li&gt;When math is learned correctly or taught correctly, it can be pretty fun too!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alright, I think I convinced you to learn something new today. Let's get started with a basic math concept/function that we see almost everywhere on the internet 🌏 &lt;/p&gt;

&lt;blockquote&gt;
&lt;h6&gt;
  
  
  &lt;em&gt;⚠️Most math explanations in this post are boiled down to very simple terms⚠️&lt;/em&gt;
&lt;/h6&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Trigonometric functions 🧬
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;h6&gt;
  
  
  &lt;em&gt;Don't panic&lt;/em&gt; 😰
&lt;/h6&gt;
&lt;/blockquote&gt;

&lt;p&gt;I will not be trying to explain you &lt;a href="https://www.pleacher.com/mp/mfacts/mobsoh.html"&gt;&lt;em&gt;Soh Cah Toa&lt;/em&gt;&lt;/a&gt; or start all the way from the &lt;a href="https://i.gifer.com/EGzB.gif"&gt;&lt;em&gt;Pythagorean theorem&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;sin and cos are fundamentally simple math functions, if you pass in a value from 0 to 2*PI(360°), you would get a corresponding value on the circumference/outer surface of a circle. The difference between sin and cos is that they are offset by PI/2(90°). Here's a helpful illustration and an &lt;a href="https://codepen.io/tkv/pen/wvdmmbO"&gt;interactive playground&lt;/a&gt; to understand better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_WiYBNB6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnbwzwvtysgda58sfocc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_WiYBNB6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnbwzwvtysgda58sfocc.gif" alt="sin and cos gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// You've got a value that bounces back and forth from -multiplier to +multiplier&lt;/span&gt;
&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offsetValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;multiplier&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Some Applications 🧠
&lt;/h3&gt;

&lt;p&gt;The applications with trig functions are limitless. you want an element that oscillates back and fourth? you want to draw a simple circle? you want an element to follow and look at the mouse position? you want to create an exact simulation of our solar system? trig functions got you covered! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/hokMyu1PAKfJK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/hokMyu1PAKfJK/giphy.gif" alt="unlimited power"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's make a very simple customisable radial menu just using the trig functions!&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;em&gt;use left and right arrows to go step by step&lt;/em&gt;
&lt;/h6&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tkv/embed/poPLOoJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Let's explore each step...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a bunch of circles, they, will be acting as our radial menu items and position them at a defined point.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the cos and sin components to the x and y coordinates of each menu item based on their element index.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// space the menu items equally around the radial menu&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wholeCircle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;menuRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;angleStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wholeCircle&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;numberOfMenuItems&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;menuItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//set each menuItem's position around a circle&lt;/span&gt;
  &lt;span class="nx"&gt;menuItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;xPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;menuCenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xPos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;menuRadius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angleStep&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;menuItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;yPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;menuCenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yPos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;menuRadius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angleStep&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;idx&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;/li&gt;
&lt;li&gt;&lt;p&gt;We are basically done now! Just add a bit of animation to ease in from initial to final positions and voila! you've got a neat looking, highly customisable radial menu in just few lines of code!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;h6&gt;
  
  
  ✨&lt;em&gt;Tip: Try to use the sin and cos functions to modify and animate other properties like size, background color, opacity to get interesting results!&lt;/em&gt;✨
&lt;/h6&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Further Exploration 👨‍🔬
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;h6&gt;
  
  
  &lt;em&gt;These may not be useful for developers, but pretty fun to know about&lt;/em&gt;
&lt;/h6&gt;
&lt;/blockquote&gt;

&lt;p&gt;sin and cos functions/waves can be fundamental building blocks to any type of wave. Decomposing waves into just sin and cos functions is done by a process called &lt;a href="https://www.jezzamon.com/fourier/"&gt;Fourier Transform&lt;/a&gt;. Fourier transforms are the magic behind JPEG and MP3 compression algorithms. They can also produce pretty amazing looking results like these:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y--oIzhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/DVtOinAUQAAsuh8.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--XSSMKYMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/666406258451152896/ROaK4lhC_normal.png" alt="じゃがりきん profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        じゃがりきん
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @jagarikin
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Generative Art. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      22:13 PM - 10 Feb 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=962449509782495232" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=962449509782495232" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=962449509782495232" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;



&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7IWUg5sP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/DjyMKAdU4AEMqAD.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--YfomcxDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1058251495794126848/p6qM3s57_normal.jpg" alt="Jez ⭐ profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Jez ⭐
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/jezzamonn"&gt;@jezzamonn&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/hashtag/gif"&gt;#gif&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/screenshotsaturday"&gt;#screenshotsaturday&lt;/a&gt; &lt;a href="https://t.co/i5mkEI4hWI"&gt;jezzamon.com/selfdraw&lt;/a&gt; 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      21:01 PM - 04 Aug 2018
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1025849287580610560" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1025849287580610560" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1025849287580610560" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I stumbled upon this beautiful side of math while working on &lt;a href="https://exoplanetexplore.vercel.app/"&gt;Exoplanet Explore&lt;/a&gt; for a hackathon. I had lots of fun &lt;code&gt;gotcha&lt;/code&gt; moments while working on it. If you've got anything interesting made with math/generative art with code. Please share them in the comments! I'd love to see them. 🌠&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nwj75Hg5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oas9st7sqozp7gcgqmau.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nwj75Hg5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oas9st7sqozp7gcgqmau.jpg" alt="the more you know"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hey! 👋
&lt;/h3&gt;

&lt;p&gt;This is my very first blog. Any kind of constructive criticism is welcome. If you like this blog, I would love to continue this as a series. ✨&lt;/p&gt;

&lt;p&gt;Find me on &lt;a href="https://twitter.com/Iamteeekay/"&gt;twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
