<?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: Elian Ibaj</title>
    <description>The latest articles on DEV Community by Elian Ibaj (@microbouji).</description>
    <link>https://dev.to/microbouji</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%2F167761%2F633ef943-109f-43d4-baa8-d9a1c8d150b4.jpg</url>
      <title>DEV Community: Elian Ibaj</title>
      <link>https://dev.to/microbouji</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/microbouji"/>
    <language>en</language>
    <item>
      <title>Modern Web Dev - State Management &amp; Data Fetching</title>
      <dc:creator>Elian Ibaj</dc:creator>
      <pubDate>Tue, 11 Jul 2023 20:58:02 +0000</pubDate>
      <link>https://dev.to/microbouji/modern-web-dev-state-management-data-fetching-ejg</link>
      <guid>https://dev.to/microbouji/modern-web-dev-state-management-data-fetching-ejg</guid>
      <description>&lt;p&gt;Oh wow it's been quite some time since I last updated this series. You don't need to read the previous articles, just read the last paragraph from the &lt;a href="https://dev.to/microbouji/intro-285c"&gt;intro to the series&lt;/a&gt; to set your expectations right about what this is.&lt;/p&gt;

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

&lt;p&gt;On the flip side of me taking so long to write this article, coming back to it months after having written some parts was the same as coming back to code you've written weeks ago: nothing made sense anymore. So I made a table of contents while thinking about how to restructure it in a way that makes more sense, and you get that bonus table of contents here for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
UI State

&lt;ul&gt;
&lt;li&gt;Local&lt;/li&gt;
&lt;li&gt;Global&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Server State

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;aside: Data fetching in javascript (fetch API)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Data fetching in react

&lt;ul&gt;
&lt;li&gt;loading, errors, data&lt;/li&gt;
&lt;li&gt;race conditions&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;React query&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Example

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;(serverless) backend aside&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;another aside about REST vs GraphQL vs tRPC&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Code and demo

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;last aside ever: animations&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;In the tradition of past articles, let's start with suggested reading. This is the only one I felt I needed to read to get up to speed with state management:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leerob.io/blog/react-state-management" rel="noopener noreferrer"&gt;https://leerob.io/blog/react-state-management&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One major point to get from that article is that there are different types of state, most importantly two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI state&lt;/li&gt;
&lt;li&gt;Server state&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  UI state&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Further divided into local and global state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local State
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;react docs&lt;/a&gt; have been rewritten since the last time I was writing an article in this series. Whether you're learning react for the first time, or need a quick refresher, that is the perfect excuse to go through all of the new docs site, or, at least, just the managing state part. In my opinion, state &lt;strong&gt;&lt;em&gt;is&lt;/em&gt;&lt;/strong&gt; react, so you'd be doing yourself a favor by learning and understanding it very well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global State
&lt;/h3&gt;

&lt;p&gt;Have you ever felt like quitting programming and becoming a farmer in a place far away isolated from the rest of civilization? If not, now you, too, can experience that feeling by setting yourself on a quest to find the best way to handle global state in react apps.&lt;/p&gt;

&lt;p&gt;To avoid that feeling I suggest you follow the advice from Lee's article above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with local state&lt;/li&gt;
&lt;li&gt;When your app evolves to become like figma such that &lt;em&gt;"... [interacting with one element] affect other elements outside of its "local" state, or where the component is rendered"&lt;/em&gt;, reach for &lt;code&gt;useContext&lt;/code&gt; so you can keep state in one place and read/write to it from anywhere, and maybe &lt;code&gt;useReducer&lt;/code&gt; to make the writing part easier.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oh7b5p8vv4k2onigqk2.png" alt="Illustration of how interacting with the toolbar in Figma affects other parts"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"If optimizing requires a state management library, you can track that metric (e.g. frame rate), measure it, and verify it solves a real problem."&lt;/em&gt; In this case pull for a granular library solving that very specific use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, I particularly liked one of his suggested solutions to "ask your tech lead." If you're a junior dev you shouldn't have to make decisions like this on your own.&lt;/p&gt;

&lt;p&gt;With all that said, if for some reason, you must still follow that route that leads to you in a farm, here's an entry to that rabbit hole that I discovered recently (at least recent as of july 2022 when I wrote about 99% of this blog and then procrastinated on finishing up and publishing it for another year):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://frontendmastery.com/posts/the-new-wave-of-react-state-management/" rel="noopener noreferrer"&gt;https://frontendmastery.com/posts/the-new-wave-of-react-state-management/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Server State (Data Fetching)&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;aside: Data Fetching in JavaScript&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Here's a word I bet you haven't heard in a long time: AJAX. Or maybe you've never even heard it before. But it's what got this whole JS-industrial-complex™ started.&lt;/p&gt;

&lt;p&gt;We all know browsers can make web requests. It's what web browsers do. You type kosovahackersclan.com (okay maybe you don't, but I did... around 2005... a lot) in your address bar and your browser (aka the client, aka the user agent) makes a request on your behalf to that l33t forum's server. The server runs phpBB to get the forum posts out of MySQL, marks them up in HTML and sends it back to you. You want to know if the user named n0thinG responded to your thread asking for some warez? You press the refresh button.&lt;/p&gt;

&lt;p&gt;But what if the JavaScript you already loaded in the page could also send requests on your behalf, without you having to type in a new address, click a link, or hit that refresh button? It could do that in the background (asynchronously). It could do it every second. And it could update the current page's markup with the new response every second without the browser updating the whole page. What would that result in? Infinite warez!!! Or a web page that shows charts of real-time stock prices. Or a web app that shows you a feed of your friends' posts, each one complete with stats of how many likes and comments it got, while simultaneously overloading your sensors with everything it can crunch in one screen from a live chat window to dopamine-inducing red counters of how much engagement your own content got.&lt;/p&gt;

&lt;p&gt;As it turns out, if you're building an app like that, it makes sense to go full Single Page Application mode (get new data in background, only update the relevant parts without refreshing the whole page) and even try an experiment where you throw out the old markup each time you want to make a change and generate completely new markup as a pure function of the new data you got.&lt;/p&gt;

&lt;p&gt;As you know, Facebook did exactly that with React and as they say the rest is history. Now, with the recent one eighty of "server first", this whole way of doing front end engineering might become history in a different way, but that's a topic for my next article in the series.&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript Fetch
&lt;/h4&gt;

&lt;p&gt;So, how do you fetch data in the background from javascript? You use the aptly named Fetch API. Straight from the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch" rel="noopener noreferrer"&gt;MDN page&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://example.com/movies.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty straightforward, especially compared to what we had before. What we had before was another browser API called &lt;code&gt;XMLHttpRequest&lt;/code&gt; and many libraries that wrapped around it to offer cross-browser compatibility and much better APIs.&lt;/p&gt;

&lt;p&gt;The most famous of those libraries, axios, is still widely used today. Now that Fetch is available in all modern browsers, you would think we'd get rid of libraries and just use Fetch directly. And that's what we did for a while. But to come full circle again, new libraries popped up that wrap around the modern Fetch API, and there are again good reasons to use those.&lt;/p&gt;

&lt;p&gt;I will use &lt;a href="https://github.com/sindresorhus/ky" rel="noopener noreferrer"&gt;ky&lt;/a&gt; later on, and you can check its readme to find out about some of those reasons in the list of "benefits over plain &lt;code&gt;fetch&lt;/code&gt;". &lt;a href="https://www.builder.io/blog/safe-data-fetching" rel="noopener noreferrer"&gt;This article&lt;/a&gt;, which recommends another library called wretch, does an even better job at highlighting what's wrong with using Fetch in its most simple form like the MDN example above.&lt;/p&gt;

&lt;p&gt;Here's what safe data fetching with ky would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ky&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://example.com/movies.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data fetching in react
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.robinwieruch.de/react-hooks-fetch-data/" rel="noopener noreferrer"&gt;This article&lt;/a&gt;, which is even linked to by the react docs, is a good place to start reading about data fetching in react. You don't need to get into &lt;em&gt;all&lt;/em&gt; the advanced topics it discusses. What I liked most about it is how it starts as simple as possible like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// you fetch data in `useEffect` and update state with results&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;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;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's wrong with this example? Well, everything, according to recent tech twitter. But here's how I feel about that recent discourse (again, recent in july 2022 when I wrote most of this blog):&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1545699880924053504-337" src="https://platform.twitter.com/embed/Tweet.html?id=1545699880924053504"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1545699880924053504-337');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1545699880924053504&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;But seriously, I might revisit this topic in the next blog about frameworks and rendering strategies. But for now, I'd like to focus on a few fundamental shortcomings of this simple approach that Robin also addresses in his article.&lt;/p&gt;

&lt;h4&gt;
  
  
  loading, error, data
&lt;/h4&gt;

&lt;p&gt;The famous pattern of: loading, error, data. You'll notice this pattern everywhere, whether you're rolling data fetching on your own or using different libraries from React Query to Apollo GraphQL. When you go out of the house remember: Phone, Keys, Wallet. When you do data fetching in react remember: Loading, Error, Data.&lt;/p&gt;

&lt;p&gt;Adding loading to our simple pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;function App() {
&lt;/span&gt;&lt;span class="gi"&gt;+ const [loading, setLoading] = useState(false);
&lt;/span&gt;  const [data, setData] = useState([]);
&lt;span class="err"&gt;
&lt;/span&gt;  useEffect(async () =&amp;gt; {
&lt;span class="gi"&gt;+   setLoading(true);
&lt;/span&gt;    const result = await fetch('url here');
&lt;span class="err"&gt;
&lt;/span&gt;    setData(result);
&lt;span class="gi"&gt;+   setLoading(false);
&lt;/span&gt;  }, []);
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;div&amp;gt;
&lt;span class="gi"&gt;+     {loading ? (
+       Loading ...
+     ) : (
&lt;/span&gt;      {data}
&lt;span class="gi"&gt;+     )}
&lt;/span&gt;    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We already talked about error handling when fetching data with ky. Here's what it looks like when we combine it with state management:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;function App() {
&lt;/span&gt;  const [loading, setLoading] = useState(false);
&lt;span class="gi"&gt;+ const [error, setError] = useState(null)
&lt;/span&gt;  const [data, setData] = useState([]);
&lt;span class="err"&gt;
&lt;/span&gt;  useEffect(async () =&amp;gt; {
    setLoading(true);
&lt;span class="gi"&gt;+   setError(null)
&lt;/span&gt;&lt;span class="gd"&gt;-   const result = await fetch('url here');
-
-   setData(result);
&lt;/span&gt;&lt;span class="gi"&gt;+   try {
+     const data = await ky.get("url here").json()
+
+     setData(data)
+   } catch(error) {
+     setError(error)
+   }
&lt;/span&gt;    setLoading(false);
  }, []);
&lt;span class="gi"&gt;+ 
+ if (error) return (&amp;lt;div&amp;gt;Error: {error.message}&amp;lt;/div&amp;gt;);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;div&amp;gt;
      {loading ? (
        Loading ...
      ) : (
      {data}
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Race conditions
&lt;/h4&gt;

&lt;p&gt;I can't do a better job at explaining this than this &lt;a href="https://react.dev/learn/synchronizing-with-effects#fix-fetching-inside-an-effect" rel="noopener noreferrer"&gt;exercise from the react docs&lt;/a&gt;. If the hash part of the link doesn't work it's the 4th exercise in the challenges section. If you click show solution it explains very simply how a race condition bug happens and how to fix it.&lt;/p&gt;

&lt;p&gt;In our running example it goes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;function App() {
&lt;/span&gt;  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null)
  const [data, setData] = useState([]);
&lt;span class="err"&gt;
&lt;/span&gt;  useEffect(async () =&amp;gt; {
&lt;span class="gi"&gt;+   let cancelled = false
&lt;/span&gt;    setLoading(true);
    setError(null)
    const result = await fetch('url here');
&lt;span class="err"&gt;
&lt;/span&gt;    setData(result);
    try {
      const data = await ky.get("url here").json()
&lt;span class="gd"&gt;-
-     setData(data) 
&lt;/span&gt;&lt;span class="gi"&gt;+     if (!cancelled) setData(data)
&lt;/span&gt;    } catch(error) {
&lt;span class="gd"&gt;-     setError(error)
&lt;/span&gt;&lt;span class="gi"&gt;+     if (!cancelled) setError(error)
&lt;/span&gt;    }
    setLoading(false);
  }, []);
&lt;span class="err"&gt;
&lt;/span&gt;  if (error) return (&amp;lt;div&amp;gt;Error: {error.message}&amp;lt;/div&amp;gt;);
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;div&amp;gt;
      {loading ? (
        Loading ...
      ) : (
      {data}
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Caching
&lt;/h4&gt;

&lt;p&gt;Caching is one of those big topics in computer science. As I was searching for the easiest way to explain caching I came across the slides for a talk on the subject, and I extracted the following excerpt from it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...] The speaker asked the audience “What’s 3,485,250 divided by 23,235 ?” Everyone fell silent. Some people pulled out calculators to do the maths, and finally someone yelled out the answer after a few seconds.&lt;/p&gt;

&lt;p&gt;Then the speaker asked the exact same question again. This time everyone was able to immediately call out the answer.&lt;/p&gt;

&lt;p&gt;This is a great demo of the concept of caching. The initial time-consuming process was done once, then after that, when the same question was asked, the answer was readily available, and delivered much faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ah! Don't you just love these types of explanations? As always though, that's just scratching the surface and it gets more complicated as you go deeper. For once, this is only talking about one kind of caching — and there are &lt;a href="https://aws.amazon.com/caching/" rel="noopener noreferrer"&gt;all kinds of those&lt;/a&gt; for you to easily get lost in whichever way you want. To narrow it down a bit, lets look at a proper definition of caching, this time from wikipedia, and focus on the second part of it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; &lt;strong&gt;the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  Caching of a computational result (memoization)
&lt;/h5&gt;

&lt;p&gt;Throughout this article (and this series) I've advocated being practical, and only learning the minimum that you need to correctly finish the task that is immediately in front of you. But the beauty of memoizing a recursive fibonacci function was just too much for me not to geek out on. I invite you to also channel your inner geek and watch this 4 minute walkthrough of how that works.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hISXxjX6Ino"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I'm always amazed by the huge difference that memoization makes here. Without memoization, every time you'd want to calculate the millionth fibonacci number you'd have to recursively call the function as many times as needed to calculate all the previous fibonacci numbers before it (&lt;code&gt;2*F(1,000,001)-1&lt;/code&gt; times to be exact). With memoization, once you've got the previous results in the cache, it's just one call that returns the sum of the &lt;em&gt;earlier computations&lt;/em&gt; of &lt;code&gt;F(999,999)&lt;/code&gt; and &lt;code&gt;F(999,998)&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Caching of data previously retrieved from an underlying slower storage layer
&lt;/h5&gt;

&lt;p&gt;And to complete our geeking out field trip let's start this part about the second type of caching with this famous video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/9eyFDBPk4Yw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;No, of course you don't &lt;a href="https://gist.github.com/hellerbarde/2843375" rel="noopener noreferrer"&gt;nEeD To kNoW ThEsE NuMbErS As a pRoGrAmMeR&lt;/a&gt;. It's just a cool thing that exists and I thought it would be cool to link to it here.&lt;/p&gt;

&lt;p&gt;Aaaand back to front-end development. How does this all relate to the type of caching we need to worry about daily? Well, this second type of caching is exactly what we must keep in mind when fetching data in our web apps. You can probably imagine how the simplest scenario goes:&lt;/p&gt;

&lt;p&gt;The first time the user needs the data, they're gonna have to make a network request to get it, no way around that. After that though, if they request the data again, and we have good reason to believe the data hasn't changed since the last time they got it, it would be a waste to make a new network request (going to the "underlying slower storage layer") instead of serving a copy we previously stored in their browser (do I need to give you nanseconds to illustrate the difference it makes for the user?).&lt;/p&gt;

&lt;p&gt;And that's the type of caching that we most frequently have to implement as front-end developers. That's how it starts at least. It starts deceptively simple like that. Makes you think "I can code that in five minutes". And you can. I'm sure you can. But you shouldn't. Why? Because it's a dead end. Or more precisely a "death by a thousand cuts". I googled to make sure I'm using that phrase right and here's one explanation that came up: "a slow, painful, demise caused by the cumulative damage of one too many ‘seemingly’ minuscule problems". And here are some of the not so minuscule &lt;a href="https://tanstack.com/query/latest/docs/react/overview" rel="noopener noreferrer"&gt;problems you'll have to think about when implementing caching on your own&lt;/a&gt;, as described by people who (I imagine) think about it daily.&lt;/p&gt;

&lt;p&gt;As you may have gathered by now, unlike for the other problems we encountered with data fetching in react, I won't try to implement a solution for caching on my own and use react query instead. Do I think I have convinced you to do the same and never implement a full caching solution from scratch in a production app? I don't know. A while ago I would've thought that it's one of those things you can only learn by going through it yourself, but the popularity of react query gives me hope that's that not the case anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Query
&lt;/h3&gt;

&lt;p&gt;I just realized that this whole section on data fetching with react reads like a giant ad for react query. Here's a list of problems I had, and TADA, react query solves them all!!! Order now!&lt;/p&gt;

&lt;p&gt;Lol I swear I didn't write it that way on purpose, but it's true, react query solves all the problems I listed above — especially caching; oh my god caching — and that's not even what I liked most about it (kill me now I just realized I'm a react query fanboy).&lt;/p&gt;

&lt;p&gt;What I liked most about it was the declarative way of doing data fetching and how much it simplifies the code. Even in the simple example I'm using the code was simplified to the point of not needing the &lt;code&gt;useEffect&lt;/code&gt; anymore, and the shift from fetching the data and then imperatively updating your state to having the data fetching happen as a result of you managing your local state was visible.&lt;/p&gt;

&lt;p&gt;Speaking of the example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As I mentioned in the beginning, when I was almost done writing this article I reorganized it in a way that I think makes it easier to read. That's why I moved everything that had to do with the example project that I've been working on since the start of this series to its own section here at the end of the article. Everything up to here was theoretical enough that it could (hopefully) benefit anyone wanting to learn about data fetching and state management in react and hasn't read the previous articles in this series. This last part is about how I added data fetching to &lt;a href="https://modern-web-dev-state.netlify.app/" rel="noopener noreferrer"&gt;this website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this section I will discuss a bit about the data fetching and state management part of the example, but before that I had to write three (not-so) short asides about other stuff that I had to do before getting to that part. The first of these was building a backend for the products.&lt;/p&gt;

&lt;p&gt;I left this part at the end of the article, but I still want you to read it though, that's why I will lure you in with this artisanally-crafted-with-my-own-two-hands pepe silvia meme.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwext8fyym9t35kvnmmg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwext8fyym9t35kvnmmg.jpg" alt="Pepe Silvia meme on top of a client server architecture diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;APIs amiright. Client-server architecture. "How the web works" youtube videos. "Describe what happens when I visit google.com" interview questions.&lt;/p&gt;

&lt;p&gt;All good to know, fundamental stuff — but this is 2023.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  (serverless) backend aside
&lt;/h3&gt;

&lt;p&gt;I've mentioned in previous articles that I like "serverless" for many reasons; and one of them is how it changed our way of thinking about the backend. Whereas before we would think of the backend as one big monolithic box (literally called a box to refer to the single machine running everything like the web server, the database server etc.), our thinking now has shifted to picking and choosing only the services we need. Your app needs authentication? Find out who offers authentication as a service. You need a database? Find a serverless solution for that. Need a place to run some code, storage for images, a proxy for caching, load-balancing or any of the other traditional backend needs? Same solution in all cases.&lt;/p&gt;

&lt;p&gt;So, what services do we need for our &lt;a href="https://ui-challenge-html-css.netlify.app/" rel="noopener noreferrer"&gt;sample website&lt;/a&gt;? I'm going to use the products section to learn about data fetching and state management. So we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a database to store the products info, and&lt;/li&gt;
&lt;li&gt;a place to store the product images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a quick overview of how I think of some of the most popular solutions that fill our database needs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon AWS&lt;/strong&gt;: The biggest of the original cloud computing platforms (alongside Microsoft's Azure). Now, I've talked about different learning styles before, and IME I don't find the AWS docs to be as bad as everyone says. IMO if you're learning about backend concepts for the first time you're gonna have a steep learning curve ahead of you no matter which serverless platform you choose. But that's not a popular opinion. In fact the opposite (that AWS is very hard to learn) is so wide-spread that it has at least two important implications:

&lt;ol&gt;
&lt;li&gt;You can make a career by learning it well and earning certifications to prove it.&lt;/li&gt;
&lt;li&gt;A plethora of other services have proliferated who use AWS under the hood and their entire business model is based on you paying them money to not have to learn AWS yourself.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Firebase&lt;/strong&gt;: Google's cloud solution, also offering virtually everything you might need. For some reason not as well respected in the industry as AWS, so instead of making a career in big tech companies you could get good at building fullstack apps with firebase and make a solo freelancing career.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt;: They describe it as "The Open Source Firebase Alternative" and it fully delivers on that promise and more. Literally, if you're thinking of starting a new project with google firebase, I can't think of a single good reason why you shouldn't go with supabase instead. Not just less vendor lock-in but also just better tech like the postgress database most of supabase is built around (they won't shut up about it though, kinda giving me some linux-flavored gatekeeping vibes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planetscale&lt;/strong&gt;: This is the perfect example of what I said above about only picking the services you need. A small company dedicated to a single purpose: offering the best serverless MySQL database out there. And in my experience it delivers — from getting you started in literally two minutes, to all the scaling needs you might have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some CMS like Strapi&lt;/strong&gt;: Or, you know, WordPress. Or shopify. This is the real-world. The above solutions only apply if you're building the back-end yourself. If you're just working on the front-end of a furniture shop like this one, the back-end will be some CMS or e-commerce solution, and you'll only care about the REST API it provides.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Speaking of which:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  another aside about REST vs GraphQL vs tRPC
&lt;/h3&gt;

&lt;p&gt;I loved the idea of GraphQL when it came out, especially what it meant for frontend developers. Yet, as one ex-famous javascript guru from the era of javascript gurus, who shall remain unnamed here, used to say: "Who is the first group to go against things that make developers' lives easier? Developers".&lt;/p&gt;

&lt;p&gt;Now I know there are many reasons as to why one technology becomes successful or not, but I can't help but think of the big role that cultural reasons play in it.&lt;/p&gt;

&lt;p&gt;TypeScript's marketing? "Bringing &lt;strong&gt;strong, enterprise-class&lt;/strong&gt; types to this &lt;em&gt;weakly-typed, toy&lt;/em&gt; language finally making it serious enough for you to write it while wearing a suit and tie". No wonder brogrammers flocked to it (don't get me wrong, I love typescript; just commenting on the high number of people who would praise it online, many of them not even javascript programmers.)&lt;/p&gt;

&lt;p&gt;GraphQL's — a similar technology to typescript — marketing? "Empowering frontend developers". Get outta here! REST is trad and lindy enough for me, don't need this shiny new thing from these facebook hipsters always reinventing the wheel.&lt;/p&gt;

&lt;p&gt;All this rant was just to say that the state of API protocols is in a kind of limbo after GraphQL was supposed to replace REST and it didn't. REST is still the most widely used one. GraphQL is used in many big companies. And tRPC is the new kid on the block.&lt;/p&gt;

&lt;p&gt;This article is targeted at juniors, probably preparing to get their first dev job, and I can't tell you for sure that my decision to only use REST examples is befitting towards that goal. If I was a hiring manager and my company used GraphQL, I would still hire a junior dev who only knows REST and expect them to learn GraphQL on the job. But I'm not a hiring manager. I know there are a few hiring managers who think like me and many others who don't. So, depending on your job applying strategy, you'll have to decide where you draw the line when it comes to learning technologies used by the companies you want to apply to, before applying.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/modern-web-dev-state" rel="noopener noreferrer"&gt;CODE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modern-web-dev-state.netlify.app/" rel="noopener noreferrer"&gt;DEMO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two quick things that may distract you from the data fetching part:&lt;/p&gt;

&lt;p&gt;The AWS link. Long story short, I used Amazon's AWS from the options above to setup the backend. Amazon S3 to store the images, DynamoDB for the product info, and API Gateway for the REST API. Ultimately, as a frontend developer, that REST endpoint is the only thing you'll care about.&lt;/p&gt;

&lt;p&gt;The other thing which may confuse you in the code is the animation which is now being done with framer, so I have to write my last aside ever about animations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  last aside ever: animations
&lt;/h3&gt;

&lt;p&gt;Okay, so, super quick. Animation is hard. Animation in react is even harder. Learn &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;framer motion&lt;/a&gt; and learn it well to survive if you have to work with animations in react.&lt;/p&gt;

&lt;p&gt;I made the animation work exactly as I wanted and as I had described in the first article, but it cost me EVERYTHING. Okaybe maybe not, but I had to learn framer motion and that's what it felt like.&lt;/p&gt;

&lt;p&gt;Looking back at the code (this is perfect cause I did the animation work quite a while ago but didn't write this part of the blog to explain it until now) it's a bit verbose, VS code even shows me that this &lt;code&gt;exitBeforeEnter&lt;/code&gt; property that I had to use to make the animation work right in a few edge cases is now deprecated. Other than that it's pretty readable I think. I like that the transition properties are now in javascript and located close to the animation components. I also like alot the truly(™) declarative style over what we had going on before with React Transition Group.&lt;/p&gt;

&lt;p&gt;Would I go back and try to make it work without the deprecated property for a hobby blog/side project/whatever this is? Hell naw.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's all the data fetching code with react query:&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;function&lt;/span&gt; &lt;span class="nf"&gt;ProductsSection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onChangeFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setActiveFilter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activeFilter&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="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setActiveFilter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;prev&lt;/span&gt;&lt;span class="p"&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://c300bbvloc.execute-api.us-east-1.amazonaws.com/dev/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;activeFilter&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="nx"&gt;isPreviousData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activeFilter&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="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ky&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;keepPreviousData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="nx"&gt;smPadding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;mdPadding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;horizontal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductsHeading&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductsTitle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ProductsTitle&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductsFilters&lt;/span&gt;
          &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;onChangeFilter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChangeFilter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ProductsFilters&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ProductsHeading&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductsList&lt;/span&gt;
          &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isPreviousData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Container&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, at the risk of sounding like an ad for react query, look at how much code we removed from what we had &lt;a href="https://github.com/microbouji/modern-web-dev-state/blob/7e94de0f1040264cd3ef561f09b613e24bad876c/src/ProductsSection.jsx" rel="noopener noreferrer"&gt;before&lt;/a&gt;. We got rid of &lt;code&gt;useEffect&lt;/code&gt; entirely and that's always a win!&lt;/p&gt;

&lt;p&gt;I pretty much said all I had to say about react query in the previous sections. One funny thing though is something I had to do to get animations to work with it.&lt;/p&gt;

&lt;p&gt;The way the loading animation works is it expects only the products that are added/removed to be actually added/removed to the markup. If you go let's say from 'All' to 'Tables' it should go from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Table 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Chair 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Table 2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;directly to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Table 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Table 2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's how it knows to only animate out the "Chair 1" div. But with react query's default behaviour it goes from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Table 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Chair 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Table 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this for a second:&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="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then finally to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Table 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Table 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see that will mess with our animation because all the product divs would be gone for a moment before the other ones are loaded. Looking through the docs, I tried to fix it with &lt;code&gt;initialData&lt;/code&gt; and &lt;code&gt;placeholderData&lt;/code&gt; to no success. My first solution then was this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductsList&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getQueryData&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;activeFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All&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="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked, but then, spending one friday evening, as one does, watching an episode of "Learn with Jason" with Dominik Dorfmeister, I realized all I had to do was add &lt;code&gt;keepPreviousData: true,&lt;/code&gt; to the &lt;code&gt;useQuery&lt;/code&gt; call. By the end I also added &lt;code&gt;staleTime: 1000 * 60 * 5,&lt;/code&gt; to ease my OCD and make sure it behaves like a proper legacy website not some fancy always up to date app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Whew! I can't believe it's done. Finished. Complete. And congrats to you if you read all the way to here. If you didn't, and just scrolled here while skimming quickly, that's fine too, no judgement from me. Whatever brings joy. For me it surely brought lots of joy to finally publish this after such a long time, and I can't wait to get started on the next article. That will most probably focus on deployments, rendering strategies, and how different frameworks especially Next.js handle these. Looking forward to that! For now here are my short conclusions about data fetching and state management in react:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I love the movement towards specific state management libraries for specific needs. Feels like a sign of maturity and a good middle ground after the pendulum swang from redux sagas to "just use react context".&lt;/li&gt;
&lt;li&gt;Use react query. That's it. That's the second conclusion.&lt;/li&gt;
&lt;li&gt;Animations, not even once. Just kidding... Unless? No, but seriously, this one was on me. I approached animations with the seriousness that a backend developer approaches frontend work and found myself in the same surprising position. If you're serious about animations in react, getting good with framer motion can be very rewarding.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>state</category>
      <category>fetch</category>
    </item>
    <item>
      <title>Modern Web Dev - UI - CSS-in-JS</title>
      <dc:creator>Elian Ibaj</dc:creator>
      <pubDate>Fri, 26 Nov 2021 23:46:48 +0000</pubDate>
      <link>https://dev.to/microbouji/modern-web-dev-ui-css-in-js-d3a</link>
      <guid>https://dev.to/microbouji/modern-web-dev-ui-css-in-js-d3a</guid>
      <description>&lt;p&gt;CSS in JS! I feel like everything that can be said about the topic (and then some more) has already been said. If you've missed it all, consider yourself lucky. For context, though, I'll only give three links here.&lt;/p&gt;

&lt;p&gt;The original presentation of the concept to the wide public (&lt;a href="https://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html"&gt;slides here&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/116209150" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;An article that does a very good, emotionless, summary of all the backlash it got:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://css-tricks.com/the-differing-perspectives-on-css-in-js/"&gt;https://css-tricks.com/the-differing-perspectives-on-css-in-js/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And finally, a great, principles-based, article that will help you stay sane while trying to keep up with all the new solutions that come up in this space every day:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://css-tricks.com/a-thorough-analysis-of-css-in-js/"&gt;https://css-tricks.com/a-thorough-analysis-of-css-in-js/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If that last article did its job correctly, you'll now have the strength to resist clicking this, for example:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nUMh68GN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/ExYVRjtVgAIA3Tq.jpg" alt="unknown tweet media content"&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--vPA7btCM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/754886061872979968/BzaOWhs1_normal.jpg" alt="🧁🍨 Mark Dalgleish profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        🧁🍨 Mark Dalgleish
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/markdalgleish"&gt;@markdalgleish&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;
      👇 NEW CSS-IN-JS LIBRARY ALERT!&lt;br&gt;&lt;br&gt;🧁 vanilla-extract&lt;br&gt;&lt;br&gt;🔥 Zero-runtime Stylesheets-in-TypeScript&lt;br&gt;✨ Minimal abstraction over standard CSS&lt;br&gt;🦄 Works with any front-end framework&lt;br&gt;🌳 Locally scoped classes + CSS Variables&lt;br&gt;🎨 High-level theming system&lt;br&gt;&lt;br&gt;&lt;a href="https://t.co/V4P4BYRDrE"&gt;github.com/seek-oss/vanil…&lt;/a&gt; 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      05:18 AM - 26 Mar 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1375316120530382848" 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=1375316120530382848" 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=1375316120530382848" 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;You still with me? Good, let's get started by:&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrapping react
&lt;/h2&gt;

&lt;p&gt;Contrary to what the ending of the last article might suggest, I've been eager to get rid of as much tooling as possible in development as soon as I saw it as a &lt;a href="https://github.com/gothinkster/web-components-realworld-example-app/wiki/Converting-existing-project-to-use-ES-modules-in-production"&gt;real possibility&lt;/a&gt;. I don't hate tools (instrumentum, for those of you fluent in latin) either, though.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does a misogynist say when called out on their misogyny? I can't be a misogynist, I have a daughter/wife. What does a misoinstrumentist, like me, say when called out on their misoinstrumenty? I can't be a misointrumentist, I have contributed to Babel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It shouldn't come as a surprise, then, that I'll use &lt;a href="https://vitejs.dev/guide/#scaffolding-your-first-vite-project"&gt;vite&lt;/a&gt; to quickly get this react project setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also, last time I did &lt;code&gt;npx create-react-app&lt;/code&gt; it created, let me check, ah yes: 41,732 items, totaling 250.7 MB. I've had one SSD and one HDD die of bad sectors in the last two years. Coincidence? I think not!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, yeah, that's the setup — &lt;code&gt;npm init vite@latest&lt;/code&gt; and follow the prompts to start a react project without typescript. Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;what-you-named-your-project
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to add styled-components, &lt;code&gt;npm install styled-components&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Last part of the setup was &lt;a href="https://github.com/microbouji/ui-challenge-css-in-js/commit/3259c3c3e284099065a23a4dcd8576a07582bb13"&gt;deleting&lt;/a&gt; the unnecessary stuff before adding the base styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Base styles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-css-in-js/tree/base-styles"&gt;CODE - Base styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://base-styles--ui-challenge-css-in-js.netlify.app/"&gt;DEMO - Base styles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the last article, I spent so much time with "suggested reading" before writing the first line of code. For this one, I'll take the opposite approach: do as little reading as necessary to get started, and then go read more when I get stuck. This was my modus operandi when I was working professionally, and I assume it is so for most people.&lt;/p&gt;

&lt;p&gt;I read from the &lt;a href="https://styled-components.com/docs/basics#getting-started"&gt;Getting Started&lt;/a&gt; to the ("to the" == including; english is hard) &lt;a href="https://styled-components.com/docs/basics#coming-from-css"&gt;Coming from CSS&lt;/a&gt; parts of the styled-components docs and started converting the base styles.&lt;/p&gt;

&lt;p&gt;I started by renaming &lt;code&gt;index.css&lt;/code&gt; and replacing the few rules there with my reset styles as well as the google font &lt;code&gt;@import&lt;/code&gt;. Keeping this as a CSS file is fine: we don't need &lt;a href="https://styled-components.com/docs/api#createglobalstyle"&gt;&lt;code&gt;createGlobalStyle&lt;/code&gt;&lt;/a&gt; as we don't need theming or template literal interpolations for these simple CSS rules.&lt;/p&gt;

&lt;p&gt;I modified &lt;code&gt;Todo&lt;/code&gt; and &lt;code&gt;Container&lt;/code&gt; a bit to make them more flexible.&lt;/p&gt;

&lt;p&gt;The rest is straightforward: almost a one-to-one correlation of old classes to new components. I liked the collocation of &lt;code&gt;@media&lt;/code&gt; rules with the rest of the styles for the component. I changed the &lt;code&gt;--body-padding&lt;/code&gt; css variable into a &lt;code&gt;bodyPadding&lt;/code&gt; js variable. I don't know why I did that.&lt;/p&gt;

&lt;p&gt;For including images, I'm not a big fan of webpack-style importing of assets to get their URL. I was happy to find out that vite also allows the most common alternative approach: a "public" folder where you can put all your images in and have them reachable from anywhere in your code with absolute URLs. That's what I did for hero img's &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;srcSet&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-css-in-js/tree/nav"&gt;CODE - Nav&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nav--ui-challenge-css-in-js.netlify.app/"&gt;DEMO - Nav&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There isn't much to say about converting the navigation styles to styled-components, and that's a very good thing to say about styled-components. Everything was easy to move over, including transition animations and complex CSS selectors, pseudo-selectors, and whatever this is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu-visible&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also had some JavaScript in &lt;code&gt;nav.js&lt;/code&gt; to toggle the menu and search input on and off. This is not a React tutorial, but just one quick observation about a subtle change that happens when you port the code to react:&lt;/p&gt;

&lt;p&gt;Doing&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="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&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;menuVisible&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu-visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu-visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of&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="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&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;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;menu-visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means we're no longer relying on HTML to tell whether the menu is visible or not. Our only source of truth for that now is the &lt;code&gt;menuVisible&lt;/code&gt; state. I'm not pointing this out to say that I'm some genius developer who anticipated this. I only noticed it after I tried to do it with &lt;code&gt;.toggle()&lt;/code&gt; first and it didn't work (the first time &lt;code&gt;useEffect&lt;/code&gt; ran on mount it was toggling the class on, and then, when the button was clicked, setting &lt;code&gt;menuVisible&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, it was toggling it off).&lt;/p&gt;

&lt;p&gt;It was a nice example of react making it easy for you to almost accidentally fall into doing things right (pit of success?).&lt;/p&gt;

&lt;h2&gt;
  
  
  Showcase
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-css-in-js/tree/showcase"&gt;CODE - Showcase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://showcase--ui-challenge-css-in-js.netlify.app/"&gt;DEMO - Showcase&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, isn't it ironic that as soon as I make the case for not relying on HTML as a source of truth, I decide to go ahead and do just that for the showcase? Sure, I could've rewritten the slider logic in an idiomatic way for react, but that &lt;code&gt;IntersectionObserver&lt;/code&gt; was my baby!&lt;/p&gt;

&lt;p&gt;Seriously, though, going the &lt;a href="https://reactjs.org/docs/uncontrolled-components.html"&gt;uncontrolled component&lt;/a&gt; way made more sense to me here. Let's quickly go through the code in &lt;code&gt;Slider.jsx&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note on code organization: there isn't any 😝. I didn't make different folders for components and started with one file per component instead (vite used &lt;code&gt;jsx&lt;/code&gt; extensions and I went with that). Still, even with region folding of code in the editor, &lt;code&gt;Showcase.jsx&lt;/code&gt; got too long even for me, so I turned that into a presentation component, and moved the core styling and functionality into &lt;code&gt;Slider.jsx&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few refs keep track of the important dom nodes: for the slider itself, the ref is set directly with &lt;code&gt;&amp;lt;StyledSlider ref={slider}&amp;gt;&lt;/code&gt;; the &lt;code&gt;useEffect&lt;/code&gt; callback, which runs only after the first render, gets the first and last slide nodes from the slider ref with standard DOM properties &lt;code&gt;firstChild&lt;/code&gt; and &lt;code&gt;lastChild&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That same callback also initializes the &lt;code&gt;IntersectionObserver&lt;/code&gt;. All it does, when an observed slide "intersects" 50% with the parent slider (&lt;code&gt;threshold: 0.5&lt;/code&gt;), is set the &lt;code&gt;currentSlide&lt;/code&gt; state to that slide's dom node. With that in place, implementing the disabled state and prev/next functions of the buttons becomes trivial.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: Rambling ahead. Remember how I said in the intro to this series that I'll try to keep rambling to a minimum? Well, I failed here. Feel free to skip until the next section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is one bug? in chrome, though, which stops &lt;code&gt;scrollIntoView&lt;/code&gt; dead in its tracks. I set the threshold to 0.5 to make sure prev/next buttons get the disabled attribute as the last slide is halfway in. For whatever reason, though, chrome was fine with me doing &lt;code&gt;btnPrev.disabled = true;&lt;/code&gt;, but it's not fine with React doing it. As you know, all we can do in react is set &lt;code&gt;disabled={currentSlide === firstSlide.current}&lt;/code&gt; and let react update the DOM however and whenever it sees fit. Well, however react is doing it, chrome doesn't like it one bit — if you click next and then previous (IMPORTANT: without scrolling the page at all in between the clicks, otherwise it works fine), as soon as the first slide comes halfway through, and the button is disabled, chrome stops the smooth scrolling.&lt;/p&gt;

&lt;p&gt;To be honest, this whole implementation of the carousel as not a carousel is a bit flimsy, I'll admit. Its strongest point, the fact that it uses a very light touch, going with the grain of the &lt;a href="https://www.robinrendle.com/notes/scrolljacking/"&gt;perennial design pattern that is scrolling&lt;/a&gt;, instead of against it, is also its weakness because of differing browser and OS implementations. There is, for example, another bug (also found in the HTML &amp;amp; CSS version from the first blog) in firefox when you tab through the slides.&lt;/p&gt;

&lt;p&gt;Nonetheless, I'm keeping it, not because it's hard to fix, but &lt;del&gt;because IntersectionObserver is my baby&lt;/del&gt; in an aspirational way (I wanted to write aspiration "towards a better web", but I think I threw up a little in my mouth).&lt;/p&gt;

&lt;p&gt;Last thing about the showcase implementation in react: did you maybe wonder what that &lt;code&gt;cloneElement&lt;/code&gt; is doing in line 241? That whole acrobatics is just so we don't have to pass an &lt;code&gt;id&lt;/code&gt; prop to each slide in Showcase.jsx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Slider&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Slide&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Lamp"&lt;/span&gt; &lt;span class="na"&gt;img=&lt;/span&gt;&lt;span class="s"&gt;"lamp"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"lamp photo"&lt;/span&gt; &lt;span class="na"&gt;link=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/Slide&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/Slider&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worth it? I don't know; the things we do for &lt;del&gt;love&lt;/del&gt; good API design. &lt;/p&gt;

&lt;h2&gt;
  
  
  Products
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-css-in-js/tree/products"&gt;CODE - Products&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://products--ui-challenge-css-in-js.netlify.app/"&gt;DEMO - Products&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Things had been going really smoothly with styled-components so far, so I decided to spice it up a bit by learning about &lt;a href="https://reactcommunity.org/react-transition-group/"&gt;React Transition Group&lt;/a&gt;. There really isn't much there, but for some reason, it wasn't clicking for me at all. Maybe I was tired, maybe I was distracted by the thought of React 18 being in beta now.&lt;/p&gt;

&lt;p&gt;Anyway, I decided to simply convert the existing animation from the HTML and CSS version to styled-components and react transition group components for now, and do a full example with loading animation and data fetching in a future article. With tailwindcss about to rewrite their documentation for the v3 release, it's very probable that I'll write that article next, before the one on tailwind. And if I ever want an excuse to not write that article either, I could always wait on Suspense for data fetching to be released...&lt;/p&gt;

&lt;p&gt;Here's how our simple animation works:&lt;/p&gt;

&lt;p&gt;When a new filter is selected, fade out ALL currently shown products. When the fade-out transition ends, fade in just the products that match the filter.&lt;/p&gt;

&lt;p&gt;This was the procedural version:&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;function&lt;/span&gt; &lt;span class="nx"&gt;displayProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;faded-out&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nx"&gt;productsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitionend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;All&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;setTimeout&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;faded-out&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&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="na"&gt;once&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bubbling &lt;code&gt;transitionend&lt;/code&gt; event, fired on the parent products list element once, controls the fading-in of new products. To account for the fact that you can't animate from &lt;code&gt;display: none&lt;/code&gt; to &lt;code&gt;display: block&lt;/code&gt;, it removes the &lt;code&gt;hidden&lt;/code&gt; class first, and then, a moment later (with the asynchronous &lt;code&gt;setTimeout(() =&amp;gt; {}, 0)&lt;/code&gt;, removes the &lt;code&gt;faded-out&lt;/code&gt; class too which transitions the opacity back from 0 to 1.&lt;/p&gt;

&lt;p&gt;Here's the react version:&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ProductsList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;listFadeOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setListFadeOut&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setListFadeOut&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;products&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Transition&lt;/span&gt;
      &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;listFadeOut&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onExited&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="nx"&gt;setListFadeOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;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;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StyledProductsList&lt;/span&gt;
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;live&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;polite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;relevant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;additions removals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;state&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TransitionGroup&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;null&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;products&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Transition&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;timeout&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;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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;state&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="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Transition&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;))}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TransitionGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/StyledProductsList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Transition&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a new filter is selected (new products received from parent, monitored in &lt;code&gt;useEffect(() =&amp;gt; {}, [products])&lt;/code&gt;), the first &lt;code&gt;&amp;lt;Transition&amp;gt;&lt;/code&gt; component fades out the products list component itself. Not the same effect as fading out all products individually, but close enough. As soon as it fades out, it fades back in (&lt;code&gt;onExited={() =&amp;gt; setListFadeOut(false)}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;TransitionGroup&amp;gt;&lt;/code&gt; delays the appearing/disappearing of individual products using the same &lt;code&gt;timeout&lt;/code&gt; as the fade-out effect of the products list. This is the equivalent of the &lt;code&gt;.hidden&lt;/code&gt; class from the vanilla js version. There's no animation in the styling of the &lt;code&gt;StyledProduct&lt;/code&gt; component, just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;({&lt;/span&gt; &lt;span class="err"&gt;state&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;'entering'&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;'none'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'flex'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;And, as is tradition, here's the full demo and code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-css-in-js"&gt;CODE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ui-challenge-css-in-js.netlify.app/"&gt;DEMO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No conclusions :) As I said in the beginning, I feel like everything has already been said about CSS-in-JS. Here's the &lt;a href="https://css-tricks.com/a-thorough-analysis-of-css-in-js/"&gt;link&lt;/a&gt; to the most important article from the top again.&lt;/li&gt;
&lt;li&gt;What I also wrote at the beginning of the article, near the setup part, and then deleted, was a full-on rant against tooling and how we bring a whole class of problems upon ourselves with so much tooling in development. I deleted it because I thought it was too harsh, and then... I spent half a day trying to figure out why Intellisense on VS Code was suddenly so slow. I won't turn this into a rant again, just letting you know that it turned out to be the typescript types library for styled-components. Excluding styled-components from &lt;a href="https://www.typescriptlang.org/tsconfig#type-exclude"&gt;typeAcquisition&lt;/a&gt; in a &lt;code&gt;jsconfig.json&lt;/code&gt; file did... nothing. So I guess you'll have to turn type acquisition off from the settings if the slow autocomplete becomes too much to handle.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>cssinjs</category>
      <category>styledcomponents</category>
    </item>
    <item>
      <title>Modern Web Dev - UI - HTML &amp; CSS</title>
      <dc:creator>Elian Ibaj</dc:creator>
      <pubDate>Tue, 16 Nov 2021 23:01:08 +0000</pubDate>
      <link>https://dev.to/microbouji/ui-html-css-5k2</link>
      <guid>https://dev.to/microbouji/ui-html-css-5k2</guid>
      <description>&lt;p&gt;As I was wrapping my head around everything I had to review about building web user interfaces in 2021, I came across this "I Design, You Build" challenge here on DEV:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/zernonia" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F400444%2F8626e93e-1c07-44f2-8eab-1f0bbd87bfed.jpeg" alt="zernonia"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/zernonia/i-design-you-build-frontend-challenge-3-gda" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;I Design, You Build! - Frontend Challenge #3&lt;/h2&gt;
      &lt;h3&gt;Zernonia ・ Oct 14 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#idesignyoubuild&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Joy sparked. Challenge accepted! With a twist — I'll use this same design to build the website multiple times with all the different technologies that I want to explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML &amp;amp; CSS&lt;/li&gt;
&lt;li&gt;CSS-in-JS (probably react &amp;amp; styled-components)&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Component library (probably Chakra UI)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the intro to this blog series, I imagined I would only write one article about UI comparing all these technologies. But after finishing just the HTML &amp;amp; CSS part, it's obvious they each need their own blog. I will preface them all with "UI -" like I've done for this first one.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML &amp;amp; CSS like it's 2009
&lt;/h2&gt;

&lt;p&gt;Except no. We now have very good browser support for Flex and Grid layouts — it would be a shame not to use them. Wait how do you use them for building full responsive pages again? Is mobile-first still the thing to do by the way? To soothe the sudden existential dread caused by the influx of these questions, I read this from start to finish:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.smashingmagazine.com/2018/02/media-queries-responsive-design-2018/" rel="noopener noreferrer"&gt;https://www.smashingmagazine.com/2018/02/media-queries-responsive-design-2018/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works. I mean, the best thing would've been a recording of this &lt;a href="https://noti.st/rachelandrew/4AtjON" rel="noopener noreferrer"&gt;talk&lt;/a&gt; by the author, but such a recording doesn't seem to exist on the internet yet, so the article will have to do.&lt;/p&gt;

&lt;p&gt;Okay so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;flexbox and grid are at the core of modern responsive web&lt;/li&gt;
&lt;li&gt;mobile-first still makes sense but breakpoints should be set where the design breaks visually instead of actual device widths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a video I watched to review what I know about CSS Grid:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SPFDLHNm5KQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Everyone's learning style is different, but for me, I remember I put that video at 1.5x playback speed, and it captured my attention long enough to watch to the end. After that, when you run into specific problems while building something with grid (as you will), &lt;a href="https://gridbyexample.com/" rel="noopener noreferrer"&gt;gridbyexample.com&lt;/a&gt; will have your answers.&lt;/p&gt;

&lt;p&gt;As for flexbox, I don't remember all the different places I've learned it from, but if this doesn't make sense to you, it probably means you should review one of the many good resources on flexbox out there:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1281339741682753542-920" src="https://platform.twitter.com/embed/Tweet.html?id=1281339741682753542"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1281339741682753542-920');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1281339741682753542&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;While we're on the prerequisites part of this blog, do you know what else is required reading? Accessibility. Yeah, I know you plan on learning about accessibility... someday. And I can't blame you entirely, to be honest — putting the responsibility of making web browsers work correctly with assistive technology entirely on developers, feels akin to fighting climate change with paper straws. Still, there is a grain of truth in both of those outwardly absurd situations: just like the fight against climate change will most probably require some changes in consumer behavior, accessibility in its current form (WAI-ARIA, etc.) is necessary until we have better solutions to the problem of inaccessible technology.&lt;/p&gt;

&lt;p&gt;Where do you start? This awesome introduction, right here:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/z8xUCzToff8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And follow that with this practical one:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cOmehxAU_4s"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And here's the last piece of reading I'll suggest before we start working on the challenge. This is the most fun to read, though — it's an interactive guide about the basics of CSS animations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.joshwcomeau.com/animation/css-transitions/" rel="noopener noreferrer"&gt;https://www.joshwcomeau.com/animation/css-transitions/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright, so with our refreshed knowledge of responsive web design with flex and grid, accessibility, and animations, we are ready to jump right in and:&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Challenge
&lt;/h2&gt;

&lt;p&gt;Here's a first look at the design:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs16olmict2dyg46kdysu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs16olmict2dyg46kdysu.jpg" alt="Screenshot of website's desktop and mobile designs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've highlighted three important components in the desktop design: the top navigation bar, the products listing component which is a grid of items you can filter, and the showcase/slider/carousel.&lt;/p&gt;

&lt;p&gt;We'll cover UI component libraries later in this series, but we must take a first look at them now as case studies to &lt;del&gt;steal code&lt;/del&gt; copy best practices from. UI components, like the three that we have in our design, are very common patterns, that are implemented again and again in all kinds of websites and apps. It makes sense to get a bit more familiar with how others are doing that.&lt;/p&gt;

&lt;p&gt;Ten years ago, that would have meant reading what some distinguished designer had to say about it in his (yes, the distinguished designer was a he) personal blog. Today, a good place to start would be the most popular design systems/component libraries. Of course, you should still read widely, listen to podcasts, and consume all kinds of content you like, but as a culmination (usually — not looking to start any debates here) of current best practices, popular component libraries are a good place to start.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://material.io/design/introduction" rel="noopener noreferrer"&gt;Material Design&lt;/a&gt; is the best Google has to say about the subject. &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt; is a truly collaborative project built under the watchful eyes of the open-source community. &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; suffers from the plague of having once been uber-popular, but is, in its current fifth version, an invaluable mix of best practices and real-world experience. We'll look at these libraries, and many other sources, to learn about the three components we identified.&lt;/p&gt;
&lt;h3&gt;
  
  
  Base Styles
&lt;/h3&gt;

&lt;p&gt;I've separated each component into its own git branch, so that you can inspect them in isolation, without getting distracted by unrelated code. The master branch contains all of them put together.&lt;/p&gt;
&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-html-css/tree/base-styles" rel="noopener noreferrer"&gt;CODE - Base Styles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://base-styles--ui-challenge-html-css.netlify.app/" rel="noopener noreferrer"&gt;DEMO - Base Styles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the others are components one, two, and three, this is like component zero? While writing the base styles for the page, and the two simple sections that I didn't circle in red, we also have to take some things into consideration as they come up, that we haven't covered in the prerequisites section above. It's very easy to fall into deep rabbit holes through each of these topics, though, so I'll keep it super short and give no "further reading" links at all.&lt;/p&gt;
&lt;h4&gt;
  
  
  CSS Units and (Responsive) Typography
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;em&lt;/code&gt;s everywhere you'd use pixels before (except for stuff like border widths, or when you explicitly don't want something to change). They work well with a custom font size in the browser settings; make it easier to work with padding and generally let you worry only about the &lt;code&gt;font-size&lt;/code&gt; of elements and have spacing adapt to it.&lt;/li&gt;
&lt;li&gt;Manually set &lt;code&gt;font-size&lt;/code&gt;s at different breakpoints to what looks good at that size. We'll have three main breakpoints: small - under &lt;code&gt;48em&lt;/code&gt;, medium - up to &lt;code&gt;80em&lt;/code&gt;, and large - &lt;code&gt;80em&lt;/code&gt; and above. "Medium" and "large" will basically share the same desktop design but with font sizes and spacing adjusted.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Responsive Images
&lt;/h4&gt;

&lt;p&gt;Doing art direction and handling different resolutions by hand, for a large website with dynamic content, is practically impossible. You will definitely need to reach for some images service like Cloudinary.&lt;/p&gt;

&lt;p&gt;In our proof of concept here, I exported two sizes of big images like the hero image and used &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; to serve the most appropriate one. I didn't export a separate file for the cropped mobile version but achieved the crop effect with &lt;code&gt;object-fit&lt;/code&gt; and &lt;code&gt;object-position&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Other
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Pixel-perfect-ish. I haven't gone through extra effort to make it pixel-perfect, but for the most part, except for where the design allows more flexibility, sizes and spaces are the same as design.&lt;/li&gt;
&lt;li&gt;Modern browsers only. And even then, since this is pure HTML and CSS with no build step at all, I haven't even added browser prefixes that would normally be added by a tool like autoprefixer.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;
  
  
  Main Navigation Bar
&lt;/h3&gt;

&lt;p&gt;Probably the most ubiquitous piece of UI in websites. It has always been there in almost all websites, evolving with the time from a &lt;code&gt;dl&lt;/code&gt; with links in the &lt;a href="http://info.cern.ch/hypertext/WWW/TheProject.html" rel="noopener noreferrer"&gt;first website ever&lt;/a&gt;, to &lt;code&gt;ul&lt;/code&gt;s with padded &lt;code&gt;li&lt;/code&gt;s floated left or right for the horizontal look that started in the 90s and continues on today, to the web 2.0 fad of &lt;a href="https://www.webfx.com/blog/web-design/50-examples-of-drop-down-navigation-menus-in-web-designs/" rel="noopener noreferrer"&gt;dropdowns on hover&lt;/a&gt;, to the proliferation of hamburger menus despite &lt;a href="https://medium.com/@kollinz/hamburger-menu-alternatives-for-mobile-navigation-a3a3beb555b8#.5273sahs0" rel="noopener noreferrer"&gt;people making the case against them&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the popular component libraries mentioned above, Material Design's &lt;a href="https://material.io/components/app-bars-top" rel="noopener noreferrer"&gt;top app bar&lt;/a&gt;, even though heavily biased towards apps rather than websites, is still a good high-level overview. The first &lt;a href="https://material.io/design/navigation/understanding-navigation.html" rel="noopener noreferrer"&gt;related link&lt;/a&gt; from that page is also a good read to get you to think more in terms of UI/UX rather than solely converting a design into a web page. In terms of implementation, bootstrap's &lt;a href="https://getbootstrap.com/docs/5.1/components/navbar/" rel="noopener noreferrer"&gt;Navbar&lt;/a&gt; seems closer to what we're looking for.&lt;/p&gt;

&lt;p&gt;Speaking of which, what are we actually looking for in our nav component? We have both desktop and mobile designs so that's good:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oub0fdndy4phvvh9j1j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2oub0fdndy4phvvh9j1j.jpg" alt="Desktop and mobile design of navigation component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I didn't make you read all those articles for nothing, so let's apply our knowledge to fully specify our nav component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A full-screen off-canvas element that slides in when the menu toggle icon is pressed, seems like the most appropriate solution for the mobile version. It's elegant to match the design. And it's big enough to accommodate all the links to the full catalog that would normally be present in a furniture shop's website like this one.&lt;/li&gt;
&lt;li&gt;The search and cart buttons are important enough to not be tucked away behind the hamburger menu, so we can leave them in the same place for mobile too, and move the toggle icon to the left.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-html-css/tree/nav" rel="noopener noreferrer"&gt;CODE - Navbar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nav--ui-challenge-html-css.netlify.app/" rel="noopener noreferrer"&gt;DEMO - Navbar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I think being able to just do &lt;code&gt;display: block;&lt;/code&gt; (the default) on mobile, and &lt;code&gt;display: flex;&lt;/code&gt; on desktop to get a responsive menu is beautifully simple. Also, I didn't do &lt;code&gt;ul &amp;gt; li&lt;/code&gt; for the menu items, and that's fine. You could do it, and that would be fine too.&lt;/li&gt;
&lt;li&gt;You'll notice I haven't gone overboard with aria properties. Just some roles and labels for important icon buttons like search etc.&lt;/li&gt;
&lt;li&gt;What I did pay attention to, with regards to accessibility, was making sure I don't break stuff that worked before, like navigating the page with the keyboard. And that's exactly what you do when you &lt;code&gt;translate&lt;/code&gt; the menu off the screen. If you press tab now to move from the logo to the search button, the focus seems to disappear for a while as it's moving through the menu items (which are not visible on the screen). That's what &lt;code&gt;visibility: hidden&lt;/code&gt; is for (as well as for hiding it from screen readers too). For more complicated components, you might have to manage the &lt;code&gt;tabindex&lt;/code&gt;es of your elements with JavaScript to fix this, but in this case, using the CSS visibility property, and making sure it works well with our animation, was all that was needed.&lt;/li&gt;
&lt;li&gt;I stole the hamburger icon animation from &lt;a href="https://dev.to/ajedral1994/comment/1j46m"&gt;this beautiful solution&lt;/a&gt; of the challenge. I must've been procrastinating on something else, though, because instead of copying the SVG animation like a normal human being, I had to go and rewrite it in CSS.&lt;/li&gt;
&lt;li&gt;I never knew I needed custom transition timing functions in my life but here we are. The &lt;code&gt;cubic-bezier&lt;/code&gt; you see at &lt;code&gt;.search&lt;/code&gt;'s &lt;code&gt;transition&lt;/code&gt; property is the "easeOutQuart" from &lt;a href="https://matthewlein.com/tools/ceaser" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Its inverse, the "easeInQuart", is used to complete the effect of making the search slowly show up (after the backdrop blur has started kicking in) and quickly get out of the way once you dismiss it.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;
  
  
  Showcase / Slider / Carousel
&lt;/h3&gt;

&lt;p&gt;Ah, the carousel — web designers' &lt;a href="https://shouldiuseacarousel.com/" rel="noopener noreferrer"&gt;most hated&lt;/a&gt; component. This is probably why you won't find it in most of the popular component libraries (okay bootstrap still has one, but that's probably part of why bootstrap is not so hip anymore). The carousel is not going away anytime soon though. Especially on mobile where swiping is second nature. Airbnb's scrolling features on their home page and app are iconic by now. Swiping on, how do we implement it?&lt;/p&gt;

&lt;p&gt;You may not find a carousel component in component libraries, but do you know where you can find one? At the &lt;a href="https://www.w3.org/TR/wai-aria-practices-1.1/#carousel" rel="noopener noreferrer"&gt;WAI-ARIA Authoring Practices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I took an even simpler approach, though. Here's the code first:&lt;/p&gt;
&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-html-css/tree/showcase" rel="noopener noreferrer"&gt;CODE - Showcase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://showcase--ui-challenge-html-css.netlify.app/" rel="noopener noreferrer"&gt;DEMO - Showcase&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I said, I took a simpler approach. Not because I'm a contrarian, but because I found out about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap/Basic_concepts" rel="noopener noreferrer"&gt;CSS Scroll Snap&lt;/a&gt;! Oh em gee! Is this real? How can this be &lt;a href="https://caniuse.com/css-snappoints" rel="noopener noreferrer"&gt;real&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;You add two CSS properties, and things... just... work? This just doesn't happen on the web. Who allowed this? Didn't they think of the slippery slope?&lt;/p&gt;

&lt;p&gt;OK, for real, this is awesome, but let's move on.&lt;/p&gt;

&lt;p&gt;With scroll snap, our carousel doesn't have to be a carousel at all. All the "slides" content will be present at all times, just waiting to be scrolled into view. As far as screen readers are concerned, this is normal content on the page (I gave slides a &lt;code&gt;role="group"&lt;/code&gt;, but even without it, screen readers would do fine reading the content in order).&lt;/p&gt;

&lt;p&gt;For keyboard navigation, when the showcase has focus, pressing left or right arrow keys would normally just scroll the content horizontally by a little. Thanks to snap scroll, that little scroll becomes a complete scroll to the previous/next slide. &lt;code&gt;scroll-behavior: smooth;&lt;/code&gt; ensures the same happens when you tab through, moving the focus from one slide to another (except for firefox apparently, but that's easy to fix with some JavaScript if you want).&lt;/p&gt;

&lt;p&gt;The JavaScript for the buttons becomes trivial too: &lt;code&gt;next/previousElement.scrollIntoView()&lt;/code&gt;. The &lt;code&gt;IntersectionObserver&lt;/code&gt; keeps track of the current slide to conditionally disable the prev and next buttons as needed.&lt;/p&gt;



&lt;h3&gt;
  
  
  (Filterable) Products Listing Component
&lt;/h3&gt;

&lt;p&gt;Have you seen that "drawing spiderman in 10 min, 1 min, 10 seconds challenge" that went viral a while ago?&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/x9wn633vl_c"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The 10-second version of our page would just be the products listing component. It's the main function of the site. The reason why visitors are there. I learned to recognize this most basic pattern as soon as I got into web development. I started with WordPress — you had this &lt;code&gt;index.php&lt;/code&gt; template to design the page that lists all the blogs/items, and &lt;code&gt;single.php&lt;/code&gt; to design the individual view that shows up when the user clicks one of those items. That pattern is everywhere in different variations: youtube, twitter, news sites, your portfolio. See a bunch of things, click one.&lt;/p&gt;

&lt;p&gt;Now, because this is so often the main functionality — it is closely tied to the business logic of the website/app — you won't usually find a generic "product listing" component in the component libraries. Instead, you'll find helpful bits intended to aid you while you're building this core feature yourself. For example, you'll find &lt;a href="https://material.io/components/cards" rel="noopener noreferrer"&gt;cards&lt;/a&gt; in material design, and this nice &lt;a href="https://chakra-ui.com/docs/navigation/link-overlay" rel="noopener noreferrer"&gt;link overlay&lt;/a&gt; in chakra. Bootstrap also has cards, &lt;a href="https://getbootstrap.com/docs/5.1/components/list-group/#custom-content" rel="noopener noreferrer"&gt;list groups&lt;/a&gt;, &lt;a href="https://getbootstrap.com/docs/5.1/components/pagination/" rel="noopener noreferrer"&gt;pagination&lt;/a&gt; etc.&lt;/p&gt;

&lt;p&gt;Pagination is interesting because it's one of those "variations" that I mentioned above — a property of the items listing component. Filtering is, in this way, also a property of the component. An important property for sure, since it changes fundamentally the way users interact with the component, but it's still just a property, something that the component "has". I guess what I'm trying to say is don't think of this component as a filtering component first, but as a products listing component, with filtering. This will set the framework for thinking on how to implement it. Let's do that now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The desktop layout screams CSS Grid. Three columns, and as many rows as needed. The mobile layout is horizontally scrollable. I made this snappy-scrollable (word?) as well, but it is a UX question to think about. The keyword here is reducing inertia. Is it easier for the user to scroll through the products with or without scroll snap?&lt;/li&gt;
&lt;li&gt;The classic &lt;a href="https://masonry.desandro.com/methods.html#remove" rel="noopener noreferrer"&gt;masonry&lt;/a&gt; is the first effect that comes to mind for animating the filtering. It animates both the items that are appearing/disappearing individually, as well as the movement of the surrounding elements to perfectly illustrate the filtering that is taking place. But I don't think it's a good fit in our case for a few reasons:

&lt;ol&gt;
&lt;li&gt;In this proof of concept we can have all the products in the HTML at all times, and hide those that must be filtered out with CSS. But in a real-world implementation of this e-commerce website, you would be fetching the products from some backend. All that movement after a loading animation would make for a disjointed experience.&lt;/li&gt;
&lt;li&gt;It seems like animating the position of items would be rather complicated with CSS Grid. Because I made my decision after thinking of the first reason above, I haven't investigated this further, but I'm including it here as something to think about. This is not about being lazy, but about being mindful of budget and time restrictions. As mentioned above, this is the core feature of our website, so if you were to decide that having a masonry style effect was important enough, it would make sense to plan for and implement a solution for it. That solution might be anything, from researching libraries you could use that animate CSS grid, to switching to absolute position altogether. The point that I'm trying to make is... actually two points: 1) decide what is important and how much time you're going to spend on it, and 2) when you see something implemented differently from how you would do it, consider they most probably had a reason to do it the way they did — if appropriate, ask them why.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-html-css/tree/products" rel="noopener noreferrer"&gt;CODE - Products&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://products--ui-challenge-html-css.netlify.app/" rel="noopener noreferrer"&gt;DEMO - Products&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For animation, I ended up fading the elements in and out. Seems unobtrusive enough, and in line with the design and the rest of the animations. The js code might seem a bit complex, but it has to be that way because of the classic problem where you can't animate from &lt;code&gt;display: block;&lt;/code&gt; to &lt;code&gt;display: none;&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;To make it work with loading, I would only fade the contents inside of &lt;code&gt;.product&lt;/code&gt;, and infinitely animate the background color to indicate loading. I might do this in one of the upcoming blogs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Using a radio group for our filter buttons solves most of the accessibility problems we would otherwise encounter. Keyboard navigation? Check. Focus on labels with &lt;code&gt;:focus-visible&lt;/code&gt;? Check. The product list itself is a &lt;code&gt;ul&lt;/code&gt;, and &lt;code&gt;aria-live="polite"&lt;/code&gt; announces to screen readers when its content changes.&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;And without further ado (lol as if I could drag this out any longer), here's the full challenge demo and code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microbouji/ui-challenge-html-css" rel="noopener noreferrer"&gt;CODE - Full website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ui-challenge-html-css.netlify.app/" rel="noopener noreferrer"&gt;DEMO - Full website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I'll try to do this in a bulleted list form too. Here are my observations from building a website with HTML and CSS in late 2021:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EVERGREEN BROWSERS BABY!!! You have no idea how bad this was before. In immigration law terms, I believe our jobs qualify as "highly skilled labor". Well, for a long time, 99% of the high skill a web developer had was knowing about and resolving browser inconsistencies. I don't know how to tie a tie, but the information that Internet Explorer 6's XMLHttpRequest was not a native JavaScript object but an ActiveX one, is still taking up space somewhere in my brain. &lt;/li&gt;
&lt;li&gt;This is still very much a full-time job. We only scratched the surface of accessibility here and didn't even get into performance. Anyone who speaks of full-stack has either stumbled upon — and is underpaying — the rare 10x coding creature or, much more probably, is neglecting many of the specialized areas just mentioned. (I have nothing against full-stack roles where responsibilities shift from time to time; this was just for the specific situations I mentioned here.)&lt;/li&gt;
&lt;li&gt;The kids are alright. Not looking to rehash all the debates on tooling and over-engineering from the past few years (almost a decade now, really). Just making my observation, that, even when building a simple website in 2021, the "just opening up notepad and coding" ideal feels very self-limiting without getting much benefit in return.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alright then, on the next blog we'll rebuild this in React and some CSS-in-JS library like styled-components. Follow me on &lt;a href="https://twitter.com/microbouji" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;. I almost never tweet, but if someone follows me from here, I'll make an exception and tweet about the next article when it comes up.&lt;/p&gt;

</description>
      <category>responsive</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Modern Web Dev - Intro</title>
      <dc:creator>Elian Ibaj</dc:creator>
      <pubDate>Tue, 16 Nov 2021 22:58:27 +0000</pubDate>
      <link>https://dev.to/microbouji/intro-285c</link>
      <guid>https://dev.to/microbouji/intro-285c</guid>
      <description>&lt;p&gt;I was a professional web developer from 2009 to 2015. As I'm getting ready to get back into it, I thought I'd write about the process of catching up with modern web dev. You don't need to be in the same place as me to benefit from this blog, though. I imagine, the biggest skills that will transfer over for me, will be my core knowledge of HTML, CSS, and JavaScript. If you're also at the place where you feel confident with your knowledge of these three foundational technologies of the web, and are thinking about what the actual job of doing web development in 2021 looks like, then this article series is for you.&lt;/p&gt;

&lt;p&gt;OK, that's a big statement so let me qualify it further by saying that I will focus only on a few parts of this job that I'm most interested in pursuing for myself. I've split up my study plan (and hence the blogs that I will write here) into three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Responsive web design and UI&lt;br&gt;
Also known as the front of the frontend, this is reflected in job titles that include the word UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data fetching and state management&lt;br&gt;
This will be react-specific (sorry?). Job titles here include the word engineering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rendering strategies and deployment&lt;br&gt;
I need this for my own sanity. Now you too can know about the wonders of SSG vs SSR vs ISR!!! And this disconnect between the perfectly set up local environment and where it will all be deployed, is a world away from live editing the files on your bluehost "server", so we'll talk about that as well.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Other than the above limitations, narrowing down both the business and tech sides, you should benefit from reading these three articles no matter which part of frontend development you end up working in. For example, knowing the above will be helpful whether you get to work as a solo freelancer, with an agency, or get a full-time job maintaining a single web product for the long term.&lt;/p&gt;

&lt;p&gt;Also, these blog posts will just be scratching the surface of what the corresponding subfields entail. If you plan on getting a job with "engineering" in the title, don't think that you're wasting your time reading the first blog. It will not make you a UI expert by any means. It will just give you a better understanding of the field, which translates, among other things, into being a better teammate.&lt;/p&gt;

&lt;p&gt;A last note on the format of these articles. I will be writing them as I'm actually reviewing all these concepts myself. It's me documenting my process of learning, kind of like a livestream. But since an actual livestream of that would be unendurably boring, I'm hoping this written format will be a nice middle ground: informative, (hopefully) not too much rambling, while still staying true to the non-linear process that is learning to become a self-taught web developer.&lt;/p&gt;

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