<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ user experience - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ user experience - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 27 May 2026 20:46:38 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/user-experience/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Apply Academic Theories to Human-Centered Web Design [Full Handbook] ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever abandoned an app right at the sign‑up page? Or felt uneasy navigating a website because the buttons were scattered randomly, the colors clashed, and the layout felt confusing and unneces ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-apply-academic-theories-to-human-centered-web-design-handbook/</link>
                <guid isPermaLink="false">69fe29e9f239332df4f7cd02</guid>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MathJax ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Great John ]]>
                </dc:creator>
                <pubDate>Fri, 08 May 2026 18:22:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/d7621fda-83a6-460e-aa38-bce970d4a655.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever abandoned an app right at the sign‑up page? Or felt uneasy navigating a website because the buttons were scattered randomly, the colors clashed, and the layout felt confusing and unnecessarily complex?</p>
<p>Maybe you were asked to complete twenty fields in one go. You carefully filled everything out, hit Submit — and only then were you told that your password didn't meet some hidden, unspoken requirement. A requirement that was never communicated upfront.</p>
<p>Instead of helpful guidance, you were met with a vague message: “Invalid input." Invalid how, you wonder?</p>
<p>Required fields weren’t marked. There was no real‑time validation. No helpful red outline showing which field was wrong. Just a generic prompt telling you to “go back and correct missing information,” as if you’re supposed to magically know what the system wants.</p>
<p>So you scroll.</p>
<p>You search.</p>
<p>You guess.</p>
<p>And you're now getting frustrated.</p>
<p>The reason you're frustrated is simple: no one enjoys repeating a task they thought they had already completed — especially when the mistakes could've been prevented with clear guidance along the way.</p>
<p>You manage to fill in the form and you tap the Submit button.</p>
<p>Nothing happens.</p>
<p>No loading spinner.</p>
<p>No subtle animation.</p>
<p>No confirmation message.</p>
<p>No success screen.</p>
<p>Just silence. For a brief moment, you’re left wondering: Did it go through? So you tap again. And maybe… one more time.</p>
<p>At this point, you become fed up and you either postpone the signup process to when you have the time, or you may not ever return.</p>
<p>Even if you haven’t experienced this exact scenario, you’ve almost certainly felt the same kind of friction: that moment when a digital interface makes you pause, hesitate, or wonder what you’re supposed to do next.</p>
<p>These frustrations often arise because frontend developers either overlook or are unaware of the essential design principles and theories that underpin a smooth, intuitive user experience.</p>
<p>As a frontend developer, your interface should minimise cognitive load, provide immediate clarity, and guide users effortlessly through every task.</p>
<p>In this handbook, I'll introduce the academic theories that should inform and elevate your frontend decisions.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-10-fittss-law">1.0 Fitts’s Law:</a></p>
<ul>
<li><p><a href="#heading-11-use-padding-wisely">1.1 Use padding wisely</a></p>
</li>
<li><p><a href="#heading-12-use-infinite-targets">1.2 Use infinite targets</a></p>
</li>
<li><p><a href="#heading-design-takeaway-from-fitts-law">Design Takeaway from Fitts Law:</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-20-hicks-law">2.0 Hick's Law:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-hicks-law">Design Takeaway from Hick's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-30-gestalt-principles">3.0 Gestalt Principles:</a></p>
<ul>
<li><p><a href="#heading-key-gestalt-principles">Key Gestalt Principles:</a></p>
</li>
<li><p><a href="#heading-31-proximity">3.1 Proximity</a></p>
</li>
<li><p><a href="#heading-32-similarity">3.2 Similarity</a></p>
</li>
<li><p><a href="#heading-33-continuity">3.3 Continuity</a></p>
</li>
<li><p><a href="#heading-34-closure">3.4 Closure</a></p>
</li>
<li><p><a href="#heading-35-figureground">3.5 Figure/Ground</a></p>
</li>
<li><p><a href="#heading-36-common-fate">3.6 Common Fate</a></p>
</li>
<li><p><a href="#heading-37-focal-point">3.7 Focal Point</a></p>
</li>
<li><p><a href="#heading-design-takeaways-from-the-gestalt-principles">Design Takeaways from the Gestalt Principles</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-40-von-restorff-effect-the-isolation-effect">4.0 Von Restorff Effect (The Isolation Effect):</a></p>
<ul>
<li><a href="#heading-design-takeway-from-von-restorff">Design takeway from Von Restorff</a></li>
</ul>
</li>
<li><p><a href="#heading-50-jakobs-law">5.0 Jakob’s Law</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-jakobs-law">Design Takeaway from Jakob's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-60-millers-law">6.0 Miller’s Law</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-millers-law">Design Takeaway from Miller's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-70-the-goal-gradient-hypothesis">7.0 The Goal-Gradient Hypothesis</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-goal-gradient-hypothesis">Design Takeaway from Goal-Gradient Hypothesis</a></li>
</ul>
</li>
<li><p><a href="#heading-80-zeigarnik-effect">8.0 Zeigarnik Effect</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-zeigarnik-effect">Design Takeaway from Zeigarnik Effect</a></li>
</ul>
</li>
<li><p><a href="#heading-90-teslas-law">9.0 Tesla’s Law:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-teslas-law">Design Takeaway from Tesla's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-100-peak-end-rule">10.0 Peak End Rule:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-peak-end-rule">Design takeaway from Peak End Rule</a></li>
</ul>
</li>
<li><p><a href="#heading-110-postels-law">11.0 Postel’s Law:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-postels-law">Design Takeaway from Postel's Law</a></li>
</ul>
</li>
<li><p><a href="#heading-120-doherty-threshold">12.0 Doherty Threshold:</a></p>
<ul>
<li><a href="#heading-design-takeaways-from-doherty-threshold">Design Takeaways from Doherty Threshold</a></li>
</ul>
</li>
<li><p><a href="#heading-130-serial-position-effect-primacy-and-recency">13.0 Serial Position Effect (Primacy and Recency):</a></p>
<ul>
<li><a href="#heading-design-takeaways-serial-position-effect">Design Takeaways Serial Position Effect</a></li>
</ul>
</li>
<li><p><a href="#heading-140-occams-razor">14.0 Occam’s Razor:</a></p>
<ul>
<li><a href="#heading-design-takeaway-from-occams-razor">Design Takeaway from Occam's Razor</a></li>
</ul>
</li>
<li><p><a href="#heading-150-parkinsons-law">15.0 Parkinson's Law</a></p>
<ul>
<li><a href="#heading-design-takeaway-for-parkinsons-law">Design Takeaway for Parkinson's law</a></li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a href="#heading-references">References</a></p>
</li>
</ul>
<p>You might wonder what academic theories have to do with frontend development.</p>
<p>The answer is simple. Academic theories aren't abstract ideas. There are the result of rigorous scientific investigation — controlled experiments, validated models, and decades of research into how humans think, learn, perceive, and interact with information.</p>
<p>Because these theories are grounded in evidence rather than opinion, they offer reliable guidance for building interfaces that align with how the human brain actually processes information.</p>
<p>Applying them to frontend development means you're not designing by guesswork or personal preference. Instead, you're applying tested, scientific insights to create clearer, faster, more humane user experiences.</p>
<p>In other words, when you build with academic theory in mind, your frontend becomes more than just visually appealing — it becomes cognitively efficient, behaviourally aligned, and measurably easier for users to navigate.</p>
<p>You can use the following laws and principles to guide your development work. Let’s start by looking at Fitt’s law.</p>
<h2 id="heading-10-fittss-law"><strong>1.0 Fitts’s Law:</strong></h2>
<p>Fitts’s law is the brainchild of Paul Fitts. He was among the early psychologists who recognised that many human errors result from flawed design rather than simple human weakness.</p>
<p>During World War II, he studied airplane cockpit layouts and concluded that numerous incidents attributed to pilot error were actually caused by poor design decisions (Hall, 2023; Budiu, 2022).</p>
<p>Here's the formula:</p>
<p>$$T = a + b \cdot \log_2\left(1 + \frac{D}{W}\right)$$</p>
<p>T = Movement Time</p>
<p>D = Distance to the target</p>
<p>W = Width (size) of the target</p>
<p>a, b = Empirically determined constants</p>
<p>Based on his findings, Fitts postulated that the time required to acquire/reach a target is determined by the distance to the target and the size of the target.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/7d2699bd-4878-4ab0-8669-21616cb8faf1.png" alt="7d2699bd-4878-4ab0-8669-21616cb8faf1" style="display:block;margin:0 auto" width="691" height="244" loading="lazy">

<p><em>Fig 1.0: Illustration of Fitts Law.</em></p>
<p>From the above, between Target B and Target C, it will be faster to interact with Target C than Target B simply because of the distance (Target B is farther away). Interestingly, though Target A and Target C are at the same distance, Target C will still be faster to interact with and less error-prone because of its larger size.</p>
<p>In simple terms, Fitt’s Law tells us that the time required to move to a target depends on two main factors: the distance to the target and the size of the target. The farther away an element is, the longer it takes to reach. The smaller it is, the more precision it demands, which increases the interaction time and the likelihood of errors.</p>
<p>Conversely, closer and larger targets reduce cognitive load, motor effort, and frustration.</p>
<p>In a nutshell, Fitts’s main message to developers is to reduce the distance users must travel on the screen and to make important buttons large and visually dominant.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/bc293949-cadc-46b2-892d-bdfa1dfc6db3.jpg" alt="bc293949-cadc-46b2-892d-bdfa1dfc6db3" style="display:block;margin:0 auto" width="928" height="664" loading="lazy">

<p><em>Fig 1.1: Showing Call-to-Action buttons are the largest and most visually prominent elements on each screen.</em></p>
<p>From the image above, you can see that the Call-to-Action buttons on each of the screens are the most visually dominant button and largest in size. They're also placed within the natural region. This makes them faster/easier to interact with.</p>
<p>You should also place your Call-to-Action button within the natural zone. This is a zone on a mobile phone where it's easy to reach with the thumb (as most people use their thumbs to select things on a phone screen). Here's a diagram showing the "natural zone" on a typical smartphone. It's much faster for a user to interact within the "natural zone" than the "hard zone" (see figure).</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/e98d8d91-b4e6-4b4a-96e5-2524a6e09028.png" alt="e98d8d91-b4e6-4b4a-96e5-2524a6e09028" style="display:block;margin:0 auto" width="1536" height="1024" loading="lazy">

<p><em>Fig 1.2: Showing three different zones for buttons placement (natural, stretching and hard region)</em></p>
<h3 id="heading-11-use-padding-wisely">1.1 Use Padding Wisely</h3>
<p>Fitts' law can be applied to your development by increasing padding wisely. You can also use padding to increase the interactive area. By doing this, you're increasing the size of the targets.</p>
<p>This is important, because imagine a menu that disappears the moment your cursor drifts a few inches away. You’re weren't trying to close it — you simply moved slightly, and suddenly the entire menu collapses. That tiny slip forces you to start the interaction all over again. It’s a small mistake, but it creates a disproportionately frustrating experience.</p>
<p>This happens because the interactive area is too narrow.</p>
<p>That’s why effective padding — or more broadly, generous interactive zones — is essential. By increasing the clickable or hoverable area around a menu, you are increasing the size of the targets, which makes the interaction more stable, more forgiving, and far less cognitively demanding.</p>
<p>This ensures users can move naturally without fear of accidentally “falling off” the target.</p>
<h3 id="heading-12-use-infinite-targets">1.2 Use Infinite Targets</h3>
<p>Another fundamental principle that emerges from Fitt’s Law is the idea of infinite targets. When an interface element is placed at the very edge or corner of a screen, it becomes effectively “infinite” because the cursor can't move beyond the screen boundary. The edge acts as a physical barrier, allowing the user to fling the mouse in that direction without precision or careful aiming.</p>
<p>As a result, corners and edges become the fastest, easiest, and most reliable places for users to access important controls.</p>
<p>This is why operating systems such as Apple’s macOS and Microsoft Windows position their most essential menus and buttons at these locations. The macOS Apple Menu sits in the top‑left corner, Windows historically placed the Start button in the bottom‑left corner, and both systems anchor taskbars, docks, and notification areas along screen edges.</p>
<p>These placements reduce cognitive load, minimise motor effort, and increase interaction speed because users do not need to slow down or correct their cursor movement. The screen itself “catches” the pointer.</p>
<p>In essence, infinite targets transform small interface elements into large, easy‑to‑hit zones simply by leveraging the geometry of the screen.</p>
<p>What this means for you: place your most important and frequently used actions where users can reach them with the least effort. Screen edges and corners act as natural stopping points, meaning users can't overshoot them.</p>
<h3 id="heading-design-takeaways-from-fitts-law">Design Takeaways from Fitts Law:</h3>
<p><strong>Place Primary Actions Where the Task Ends:</strong><br>Placing a submit button at the top‑right forces users to travel all the way back after completing a long form. This increases interaction cost and breaks flow. The best place for a submit button is at the bottom of the form — exactly where the user finishes the task. This aligns with natural reading and interaction patterns.</p>
<p><strong>Keep Related Actions Physically Close:</strong><br>Separating “Add to Cart” and “Check Out” across opposite sides of the screen forces unnecessary thumb movement. Group related actions to reduce effort and speed up decisions.</p>
<p><strong>Make Primary Targets Large and Visually Dominant:</strong><br>Your main CTAs (“Subscribe Now,” “Pay Now,” “Create Account,” “Sign Up”) should be the most recognisable elements on the screen. Large, high‑contrast targets reduce errors and improve speed.</p>
<p><strong>Place High‑Value Actions at Screen Edges and Corners:</strong><br>Edges and corners act as “infinite targets” because the cursor can’t overshoot them. This makes them the fastest, easiest, and most reliable places for critical controls.</p>
<p>A tiny icon in the middle of the screen is hard to hit. The same icon placed at an edge becomes effectively huge because the boundary “catches” the pointer. Also, actions like navigation, primary CTAs, or global controls should live where users can reach them with minimal effort. Avoid burying important actions in the centre of the screen.</p>
<p><strong>Increase Target Size With Generous Padding:</strong><br>Small interactive zones force users to aim with pixel‑level precision. Adding padding expands the clickable or hoverable area, making interactions easier, faster, and more forgiving.</p>
<p><strong>Prevent Accidental “Fall‑Off” With Larger Hit Areas:</strong><br>Menus that collapse the moment the cursor drifts slightly create frustration. A wider interactive zone keeps the menu open during natural mouse movement, reducing accidental resets.</p>
<p>Users don’t move perfectly. Interfaces should accommodate slight slips without punishing them. Larger targets reduce cognitive load and eliminate unnecessary frustration. so by increasing the effective size of buttons, menus, and controls, you create interactions that feel stable and predictable, and users can move confidently without fear of losing their place.</p>
<p><strong>To Sum Up:</strong> The farther away an element is, the longer it takes to reach. The smaller it is, the more precision it demands, which increases the interaction time and the likelihood of errors. Conversely, closer and larger targets reduce cognitive load, motor effort, and frustration.</p>
<h2 id="heading-20-hicks-law"><strong>2.0 Hick's Law</strong>:</h2>
<p>Hick’s Law is a psychological principle that describes the relationship between the number of choices presented to a user and the time it takes them to make a decision. It was formulated by William Edmund Hick in 1952 (Yablonski, 2022; Proctor &amp; Scheider, 2018).</p>
<p>The law states that as the number of options increases, the decision time increases logarithmically. In simple terms, more choices slow users down, while fewer choices speed up decision-making.</p>
<p>$$T = a + b \cdot \log_2(n + 1)$$</p>
<p>Where:</p>
<p>T = time to make a decision,</p>
<p>n = number of choices,</p>
<p>b= a constant that depends on the task and the individual</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/d2c45f4b-77e9-42dc-a9c3-165dfe5f7ce7.jpg" alt="d2c45f4b-77e9-42dc-a9c3-165dfe5f7ce7" style="display:block;margin:0 auto" width="1136" height="692" loading="lazy">

<p><em>Figure 2.0 illustrates the relationship between user experience, reaction time, and the number of actions.</em></p>
<p>This is how users feel, for example, when they encounter a form that asks for too much information upfront. The longer the form gets, the more frustrated they become.</p>
<p>Examples of this are overloading menus with too many items, presenting long, unorganised forms, giving too many calls-to-action on one screen, and building nested menus with excessive depth.</p>
<p>All of these create friction and can lead to cognitive overload.</p>
<h3 id="heading-design-takeaway-from-hicks-law">Design Takeaway from Hick's Law</h3>
<p><strong>Avoid Overloading Users With Too Many Actions:</strong><br>Too many buttons, menu items, or choices at once increases cognitive load and slows decision‑making. Users freeze when everything competes for attention.</p>
<p><strong>Keep Navigation Clean and Focused:</strong><br>Cluttered menus hurt both usability and SEO. Search engines struggle to track overly complex navigation structures, and users struggle to find what matters.</p>
<p><strong>Use Progressive Disclosure to Reduce Complexity:</strong><br>Hide advanced or rarely used options under “More” or expandable sections. Reveal complexity only when the user needs it.</p>
<p><strong>Break Complex Tasks Into Smaller, Manageable Steps:</strong><br>Progressive disclosure works beautifully for multi‑step forms and decision flows. Smaller steps reduce overwhelm and improve completion rates.</p>
<p><strong>Group Related Options Into Logical Categories:</strong><br>Organising actions into meaningful clusters helps users process information faster. For example, placing “Edit” and “Delete” together leverages natural mental grouping.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/mbYIfRxSkHs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>

<p><em>Video 2.0: Video description of Progressive Disclosure.</em></p>
<p>From the video above, instead of showing all the menu details at once, it is better to hide them initially. As you can see, the additional information only appears when the arrow down button is pressed. This approach prevents overwhelming the user and keeps the interface clean and focused.</p>
<p>You should also reduce decision anxiety, as too many choices create doubt and friction (as they say, the more you ask from a user, the less you get).</p>
<p>Beyond this, try to use recommended labels, show brief descriptions, provide visual previews, and use comparison tables wisely to show comparison between products especially when they have many characteristics. An example of a comparison table is shown below:</p>
<p><a href="https://drive.google.com/file/d/1sY-tb9W1QDnyrH9dd3NSYsteP9WoMT27/view?usp=drive_link"><img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/5e52527a-3faf-4da1-97c2-a2a09ca7af8b.jpg" alt="Use of comparison table to compare products" style="display:block;margin:0 auto" width="1038" height="972" loading="lazy"></a></p>
<p><em>Figure 2.1: A comparison table being used to simplify complex information.</em></p>
<p>Also, rather than showing advanced configuration options by default, display only the most commonly used settings. Advanced options can be hidden under an expandable section like “Advanced” or “More Settings. This makes your interface less cluttered and more visually organized.</p>
<p>And speaking of visual organization, this is the perfect moment to introduce Gestalt principles — the psychological rules that explain how users naturally group and interpret what they see.</p>
<p><strong>To Sum Up</strong>: As the number of options increases, the decision time increases logarithmically.</p>
<h2 id="heading-30-gestalt-principles"><strong>3.0 Gestalt Principles</strong>:</h2>
<p>In the 1920s, a group of German psychologists – Max Wertheimer, Kurt Koffka, and Wolfgang Köhlern – introduced what are now known as the Gestalt Principles. Their work sought to understand how humans perceive and interpret visual information (Bustamante, 2023).</p>
<p>The word “Gestalt” is German for “unified whole,” reflecting the core idea behind the theory: people naturally perceive objects as organised patterns and complete forms rather than as separate, disconnected parts.</p>
<p>These principles explain how the human mind structures visual elements to make sense of the world. Over time, they have become highly influential in fields such as design, user experience (UX), psychology, and data visualization, where understanding perception is critical.</p>
<h3 id="heading-key-gestalt-principles">Key Gestalt Principles:</h3>
<h3 id="heading-31-proximity">3.1 Proximity</h3>
<p>Elements that are placed close to each other are perceived as a group, while those spaced far apart are seen as separate. This is why labels are placed directly next to their corresponding input fields.</p>
<p>For example: In a blog feed, the "Title," "Author," and "Date" should have small margins between them (8px), while the space between one blog post card and the next should be much larger (40px). This tells the user's brain: "These three text strings belong to this specific post."</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/b68ab7f4-1b2e-47f1-8e8c-8ae8f32198c1.jpg" alt="b68ab7f4-1b2e-47f1-8e8c-8ae8f32198c1" style="display:block;margin:0 auto" width="1046" height="956" loading="lazy">

<p><em>Fig 3:0 Illustration of proximity (Gestalt Principle)</em></p>
<p>From the fig above, the spacing within the blog feed plays a powerful role in how effortlessly users interpret what they see. When elements sit close together, the brain instinctively treats them as belonging to the same unit. This is why placing the author credit just 8px beneath the title creates an immediate mental link. The viewer doesn’t need to pause or decode who wrote which article; proximity does the cognitive work automatically, forming a tight, intuitive grouping.</p>
<p>Equally important is the generous 40px gap between individual cards. This larger spacing introduces “visual breathing room.” Without it, a feed can quickly collapse into a dense wall of text, overwhelming the user and discouraging exploration. The wider margin establishes a clear boundary—a natural stop-and-start rhythm—that makes each card feel distinct and the entire layout more scannable.</p>
<p>Finally, subtle spacing differences can guide behaviour, not just perception. The slightly larger 12px margin above the read‑more link separates it from the passive information above it. This spacing cues the user that the link represents an action rather than another piece of descriptive text. It’s a small adjustment, but it shifts the element’s role from informational to interactive, helping users understand what they can <em>do</em> next.</p>
<p>Together, these spacing decisions transform a simple list of posts into a structured, intuitive, and behaviourally clear interface—one where the user never has to think about the layout, because the layout is already thinking for them.</p>
<p>Proximity controls meaning: move elements closer to show connection, separate them to show difference.</p>
<h3 id="heading-32-similarity">3.2 Similarity</h3>
<p>We naturally group elements that share similar visual characteristics, such as color, shape, size, or orientation.</p>
<p>For example, even if buttons are spread across a page, if they're all the same shade of blue, the user understands they perform similar functions.</p>
<p>If your primary "Submit" button is blue with rounded corners, every other primary action on your site should look exactly the same. If you suddenly use a square red button for a primary action, the user will be confused because the "similarity" is broken.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/151d9658-5a4b-4d1d-b885-02c73c53cdbd.jpg" alt="151d9658-5a4b-4d1d-b885-02c73c53cdbd" style="display:block;margin:0 auto" width="1618" height="2170" loading="lazy">

<p><em>Fig 3:1 : illustration of similarity (Gestalt principle)</em></p>
<p>As you can see from above, the layout clearly demonstrates how the Gestalt Principle of Similarity works by showing two different visual situations: one where everything matches, and one where a single element breaks the pattern.</p>
<p>All three product cards share the same visual characteristics:</p>
<ul>
<li><p>Same card shape</p>
</li>
<li><p>Same border and shadow</p>
</li>
<li><p>Same image size and placement</p>
</li>
<li><p>Same blue “Add to Cart” button</p>
</li>
<li><p>Same font style and spacing</p>
</li>
</ul>
<p>Because these elements look alike, your brain automatically groups them as one category — “products that belong together.”<br>You don’t have to think about it; the similarity creates instant visual unity.</p>
<p>This is the Gestalt Principle of Similarity in action.</p>
<p>In the second row, everything is still similar except one button:</p>
<ul>
<li><p>The middle product’s button is orange, not blue</p>
</li>
<li><p>It has square corners, not rounded</p>
</li>
<li><p>The text is italic, not regular</p>
</li>
<li><p>The label changes to “Quick Buy”</p>
</li>
</ul>
<p>Because this button breaks the shared pattern, your brain immediately notices it and treats it as different or special.</p>
<p>Developers can use broken similarity to intentionally highlight featured items, promotions, or urgent actions.</p>
<p>When similarity is broken, the different element stands out and draws attention.</p>
<h3 id="heading-33-continuity">3.3 Continuity</h3>
<p>The human eye prefers to follow a continuous path or curve rather than jagged or broken lines. We perceive items aligned on a line or curve as being related. This is often used in navigation menus or horizontal carousels to guide the user's gaze.</p>
<p>For example, you might have a horizontal carousel where the last visible card is slightly "cut off" at the edge of the screen. This visual break creates a path that encourages the user to keep scrolling as their eyes follow the line of cards.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/e44556e8-4174-4073-9c05-63b5df9a1278.jpg" alt="e44556e8-4174-4073-9c05-63b5df9a1278" style="display:block;margin:0 auto" width="1650" height="214" loading="lazy">

<p><em>Fig 3:2: illustration of continuity (Gestalt principle)</em></p>
<p>As you can see, all four form fields — <em>First Name</em>, <em>Last Name</em>, <em>Email Address</em>, and <em>Phone Number</em> — are perfectly aligned along one continuous horizontal path. Because the human eye naturally prefers to follow an unbroken line, your gaze moves smoothly from left to right across the fields without effort.</p>
<p>The final field is slightly cut off at the edge, which creates a subtle visual cue that the line continues beyond the visible area. This encourages the user to keep scrolling or swiping, because their eyes are already following the direction of the form.<br>when elements are arranged along a straight path, curve, or flow, the brain automatically treats them as connected and expects the pattern to continue.</p>
<p>Another example is Instagram Stories, which are arranged in a smooth horizontal line at the top of the app. Instagram reinforces this by slightly revealing the next story circle at the edge of the screen. That tiny “peek” acts as a continuation cue — your eyes expect the line to keep going, so your finger follows.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/bb019a6d-33c9-4509-b1c7-191b5a453c46.jpg" alt="bb019a6d-33c9-4509-b1c7-191b5a453c46" style="display:block;margin:0 auto" width="1320" height="520" loading="lazy">

<p><em>Fig 3:3: illustration of continuity (Gestalt principle)</em></p>
<p>As you can see from above, all the circular story icons are arranged in a straight horizontal line, and your visual system instinctively follows that line from left to right without effort.</p>
<p>The slight visibility of the next story at the edge of the screen strengthens this effect, signaling that the sequence continues beyond what's currently shown. Also, because the icons share the same size, spacing, and shape, there are no visual interruptions, allowing your eyes to glide across them in one continuous motion.</p>
<p>This seamless flow is exactly what continuity describes: the tendency of the human eye to follow the direction of a line or pattern, assuming it continues even when part of it is out of view.</p>
<p>Continuity is the tendency of the human eye to follow the direction of a line or pattern, assuming it continues even when part of it is out of view.</p>
<h3 id="heading-34-closure">3.4 Closure</h3>
<p>Closure refers to the mind’s ability to perceive a complete, unified form even when parts of that form are missing. Rather than requiring every boundary, line, or shape to be explicitly drawn, the brain instinctively fills in the gaps. When used intentionally, closure allows interfaces to feel cleaner, more elegant, and more cognitively efficient.</p>
<p>When we look at a complex arrangement of visual elements, we tend to look for a single, recognisable pattern. If an image is missing parts, our brains fill in the gaps to "close" the shape.</p>
<p>One of the most celebrated examples of closure in visual identity design is the panda symbol used by the World Wide Fund for Nature (WWF). This logo demonstrates how strategic omission can produce a memorable, emotionally resonant, and universally recognisable mark.</p>
<p>At first glance, the panda illustration appears simple, composed of a few bold black shapes arranged against a white background.</p>
<p>Yet a closer look reveals that the panda is not fully drawn. There are no outlines defining the body, no complete contours around the head, and no explicit boundaries separating limbs from background. Instead, the designer uses a series of carefully placed shapes (ears, eye patches, nose, and partial limbs) to imply the rest of the animal. The viewer’s mind fills in the missing information, completing the silhouette effortlessly.</p>
<p>This is closure at its most effective: the brain constructs a whole from fragments, creating a sense of completeness without visual overload.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/0c40effd-e4d2-4669-af1d-56faac976b4a.jpg" alt="0c40effd-e4d2-4669-af1d-56faac976b4a" style="display:block;margin:0 auto" width="522" height="784" loading="lazy">

<p><em>Fig 3:4: illustration of closure (Gestalt principle)</em></p>
<p>For example, a "hamburger menu" (three lines) isn't a literal drawer, but our brains "close" the shape to understand it represents a menu.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/b67253cd-2a2c-4565-9818-f5586dad6d36.jpg" alt="b67253cd-2a2c-4565-9818-f5586dad6d36" style="display:block;margin:0 auto" width="1244" height="236" loading="lazy">

<p><em>Fig 3:5: illustration of closure (Gestalt principle)</em></p>
<p>An example of closure in practice can be seen in step indicators commonly used in checkout flows. These components often rely on partial shapes, implied boundaries, and incomplete outlines to guide the user through a sequence of actions.</p>
<p>For instance, upcoming steps may be represented by dashed circles. Although the circles aren't fully drawn, the viewer immediately recognises them as complete shapes. The brain resolves the missing segments, allowing the interface to communicate progression without heavy borders or fully rendered icons. This subtle use of closure reduces visual clutter while preserving clarity.</p>
<p>Closure refers to the mind’s ability to perceive a complete, unified form even when parts of that form are missing.</p>
<h3 id="heading-35-figureground">3.5 Figure/Ground</h3>
<p>This principle describes the mind's tendency to separate an object (the figure) from its surrounding area (the ground or background). In web design, using a "modal" or "pop-up" relies on this: by blurring the background, you force the user to see the pop-up as the focal figure.</p>
<p>When a user clicks "Login" on a modal/lightbox, the background site often dims (the "Ground") while the login box stays bright and centered (the "Figure"). This immediate depth change tells the user exactly where their attention belongs.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/UCdjymjASOU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>

<p><em>Video 3.5.0 Video description of Figure/Ground (Gestalt Principle)</em></p>
<p>From the video above, you can see that when the Quick View button is clicked, the selected figure stands out while the background darkens. This contrast guides the user’s attention and helps them focus on the figure. Developers can use this technique to direct users’ attention to what matters most or to what they want users to notice.</p>
<p>This principle describes the mind's tendency to separate an object (the figure) from its surrounding area (the ground or background).</p>
<h3 id="heading-36-common-fate">3.6 Common Fate</h3>
<p>Elements that move in the same direction are perceived as more related than elements that are stationary or move in different directions. Think of a dropdown menu: when all sub-items slide down together, they are clearly part of the same "unit."</p>
<p>For example, when you click a FAQ header and five sub-items slide down at the exact same speed and direction, the "Common Fate" tells the user that all those items belong to that specific category. If they flew in from different directions, the relationship would be lost.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/T10xmlne6E4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>

<p><em>Video 3.6.1 Video description of common fate (Gestalt Principle)</em></p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/5FncGLRy0bM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>

<p><em>Video 3.6.2 Video description of common fate (Gestalt Principle)</em></p>
<p>From the video shown above, the e‑commerce animation example demonstrates these principles clearly by using two distinct motion patterns: a group of regular products that move upward together, and a pair of special‑category items that enter dramatically from the left. Through these contrasting movements, the interface communicates category differences without relying on text labels or explicit instructions.</p>
<p>Therefore, developers can use this motion‑based differentiation as a design strategy to guide users’ perception—allowing the interface to signal hierarchy, category structure, and product importance purely through animated behaviour rather than through static visual labels.</p>
<p>Elements that move in the same direction are perceived as more related than elements that are stationary or move in different directions.</p>
<h3 id="heading-37-focal-point">3.7 Focal Point</h3>
<p>Whatever stands out visually will capture and hold the viewer’s attention first. This is essentially the principle of emphasis. A bright "Sign Up" button in a sea of gray text acts as the focal point, directing the user's primary action.</p>
<p>For example, an alert banner or a pricing table should stand out from its surroundings. Beyond this, in a three-tier pricing table (Basic, Pro, Enterprise), the "Pro" column is often slightly larger or a different color. This creates a focal point that draws the eye to the "recommended" option immediately.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/711c9ee9-b546-4041-bf86-30c80154bd47.jpg" alt="711c9ee9-b546-4041-bf86-30c80154bd47" style="display:block;margin:0 auto" width="1784" height="646" loading="lazy">

<p><em>Fig 3:7: illustration of closure (Gestalt principle)</em></p>
<p>In visual interface design, the Gestalt principle of Focal Point plays a crucial role in directing user attention toward the most important element on a screen.</p>
<p>A focal point is created when one element breaks the established pattern of surrounding elements, making it stand out immediately.</p>
<p>In e‑commerce interfaces, this principle is often applied to highlight primary actions such as purchasing, subscribing, or upgrading. The “Buy Now” button provides a clear and practical example of how focal points function within a layout.</p>
<p>From the example above, the first two buttons share the same visual characteristics: neutral colours, and regular weight text. This repetition establishes a visual pattern that the user quickly becomes familiar with.</p>
<p>But the “Buy Now” button intentionally disrupts this pattern. It uses a bright colour, which contrasts sharply with the muted tones of the other buttons. This colour difference alone is enough to draw the eye, as humans are naturally sensitive to changes in hue and saturation within a uniform environment.</p>
<p>The Focal Point may sound like it's similar to the principle of Similarity, but the two operate in completely opposite ways within perceptual psychology.</p>
<p>Similarity explains how the mind naturally groups elements that share visual characteristics –&nbsp;such as colour, shape, or size – into coherent units. Once this grouping is established, the interface gains structure and predictability.</p>
<p>Focal Point, on the other hand, works by intentionally breaking that structure. Instead of reinforcing uniformity, it introduces a deliberate contrast – through colour, scale, brightness, or motion –&nbsp;to draw the viewer’s attention to one specific element.</p>
<p>In other words, Similarity creates the background pattern, while Focal Point identifies the one element that must stand out against that pattern.</p>
<p>Whatever stands out visually will capture and hold the viewer’s attention first.</p>
<h3 id="heading-design-takeaways-from-the-gestalt-principles">Design Takeaways from the Gestalt Principles</h3>
<p><strong>Use Spacing as Your Primary Grouping Tool:</strong><br>Elements that belong together should sit closer to each other than to anything else. Spacing communicates structure faster than borders or boxes. Use tight internal spacing (6–12px) for related items and wide external spacing (24–48px) to separate groups.</p>
<p><strong>Build a Strict, Consistent Visual System — and Stick to It:</strong><br>Define clear rules for button types, text styles, icon sizes, and alignment patterns. Consistent left‑aligned text blocks, predictable carousel lines, and stable flow patterns reduce cognitive load and make interfaces feel trustworthy.</p>
<p><strong>Guide the Brain With Spacing, Alignment, Consistency, Contrast, and Motion:</strong><br>The human brain is always trying to group, follow, and prioritise what it sees. Your job is to guide that instinct through intentional layout decisions, not fight against it.</p>
<h2 id="heading-40-von-restorff-effect-the-isolation-effect"><strong>4.0 Von Restorff Effect (The Isolation Effect)</strong>:</h2>
<p>This is the brainchild of Hedwig von Restorff, posited in 1933. In principle it states: An item that stands out is more noticable and more likely to be remembered than other items (Hunt, 1995).</p>
<p>So unique or visually distinct elements grab attention and are more memorable – in other words, distinctiveness dictates memory. When a user interacts with an interface, their brain naturally seeks patterns to minimize cognitive effort.</p>
<p>While consistency is generally a virtue in design, a perfectly uniform layout can lead to "banner blindness" or habituation, where the user stops noticing details.</p>
<p>By strategically breaking a pattern through changes in color, size, shape, or spacing, the developer can "isolate" an element, triggering a biological response that flags the item as high-priority.</p>
<p>Note that although the Focal Point principle may initially seem similar to the Von Restorff Effect, they describe two different psychological processes.</p>
<p>Focal Point is a Gestalt visual principle that explains how one element becomes the centre of attention within a composition because it carries the strongest visual contrast –&nbsp;through size, colour, brightness, position, or motion. Its purpose is to guide the viewer’s eye toward the most important element in the layout.</p>
<p>The Von Restorff Effect comes from cognitive psychology, not Gestalt theory. It states that an item that is noticeably different from a group of similar items is not only more attention‑grabbing but also more memorable.</p>
<p>So Focal Point is about where the eye goes first, while the Von Restorff Effect is about what the brain remembers later.</p>
<h3 id="heading-design-takeaways-from-von-restorff">Design takeaways from Von Restorff</h3>
<p><strong>Use Isolation to Make CTAs Impossible to Miss:</strong><br>On a page filled with neutral text and standard links, a single high‑contrast button (like a bold “Primary Blue” or “Emergency Red”) instantly becomes the standout element. This leverages the Von Restorff Effect to pull the user’s eye toward the most important action.</p>
<p><strong>Create a Visual “Hitch” in the Scan Path:</strong><br>A distinct CTA interrupts the user’s natural left‑to‑right, top‑to‑bottom scanning rhythm. This makes actions like “Buy Now” or “Sign Up” the first thing they notice and the last thing they forget.</p>
<p><strong>Make Critical Actions Visually Distinct:</strong><br>Because users naturally notice the one element that breaks a pattern, your most important actions should use deliberate contrast — color, size, shape, weight, or motion. Isolate key information instead of letting it blend into surrounding UI noise.</p>
<p><strong>Avoid Over‑Differentiation — or Nothing Stands Out:</strong> If every button is loud, animated, or uniquely styled, the interface becomes chaotic. The Von Restorff Effect only works when there is a clear, stable pattern — and you break it once, intentionally.</p>
<p><strong>To Sum Up:</strong> An item that stands out is more noticable and more likely to be remembered than other items.</p>
<h2 id="heading-50-jakobs-law"><strong>5.0 Jakob’s Law</strong></h2>
<p>Jakob’s Law states that users spend most of their time on other sites, so they expect your interface to behave like the ones they already know.</p>
<p>Familiar patterns — hamburger menus, top navigation, search icons, and clickable top‑left logos — reduce cognitive load because users don’t have to interpret anything new.</p>
<p>But while Jakob’s Law is foundational to UX, I think it can also unintentionally suppress innovation.</p>
<p>When developers over‑prioritise familiarity, they fall into a standardisation trap: endlessly optimising conventional patterns instead of exploring fundamentally better ones.</p>
<p>The Pie Menu is a perfect illustration of this. According to Fitts’s Law, the time required to reach a target depends on its distance and size. Linear menus place the last item much farther from the cursor than the first, creating uneven interaction costs.</p>
<p>Radial menus position every option at an equal distance from the centre, and their wedge‑shaped targets effectively grow larger as the pointer moves outward.</p>
<p>Mathematically, pie/radial menu are faster to interact with and more efficient — yet they remain rare in mainstream web design because they violate users’ expectations. In other words, Jakob’s Law keeps us locked into a familiar but suboptimal pattern simply because “that’s how it’s always been done.”</p>
<p>But the challenge is not choosing between familiarity and innovation, but balancing them.</p>
<p>This is where the Aesthetic–Usability Effect becomes powerful. Research shows that users perceive attractive interfaces as easier to use, and they are more forgiving of minor usability friction when the design is visually pleasing.</p>
<p>A beautifully crafted Pie Menu, for example, can encourage users to invest the small amount of learning required to use it. By applying aesthetic delight strategically, developers can introduce innovative patterns without overwhelming users.</p>
<p>The principle that emerges is simple: Be conventional where it matters, and innovative where it delights.</p>
<h3 id="heading-design-takeaway-from-jakobs-law">Design Takeaway from Jakob's Law</h3>
<p><strong>Keep Trust‑Critical Elements Predictable:</strong><br>Navigation, search, authentication, and other high‑stakes interactions must follow established conventions. Users rely on these patterns for speed, confidence, and safety — this is where Jakob’s Law should be respected without exception.</p>
<p><strong>Experiment Only in Low‑Risk, High‑Creativity Areas:</strong><br>In creative or productivity‑focused zones — like editing tools in a photo app — you can safely introduce new interaction models such as radial menus, gesture wheels, or context‑aware tool selectors. These areas invite exploration and benefit from efficiency‑driven innovation.</p>
<p><strong>To Sum Up:</strong> Be conventional where it matters, and innovative where it delights.</p>
<h2 id="heading-60-millers-law"><strong>6.0 Miller’s Law</strong></h2>
<p>Miller’s Law originates from George A. Miller’s classic paper “The Magical Number Seven, Plus or Minus Two.” It states that the average person can hold only about 7 (±2) chunks of information in working memory at any given moment (Miller, 1956).</p>
<p>Crucially, Miller emphasised that the brain doesn’t store isolated items — it groups them into meaningful units called chunks. Because working memory is so limited, developers must structure information in ways that respect this cognitive boundary.</p>
<p>This principle has direct implications for interface design. Long, unbroken strings of information overwhelm users, whereas chunked formats are far easier to process.</p>
<p>For example, instead of displaying a phone number as 1234567890, formatting it as 123‑456‑7890 transforms ten digits into three manageable chunks. The same logic applies to navigation: aim for five to nine primary menu items, and if you need more, group them into categories. Users remember the category as a single chunk rather than each individual link.</p>
<p>Miller’s Law also explains why long forms are so intimidating. When a user sees 30 fields on one page, their brain interprets it as a single, massive task — far beyond the 7±2 limit.</p>
<p>A progressive stepper solves this by breaking the form into smaller stages of 5–7 fields each. This reduces cognitive load, creates a sense of progress, and significantly lowers abandonment rates.</p>
<p>The same principle applies to product listings or search results. Expecting users to compare 50 items at once is unrealistic. Instead, provide strong filtering tools so users can narrow the set to a manageable size — ideally within the range their working memory can meaningfully evaluate.</p>
<p>In essence, Miller’s Law reminds developers that humans don’t process information in bulk. They process it in structured, meaningful chunks.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6998def93dc17c4862075045/3add2891-c6f8-4df1-a044-6cea6c5f1945.jpg" alt="Image of progressive stepper" style="display:block;margin:0 auto" width="818" height="960" loading="lazy">

<p><em>Fig 6.0: Illustrating progressive stepper</em></p>
<p>In the example above, the interface uses both a progress bar and a stepper to guide the user through multiple stages of a task. After completing the first page and selecting “Continue,” the user moves to the next step, and the progress bar updates accordingly. This creates a clear sense of forward movement and accomplishment.</p>
<p>By breaking the process into smaller segments, the interface prevents cognitive overload. If all the information were presented on a single page, users might feel overwhelmed, unsure where to begin, or discouraged by the sheer volume of work.</p>
<p>A step‑by‑step flow transforms a large task into a sequence of manageable actions, increasing the likelihood of completion.</p>
<h3 id="heading-design-takeaway-from-millers-law">Design Takeaway from Miller's Law</h3>
<p><strong>Respect the 7±2 Working‑Memory Limit:</strong><br>Users can only hold about seven chunks of information at once. Long, unbroken content overwhelms them, while chunked information is instantly easier to process.</p>
<p><strong>Chunk Information Into Meaningful Units:</strong><br>The brain doesn’t store isolated items — it groups them. Format data (like phone numbers), menus, and settings into clear, memorable chunks instead of long, flat lists.</p>
<p><strong>Keep Navigation Within 5–9 Primary Items:</strong><br>If you need more than nine options, group them into categories. Users remember the category as a single chunk, not each individual link.</p>
<p><strong>Break Long Forms Into Smaller Steps:</strong><br>A 30‑field form feels like one giant task. A progressive stepper with 5–7 fields per step keeps users below the cognitive overload threshold and dramatically reduces abandonment.</p>
<p><strong>Reduce Comparison Load With Strong Filters:</strong><br>Expecting users to compare 50 products at once is unrealistic. Provide filtering tools that shrink the decision set to something the working memory can actually handle.</p>
<p><strong>Design for Chunked Thinking, Not Bulk Processing:</strong><br>Humans don’t process information in bulk — they process structured, meaningful groups. Interfaces that respect this limitation feel lighter, faster, and more intuitive.</p>
<p><strong>To Sum Up:</strong> A step‑by‑step flow transforms a large task into a sequence of manageable actions, increasing the likelihood of completion.</p>
<h2 id="heading-70-the-goal-gradient-hypothesis">7.0 The Goal-Gradient Hypothesis</h2>
<p>This is the perfect moment to introduce the Goal‑Gradient Hypothesis, originally proposed by behaviorist Clark Hull in 1932 (Yablonski, 2022). The hypothesis states that people become more motivated as they get closer to achieving a goal. In other words, users naturally accelerate their engagement when they sense they are nearing completion.</p>
<p>This principle is incredibly powerful in UX design, especially for progress tracking, gamification, and reward systems.</p>
<p>The takeaway is straightforward: Because users are more motivated near the finish line, progress indicators should be prominent and meaningful.</p>
<p>Percentages, progress bars, and step counters reinforce momentum. Micro‑achievements — such as badges, checkmarks, or subtle confetti — amplify motivation by celebrating small wins.</p>
<p>Tasks should be broken into measurable milestones so users can see themselves advancing.</p>
<p>This is why e‑learning platforms display messages like “You’ve completed 8 of 10 lessons — almost there!” and why fitness apps highlight progress with prompts such as “3 km done, 2 km to go.” These cues leverage the goal‑gradient effect to keep users engaged, energized, and eager to finish.</p>
<p>By combining progressive steppers with clear progress feedback, developers create interfaces that feel lighter, more encouraging, and far more motivating — ultimately improving completion rates and overall user satisfaction.</p>
<p>But what happens when a goal isn't completed? Why do we sometimes feel uncomfortable leaving things unfinished? That discomfort is explained by another psychological principle called the Zeigarnik Effect — the tendency for people to remember and feel tension about incomplete tasks. We will look at this next.</p>
<h3 id="heading-design-takeaway-from-goal-gradient-hypothesis">Design Takeaway from Goal-Gradient Hypothesis</h3>
<p><strong>Make Progress Visible to Boost Motivation:</strong><br>According to the Goal‑Gradient Hypothesis, users naturally speed up as they sense they’re nearing completion. Prominent progress bars, percentages, and step counters tap into this instinct and keep momentum high.</p>
<p><strong>Celebrate Micro‑Achievements to Reinforce Engagement:</strong><br>Badges, checkmarks, subtle confetti, and “step completed” cues reward small wins. These micro‑rewards amplify motivation and make long tasks feel lighter and more achievable.</p>
<p><strong>Break Tasks Into Measurable Milestones:</strong><br>Users stay motivated when they can see themselves advancing. Divide complex flows into clear steps so progress feels tangible rather than overwhelming.</p>
<p><strong>Use Progress Feedback to Drive Completion:</strong><br>Messages like “8 of 10 lessons completed — almost there” or “3 km done, 2 km to go” leverage the goal‑gradient effect to energise users and pull them toward the finish line.</p>
<p><strong>Combine Steppers With Clear Feedback for Maximum Impact:</strong><br>Progressive steppers paired with strong visual feedback create interfaces that feel encouraging, structured, and motivating — dramatically improving completion rates.</p>
<div class="embed-wrapper"><iframe width="560" height="315" src="https://www.youtube.com/embed/-mDuFB3W2bg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>

<p><em>Video 8.0 : Video illustrating goal gradient</em></p>
<p><strong>To Sum Up:</strong> People become more motivated as they get closer to achieving a goal.</p>
<h2 id="heading-80-zeigarnik-effect"><strong>8.0 Zeigarnik Effect</strong></h2>
<p>The Zeigarnik Effect is a psychological principle stating that people remember unfinished or interrupted tasks better than completed ones (Cherry, 2024).</p>
<p>Memory begins with sensory input, which is processed into short-term memory. Unfinished tasks persist in our thoughts, leading to active recall. This ongoing engagement can turn them into long-term memories, enhancing recall until resolved. This increases engagement, encourages task completion, improves retention, and drives conversions.</p>
<p>So because people remember unfinished tasks better than completed ones (Zeigarnik Effect), developers use progress indicators to make users aware that something is incomplete and motivate them to finish it.</p>
<p>In your designs, you can break long forms into multi-step processes to encourage completion and display profile completion percentages (for example, 70% complete) to push users toward 100%.</p>
<p>This is the main reason why e-commerce platforms send abandoned cart reminders to bring users back to complete their purchases. It's also why apps use streak systems to encourage daily engagement and habit formation and learning platforms show course completion bars to motivate users to finish modules.</p>
<h3 id="heading-design-takeaway-from-zeigarnik-effect">Design Takeaway from Zeigarnik Effect</h3>
<p><strong>Unfinished Tasks Stay Active in Memory — Use That to Drive Completion:</strong><br>Because incomplete tasks linger in working memory (Zeigarnik Effect), users naturally keep thinking about what they haven’t finished. This tension boosts recall, engagement, and the likelihood of returning to complete the task.</p>
<p><strong>Make Incompleteness Visible With Progress Indicators:</strong><br>Progress bars, percentages, and step counters remind users that something is still unfinished. This gentle psychological pressure motivates them to continue until the task is complete.</p>
<p><strong>Break Long Flows Into Multi‑Step Processes:</strong><br>A massive form feels overwhelming, but a stepper with smaller chunks keeps users moving. Showing “70% complete” nudges them toward finishing the last stretch.</p>
<p><strong>Use Reminders to Re‑activate Unfinished Intent:</strong><br>Abandoned cart emails, streak reminders, and “continue your lesson” prompts work because the unfinished task is already active in the user’s mind. The reminder simply pulls them back into the loop.</p>
<p><strong>Celebrate Completion to Close the Cognitive Loop:</strong><br>Checkmarks, confirmations, and completion badges give users closure. This resolves the mental tension created by the unfinished task and reinforces positive behaviour.</p>
<p><strong>To Sum Up:</strong> Unfinished tasks persist in our thoughts, leading to active recall.</p>
<h2 id="heading-90-teslers-law"><strong>9.0 Tesler’s Law</strong>:</h2>
<p>This law was proposed by Lawrence Tesler. He was a computer scientist known for his work on human-computer interaction, and he contributed significantly to making software more user-friendly, including work on cut, copy, and paste functionality.</p>
<p>This law is otherwise known as the Law of Conservation of Complexity. The core Idea here is every process has a certain amount of “inherent complexity" that can't be removed. You can only decide who handles it: the user or the system.</p>
<p>Some examples of these inherent complexities might be:</p>
<ul>
<li><p>translating user actions into correct operations behind the scenes,</p>
</li>
<li><p>handling unreliable or slow network connections,</p>
</li>
<li><p>connecting with third-party APIs, services, or legacy systems,</p>
</li>
<li><p>sorting large datasets quickly,</p>
</li>
<li><p>performing complex search operations</p>
</li>
<li><p>managing version changes and compatibility issues,</p>
</li>
<li><p>managing state, interactions, and animations without confusing the user.</p>
</li>
</ul>
<p>All of these can be inherently complex, but it's the job of the developer to deal with the complexity.</p>
<p>As a developer, you should always try as much as possible to push complexity to the system. For example, instead of making a user type their full address manually, use an Auto-complete API (Google’s Places and Map is best for this). The complexity of finding and validating the address still exists, but the software handles the work for them.</p>
<p>Here's a practical example: let’s say you're designing a student platform that requires users to enter their university name. A practical approach would be to store an array of all universities in the UK in your codebase (This is the hard part Tesla hinted at).</p>
<p>As the user types, they don't need to enter the full name, and their full university name is shown (relating to what they have typed). For instance, if they intend to type “University of Sheffield,” simply typing “Sheff” should prompt the system to display the full university name, which they can then select.</p>
<p>In Dart, you can use a package like fuzzysearch to implement this kind of intelligent matching.</p>
<p>The advantage of this approach is greater than it first appears. It improves data consistency because users often enter the same information in different ways. For example, some users might type “Uni of Sheff,” others “Sheffield University,” and others “Uni of Sheffield,” while all are referring to “University of Sheffield.”</p>
<p>This is how messy data is created, and it creates more work for data analysts. Little wonder that data analysts spend up to 70% of their time cleaning data.</p>
<p>If developers invested more time in structuring how data is collected to ensure consistency, there would be far less work downstream for analysts. This same logic should be applied in how we collect date, time, and other information.</p>
<p>So apart from people's names and email addresses, you should try to standardize the data your app collects as much as possible. Use date and time pickers, stepper controls, input masks, checkboxes, dropdown menu and radio buttons, toggle switches. and so on.</p>
<p>The essence of removing complexity from the user is not only about improving usability, but also about ensuring that the data collected is standardised, structured, and consistent.</p>
<h3 id="heading-design-takeaway-from-teslers-law">Design Takeaway from Tesler's Law</h3>
<p><strong>Push Complexity to the System, Not the User:</strong><br>Every process contains unavoidable complexity. Your job is to handle it behind the scenes so the user experiences the simplest possible interaction.</p>
<p><strong>Automate Tasks Users Shouldn’t Have to Think About:</strong><br>Use tools like autocomplete, fuzzy search, intelligent defaults, and validation APIs to remove manual effort. The complexity still exists — but the system absorbs it instead of the user.</p>
<p><strong>Standardise Inputs to Prevent Messy Data:</strong><br>Users enter the same information in wildly different ways. Use pickers, dropdowns, input masks, radio buttons, and toggles to enforce consistent, structured data collection.</p>
<p><strong>Handle Inherent Technical Complexity Internally:</strong><br>Network issues, API quirks, large dataset sorting, search optimisation, state management, and animation logic are all developer responsibilities. Users should never feel this complexity.</p>
<p><strong>To Sum Up:</strong> Every process contains unavoidable complexity. Your job as a developer is to handle it behind the scenes so the user experiences the simplest possible interaction.</p>
<h2 id="heading-100-peak-end-rule"><strong>10.0 Peak End Rule</strong>:</h2>
<p>In 1993, Daniel Kahneman, Barbara Fredrickson, Charles Schreiber, and Donald Redelmeier invited volunteers into a lab for what sounded like a simple experiment. The task was straightforward: place a hand into a container of painfully cold water (Kahneman et al., 1993)</p>
<p>In the first round, participants kept their hand in 14°C water for 60 seconds. It was uncomfortable, sharp, and unpleasant but after one minute, it was over.</p>
<p>In the second round, they again endured 60 seconds in 14°C water. But this time, they were asked to keep their hand in for an extra 30 seconds. The temperature was raised slightly to 15°C. Still cold. Still unpleasant. Just slightly less intense.</p>
<p>Objectively, the second experience was worse. It lasted 90 seconds instead of 60. More total pain. More suffering.</p>
<p>Later, the researchers asked a simple question:</p>
<p>If you had to repeat one of the trials, which would you choose?” Surprisingly, most participants chose the longer one.</p>
<p>Why would anyone choose more pain?</p>
<p>The researchers realised something profound: people don’t remember experiences by calculating total discomfort. Instead, the mind summarizes the experience using just two key moments — the most intense point (the peak) and the final moment (the end).</p>
<p>In both trials, the peak pain was the same: 14°C. But the longer trial ended slightly better, at 15°C. That small improvement at the end reshaped how the entire episode was remembered. The participants’ “experiencing self” suffered more during the longer trial. But their “remembering self” preferred it because it ended on a less painful note.</p>
<p>From this, the researchers introduced what became known as the Peak–End Rule: we judge experiences largely by their most intense moment and how they finish, not by how long they last.</p>
<p>Since people largely judge an experience by how it ends, developers should focus on designing satisfying confirmation screens and smooth exit interactions. You should concentrate less on making every single moment perfect and instead prioritise optimising the peak and final moments.</p>
<p>A negative ending can overshadow an otherwise good experience, so carefully avoid frustrating final steps such as unexpected fees or confusing confirmations.</p>
<p>Emotional intensity strongly shapes memory, which is why many apps incorporate celebration animations, rewards, or success messages at key moments to leave a lasting positive impression.</p>
<h3 id="heading-design-takeaway-from-peak-end-rule">Design takeaway from Peak End Rule</h3>
<p><strong>People Judge Experiences by the Peak and the Ending — Not the Total Duration:</strong><br>Users don’t remember every moment. They remember the most intense point and how the experience ends. A slightly better ending can completely reshape how the entire interaction is remembered.</p>
<p><strong>Prioritise Strong, Positive Endings in Your UX Flows:</strong><br>A smooth final step, a clear confirmation, or a satisfying success screen leaves a disproportionately strong impression. A bad ending can overshadow an otherwise great experience.</p>
<p><strong>Design for Emotional Peaks at Key Moments:</strong><br>Celebration animations, rewards, checkmarks, and success messages create memorable emotional spikes. These peaks anchor the experience in the user’s memory.</p>
<p><strong>Don’t Try to Perfect Every Moment — Perfect the Right Moments:</strong><br>Optimise the peak and the end of the journey. These two moments define how users recall the entire interaction.</p>
<p><strong>Avoid Negative Surprises at the Finish Line:</strong><br>Unexpected fees, confusing confirmations, or friction at the last step can ruin the memory of the whole process. Protect the ending carefully.</p>
<p><strong>To Sum Up:</strong> We judge experiences largely by their most intense moment and how they finish, not by how long they last.</p>
<h2 id="heading-110-postels-law"><strong>11.0 Postel’s Law</strong>:</h2>
<p>Jon Postel’s famous principle – “Be conservative in what you send, be liberal in what you accept” –&nbsp; is a philosophy of kindness in software design. At its core, the principle argues that systems should be generous with what they accept from users, yet disciplined and predictable in what they output.</p>
<p>When developers follow this approach, users feel supported and understood. When they don’t, users feel punished for being human.</p>
<p>A user’s input is rarely perfect. People type quickly, make mistakes, follow their own habits, or rely on formats familiar to them. A robust system embraces this reality. It accepts messy, human input and quietly transforms it into clean, standardized data.</p>
<p>Real people don't think in strict formats. They write dates the way they learned in school, type phone numbers the way they say them aloud, and enter names and addresses in whatever structure feels natural to them.</p>
<p>A rigid system will reject anything that doesn’t match its narrow expectations, but a robust system, by contrast, adapts to the user.</p>
<p>Consider dates. A brittle interface might demand MM/DD/YYYY and reject everything else. A more humane system accepts a wide range of formats — “1 May 2024,” “2024‑05‑01,” “05/01/24,” or “May 1st, 2024” — and quietly converts them into a standard internal representation. This is where the complex handling described by Tesla's Law comes into play (Shifting complexity to the system, rather than the user).</p>
<p>Phone numbers follow the same pattern. People might enter (555) 123 4567, 555‑123‑4567, 5551234567, or +1 555 123 4567. A fragile system throws errors. A robust one parses all of them using libraries like libphonenumber and moves on.</p>
<p>Addresses are equally varied. “221B Baker St,” “221‑B Baker Street,” and “221 Baker St., Apt B” all refer to the same place. A forgiving system normalizes these instead of rejecting them.</p>
<p>Even names can be surprisingly complex. Hyphens, apostrophes, multiple words, and titles are all part of real human identity. Rejecting “O’Connor,” “Jean‑Luc,” or “Dr. Sarah Lee” is not just technically incorrect — it's disrespectful to the user.</p>
<p>Search bars offer another clear example. A strict search bar demands perfect spelling and exact phrasing. A robust one handles typos (“restuarant”), partial words (“resta”), synonyms (“food places”), and natural language (“where can I eat nearby”). It meets the user where they are instead of forcing them to think like a machine.</p>
<p>Currency should be normalized to a clear format such as GBP 5.00, no matter whether the user typed “£5,” “5 pounds,” or “5 GBP.”</p>
<p>Even file uploads benefit from standardization: whether the user uploads .jpeg, .jpg, .JPG, or .JPEG, the system should store everything as .jpg.</p>
<p>Error messages follow the same principle. Vague feedback like “Invalid password” leaves users confused and frustrated.</p>
<p>A clear, conservative message — “Incorrect password. Please try again.” — respects the user’s time. And instead of hiding password requirements, the system should state them upfront: minimum eight characters, at least one uppercase letter, at least one number.</p>
<p>Predictability reduces friction.</p>
<p>Because users inevitably make mistakes or enter data in unexpected ways, developers should design input fields that are tolerant rather than brittle. This means accepting flexible formats, offering autocorrect or intelligent parsing, and using forgiving validation rules that interpret the user’s intent instead of rejecting their effort.</p>
<p>Clear instructions, tooltips, and visible requirements should appear before submission so users understand what the system expects without trial and error.</p>
<p>When errors do occur, the interface should handle them gently—never crashing, and never forcing the user to start over.</p>
<p>Even simple variations, such as phone numbers typed with spaces, dashes, or parentheses, should be accepted and normalized behind the scenes.</p>
<p>By embracing flexibility on the input side and clarity on the output side, developers create systems that feel humane, resilient, and respectful of the way real people actually behave.</p>
<h3 id="heading-design-takeaway-from-postels-law">Design Takeaway from Postel's Law</h3>
<p><strong>Accept Messy Human Input, Output Clean Structured Data:</strong><br>Users type dates, names, phone numbers, and addresses in unpredictable ways. A humane system accepts this variability and quietly normalises it into a consistent internal format.</p>
<p>Rigid interfaces punish users for being human. Robust interfaces interpret intent — handling typos, partial matches, synonyms, and natural language without complaint.</p>
<p>Also accept variations in spacing, punctuation, casing, and structure. Let users type naturally — the system should handle the complexity, not them.</p>
<p><strong>Be Flexible With Input, Be Strict With Output:</strong><br>This is the heart of Postel’s Law. Let users express information naturally, but ensure your system stores and displays it in a predictable, standardised way.</p>
<p><strong>Use Intelligent Parsing and Autocorrection to Reduce Errors:</strong><br>Libraries like libphonenumber, fuzzy search, and natural‑language parsers allow systems to accept a wide range of formats while still producing clean, reliable data.</p>
<p><strong>Normalise Everything Behind the Scenes:</strong><br>Dates, phone numbers, currency, file extensions, and addresses should all be standardised internally. This prevents messy data and reduces downstream cleanup work.</p>
<p><strong>Provide Clear, Predictable Feedback:</strong><br>Error messages should be specific and helpful. Requirements should be visible upfront. Users should never be surprised, confused, or forced to start over.</p>
<p><strong>Combine Postel’s Law With Tesler’s Law:</strong><br>Shift complexity to the system. Intelligent handling of messy input reduces cognitive load, improves usability, and ensures consistent, high‑quality data.</p>
<p><strong>To Sum Up:</strong> A rigid system will reject anything that doesn’t match its narrow expectations, but a robust system, by contrast, adapts to the user.</p>
<h2 id="heading-120-doherty-threshold"><strong>12.0 Doherty Threshold</strong>:</h2>
<p>The Doherty Threshold is a principle in human–computer interaction which proposes that systems should respond quickly enough to keep users actively engaged (Mod 2024).</p>
<p>When response times stay below a certain limit, users remain focused and productive. But once performance already meets this optimal responsiveness level, making the system even faster or adding extra capability doesn't significantly enhance satisfaction or efficiency.</p>
<p>The idea was introduced by Walter J. Doherty in 1976 in his paper “A Comparison of Programming Systems and Doherty Threshold.” His research showed that maintaining rapid system feedback fast enough to sustain continuous interaction has a stronger impact on productivity than simply increasing system power or features beyond that point.</p>
<p>Doherty proposes that this shouldn't be greater than 400ms Rule: If the system responds within this window, the user feels in total control. If the response takes longer, the user's attention begins to wander, and their "train of thought" is broken.</p>
<p>The challenge, of course, is that not every operation can realistically complete within 400ms. Some tasks require heavy computation, large network calls, or complex rendering. This is where the concept of perceived performance becomes essential.</p>
<p>Even when the system can't finish the work quickly, it can feel fast by responding instantly at the UI level. Developers can achieve this illusion of speed through a combination of thoughtful design patterns and disciplined engineering practices.</p>
<p>On the technical side, performance begins with reducing unnecessary work. Keeping the number of HTML elements low helps the browser render faster. Rendering only the visible portion of long lists prevents the Document Oject Model (DOM) from becoming bloated. Splitting scripts and deferring non‑critical code ensures that essential interactions load first.</p>
<p>Using CSS transforms and opacity changes avoids expensive layout recalculations. Lazy‑loading images, videos, and scripts ensures that the interface becomes interactive long before all assets are downloaded.</p>
<p>These optimizations don’t just improve raw speed — they create the foundation for interfaces that feel responsive.</p>
<h3 id="heading-design-takeaways-from-doherty-threshold">Design Takeaways from Doherty Threshold</h3>
<p><strong>Instant Feedback</strong>: When a user clicks a button, provide a visual change (like a button press animation or a spinner) immediately, even if the background task takes longer.</p>
<p><strong>Skeleton Screens</strong>: Use placeholder blocks that mimic the layout of the page while data loads. This makes the app feel like it is responding instantly.</p>
<p><strong>Progressive Loading</strong>: Load text and basic structures first, then "pop in" high-resolution images later.</p>
<p><strong>Optimistic UI</strong>: When a user hits "Save," don't wait for the server. Update the UI instantly (Doherty) and handle the "messy" data formatting on the backend (Postel).</p>
<p><strong>Live Inline Validation</strong>: Show a green checkmark or a helpful error message as the user types. This keeps them below the 400ms "thought-break" limit.</p>
<p><strong>Debouncing</strong>: In search bars, start showing results after a few keystrokes so the user feels the app is "predicting" their needs.</p>
<p><strong>To Sum Up:</strong> When response times stay below a certain limit, users remain focused and productive. But once performance already meets this optimal responsiveness level, making the system even faster or adding extra capability doesn't significantly enhance satisfaction or efficiency.</p>
<h2 id="heading-130-serial-position-effect-primacy-and-recency"><strong>13.0 Serial Position Effect (Primacy and Recency)</strong>:</h2>
<p>Murdock’s study investigated how the position of a word in a list affects recall, known as the serial position effect. He presented 103 psychology students with lists of 10 to 40 words, one at a time, at either 1 or 2 seconds per word (McLeod, 2025).</p>
<p>Participants were divided into six groups, each experiencing a different combination of list length and presentation rate, and were asked to recall as many words as possible in any order.</p>
<p>The results showed that participants were most likely to remember words at the beginning of the list (primacy effect) and at the end of the list (recency effect), while words in the middle were recalled less often. The recency effect persisted even in longer lists, and the middle section of the recall curve formed a flat asymptote.</p>
<p>Murdock explained this using the multi-store model of memory: early words were rehearsed and transferred to long-term memory, last words remained in short-term memory, and middle words were neither sufficiently rehearsed nor retained, leading to poorer recall.</p>
<p>The experiment demonstrated that memory performance varies systematically with the position of information in a sequence.</p>
<p>This is the reason why the most important information or actions should never be buried in the middle.</p>
<p>As a developer, you should put your most critical navigation links (like "Home" or "Dashboard") at the far left or the top of a list. In a pricing table, put the most popular or recommended plan on the Place "Final Actions" (like "Log Out," "Cart," or "Support") at the end of a menu or the far right of a navigation bar.</p>
<p>In a long onboarding flow, put the most exciting benefit of the app on the very last slide so the user enters the app feeling motivated.</p>
<p>Avoid placing highly important buttons in the middle of a row. If you have a row of 7 buttons, the user is statistically likely to overlook the 4th one.</p>
<h3 id="heading-design-takeaways-serial-position-effect">Design Takeaways <strong>Serial Position Effect</strong></h3>
<p><strong>Place Critical Items at the Beginning or End — Never the Middle:</strong><br>Users reliably remember the first and last items in any sequence (primacy and recency). Anything placed in the middle is statistically more likely to be forgotten or ignored. Also, actions such as “Log Out,” “Cart,” “Support,” or “Checkout” should sit at the far right or bottom — the natural recency position.</p>
<p><strong>Put Essential Navigation Links at the Far Left or Top:</strong><br>Links like “Home,” “Dashboard,” or “Overview” should appear at the start of a menu, where recall and recognition are strongest.</p>
<p><strong>To Sum Up:</strong> The results showed that participants were most likely to remember words at the beginning of the list (primacy effect) and at the end of the list (recency effect), while words in the middle were recalled less often.</p>
<h2 id="heading-140-occams-razor"><strong>14.0 Occam’s Razor</strong>:</h2>
<p>Although first articulated in the 14th century by the Franciscan friar William of Ockham, Occam’s Razor remains one of the most indispensable principles in a developer’s toolkit. In fact, skipping this law while discussing other theories and principles would be like skipping the glue that holds the entire framework together.</p>
<p>At its core, Occam’s Razor states that “among competing explanations, the simplest one is usually the best.”</p>
<p>For example, if two user interfaces achieve the same goal, the one with fewer visual elements is typically superior because it requires less processing power.</p>
<p>The fundamental takeaway for modern developers regarding Occam’s Razor is that complexity is a tax on the user’s cognitive resources.</p>
<p>In an era of information density, the developer's primary role is no longer to provide "more" features – rather, it's to curate the most direct path to a solution.</p>
<p>In practice, Occam’s Razor becomes a reminder to keep things as simple as possible. This “less is more” mindset shapes everything from navigation to forms.</p>
<p>A good rule for navigation is the Rule of Five: aim for three to five main menu items instead of a long, overwhelming list. This keeps choices clear and prevents users from freezing up when they see too many options.</p>
<p>The same idea applies to data entry. When you ask only for the information that truly matters, you respect the user’s time and reduce the chance of “form fatigue,” which is one of the biggest reasons people abandon sign‑ups or checkout flows.</p>
<p>Simplicity isn’t just elegant — it’s practical, humane, and far more effective.</p>
<h3 id="heading-design-takeaway-from-occams-razor">Design Takeaway from Occam's Razor</h3>
<p><strong>Choose the Simplest Effective Solution:</strong><br>When two designs achieve the same goal, the one with fewer elements is almost always better. Simplicity reduces cognitive load and speeds up user decision‑making.</p>
<p><strong>Simplicity Is Not Just Aesthetic — It’s Humane:</strong><br>Clear, minimal interfaces respect the user’s time, reduce friction, and make the product feel effortless. Simplicity is both a design strategy and an act of empathy.</p>
<p><strong>To Sum Up:</strong> Simplicity isn’t just elegant — it’s practical, humane, and far more effective.</p>
<h2 id="heading-150-parkinsons-law">15.0 Parkinson's Law</h2>
<p>Occam’s Razor teaches us to prefer the simplest solution that works. But why do we so often end up with complex systems in the first place? That tendency is explained by another principle: Parkinson’s Law.</p>
<p>Parkinson’s Law states that "work expands to fill the time available for its completion". In design, this means projects often become overly complex or take longer than necessary if given too much time, resulting in inefficient, over-designed, or cluttered interfaces.</p>
<p>In design, this manifests as Feature Creep. If you give yourself three months to build an app, you will spend three months adding "nice-to-have" animations, extra settings toggles, and niche edge cases that nobody asked for and in reality, what you have added isn’t that important.</p>
<p>You just succeeded in adding layers of complexity that might ends up violating some of the laws we spoke about. Occam’s Razor reminds us that the simplest solution is often the most effective.</p>
<p>By being aware of Parkinson’s Law and the tendency for work to expand, developers can manage their time intentionally and focus only on what truly matters.</p>
<h3 id="heading-design-takeaway-for-parkinsons-law">Design Takeaway for Parkinson's law</h3>
<p><strong>Set Clear Constraints to Keep Designs Focused:</strong><br>Intentional time limits and scope boundaries prevent over‑designing. Constraints force clarity, prioritisation, and simplicity.</p>
<p><strong>Build Only What Truly Matters for the User:</strong><br>Parkinson’s Law reminds you to resist the urge to fill time with unnecessary features. Focus on the core experience, not the edge cases nobody asked for.</p>
<p><strong>Use Occam’s Razor to Counterbalance Parkinson’s Law:</strong><br>As work expands, complexity grows. Occam’s Razor pulls you back to the simplest effective solution. Together, the two principles prevent bloated, over‑engineered products.</p>
<p><strong>To Sum Up:</strong> Work expands to fill the time available for its completion</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Human-centered design is deeply influenced by a set of psychological principles that explain how users perceive, process, and interact with digital systems.</p>
<p>Among these, Fitts’s Law establishes that the time required to acquire a target depends on its size and distance. In practice, this means that larger and closer elements are easier and faster to interact with.</p>
<p>To apply this in practice, developers should make primary call-to-action elements prominent, large, and easily reachable –&nbsp;especially in mobile interfaces where thumb accessibility is critical.</p>
<p>Closely related to decision-making is Hick’s Law, which states that the more choices a user is presented with, the longer it takes to make a decision. Excessive options can overwhelm users and lead to decision fatigue.</p>
<p>To address this, developers should simplify interfaces, minimise unnecessary options, and guide users through processes step-by-step rather than presenting everything at once.</p>
<p>Another important cognitive principle discussed is Miller’s Law, which suggests that the average person can hold approximately seven (plus or minus two) items in working memory at a time. This limitation highlights the need to present information in manageable chunks.</p>
<p>By breaking content into smaller groups and avoiding information overload, developers can improve comprehension and usability.</p>
<p>User expectations are strongly shaped by Jakob’s Law, which says that people spend most of their time on other websites and therefore expect similar patterns across digital products.</p>
<p>Instead of reinventing basic interactions, developers should follow familiar conventions such as placing the logo in the top‑left, the shopping cart in the top‑right, and keeping scrolling behaviour predictable.</p>
<p>But innovation is still possible where it truly adds value. As we discussed with the Aesthetic‑Usability Effect, users are far more tolerant of new or unusual design patterns when the interface is visually appealing and thoughtfully crafted.</p>
<p>The Gestalt Principles provided additional insight into how users visually organise information. The principle of proximity suggests that objects placed close together are perceived as related, so grouping related elements improves clarity. Similarity indicates that elements with consistent colours, shapes, or styles are seen as belonging together, reinforcing visual hierarchy and function. Closure explains that users can perceive incomplete shapes as complete, allowing for minimalistic designs where the brain fills in missing details. Continuity highlights that users naturally follow smooth visual paths, meaning layouts should guide the eye logically through alignment and structure.</p>
<p>We also looked at The Von Restorff Effect which emphasizes that elements which stand out are more likely to be remembered. By using contrast in colour, size, or design, important features such as buttons or alerts can capture user attention.</p>
<p>Managing complexity was addressed by Tesler’s Law, which asserts that every system has inherent complexity that cannot be eliminated but only managed.</p>
<p>Developers must therefore shift complexity away from the user by simplifying interfaces while handling intricate processes behind the scenes.</p>
<p>The Zeigarnik Effect reveals that people remember unfinished tasks better than completed ones, creating a sense of mental tension. This can be leveraged by incorporating progress indicators, checklists, and reminders that encourage users to complete tasks.</p>
<p>Similarly, the Peak-End Rule suggests that users judge an experience based on its most intense moment and its conclusion. Developers should create memorable highlights and ensure a smooth, satisfying ending to user journeys.</p>
<p>We also discussed the Goal-Gradient Effect, which explains that users become more motivated as they approach the completion of a task. By showing progress –such as indicating that a process is “80% complete” –&nbsp;and breaking tasks into stages, developers can encourage users to finish what they have started.</p>
<p>In terms of system interaction, Postel’s Law advises developers to be flexible in accepting user input while maintaining strict standards for output. This means allowing different input formats while ensuring consistent and reliable system responses.</p>
<p>Performance is equally important, as highlighted by the Doherty Threshold, which shows that productivity increases when system response times stay under 400 milliseconds. Fast systems keep users engaged and create a sense of ease.</p>
<p>This means that developers should focus on building interfaces that feel instant, even when real processing takes longer, by combining smart engineering practices with thoughtful design patterns that maintain the illusion of speed.</p>
<p>Memory and attention are further explained by the Serial Position Effect, where users tend to remember the first and last items in a sequence more than those in the middle. Developers should position key information or actions at the beginning or end of lists.</p>
<p>Simplicity is reinforced by Occam’s Razor, which argues that the simplest solution is often the most effective. Eliminating unnecessary features reduces friction and enhances usability, and we further discussed about Parkinson’s Law, which suggests that tasks expand to fill the time available, indicating the importance of setting constraints such as deadlines or timers to encourage timely action.</p>
<p>These principles collectively highlight the importance of simplicity, clarity, performance, and user psychology in design. By applying them thoughtfully, developers can create intuitive, efficient, and engaging user experiences that align with both human behaviour and user expectations.</p>
<h2 id="heading-references">References</h2>
<p>Budiu, R. (2022). <em>Fitts’s Law and Its Applications in UX</em>. [online] Nielsen Norman Group. Available at: <a href="https://www.nngroup.com/articles/fitts-law/">https://www.nngroup.com/articles/fitts-law/</a>.</p>
<p>Bustamante, N. (2023). <em>Gestalt Psychology? Definition, Principles, &amp; Examples - Simply Psychology</em>. [online] <a href="http://www.simplypsychology.org">www.simplypsychology.org</a>. Available at: <a href="https://www.simplypsychology.org/what-is-gestalt-psychology.html">https://www.simplypsychology.org/what-is-gestalt-psychology.html</a>.</p>
<p>Cherry, K. (2024). <em>The Zeigarnik Effect Is Why You Keep Thinking of Unfinished Work</em>. [online] Verywell Mind. Available at: <a href="https://www.verywellmind.com/zeigarnik-effect-memory-overview-4175150">https://www.verywellmind.com/zeigarnik-effect-memory-overview-4175150</a>.</p>
<p>DO, A.M., RUPERT, A.V. and WOLFORD, G. (2008). Evaluations of pleasurable experiences: The peak-end rule. <em>Psychonomic Bulletin &amp; Review</em>, 15(1), pp.96–98. doi:<a href="https://doi.org/10.3758/pbr.15.1.96">https://doi.org/10.3758/pbr.15.1.96</a>.</p>
<p>GUPTA, S., GUPTA, S., MAHENDRA, A. and GUPTA, S. (2006). Inverse Halo Nevus. <em>Dermatologic Surgery</em>, 32(6), pp.871–872. doi:<a href="https://doi.org/10.1097/00042728-200606000-00025">https://doi.org/10.1097/00042728-200606000-00025</a>.</p>
<p>‌Hall, D. (2023). <em>Pilot Error, Chapanis and The Shape of Things to Come</em>. [online] UX Magazine. Available at: <a href="https://uxmag.com/articles/pilot-error-chapanis-and-the-shape-of-things-to-come">https://uxmag.com/articles/pilot-error-chapanis-and-the-shape-of-things-to-come</a>.</p>
<p>Hunt, R.R. (1995). The subtlety of distinctiveness: What von Restorff really did. <em>Psychonomic Bulletin &amp; Review</em>, 2(1), pp.105–112. doi:<a href="https://doi.org/10.3758/bf03214414">https://doi.org/10.3758/bf03214414</a>.</p>
<p>Kahneman, D., Fredrickson, B.L., Schreiber, C.A. and Redelmeier, D.A. (1993). When More Pain Is Preferred to Less: Adding a Better End. <em>Psychological Science</em>, 4(6), pp.401–405. doi:<a href="https://doi.org/10.1111/j.1467-9280.1993.tb00589.x">https://doi.org/10.1111/j.1467-9280.1993.tb00589.x</a>.</p>
<p>Mod, D. (2024). <em>Doherty Threshold: UX Law of Swift Interactions</em>. [online] Articles on everything UX: Research, Testing &amp; Design. Available at: <a href="https://blog.uxtweak.com/doherty-threshold/">https://blog.uxtweak.com/doherty-threshold/</a>.</p>
<p>Miller, G.A. (1956). The magical number seven, plus or minus two: Some limits on our capacity for processing information. <em>Psychological Review</em>, [online] 101(2), pp.343–352. doi:<a href="https://doi.org/10.1037/0033-295x.101.2.343">https://doi.org/10.1037/0033-295x.101.2.343</a>.</p>
<p>Proctor, R.W. and Schneider, D.W. (2018). Hick’s law for choice reaction time: A review. <em>Quarterly Journal of Experimental Psychology</em>, [online] 71(6), pp.1281–1299. doi:<a href="https://doi.org/10.1080/17470218.2017.1322622">https://doi.org/10.1080/17470218.2017.1322622</a>.</p>
<p>Yablonski, J. (2022). <em>Hick’s Law</em>. [online] Laws of UX. Available at: <a href="https://lawsofux.com/hicks-law/">https://lawsofux.com/hicks-law/</a>.</p>
<p>Yablonski, J. (2022). <em>Goal-Gradient Effect</em>. [online] Laws of UX. Available at: <a href="https://lawsofux.com/goal-gradient-effect/">https://lawsofux.com/goal-gradient-effect/</a>.</p>
<p>‌</p>
<p>‌</p>
<p>‌</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Automate Form UX Audits: Errors, Hints, and Keyboard Flows ]]>
                </title>
                <description>
                    <![CDATA[ Forms are often the gatekeepers to conversions on a site or application. Abandoned carts, partially signed-up users, and users who stop engaging with your app are often thanks to friction with forms.  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/automate-form-ux-audits-handbook/</link>
                <guid isPermaLink="false">69bc38cfb238fd45a3231433</guid>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UIUX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                    <category>
                        <![CDATA[ forms ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oleh Romanyuk ]]>
                </dc:creator>
                <pubDate>Thu, 19 Mar 2026 17:56:31 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/5bf2b730-1a9f-46e9-9809-b63926589f00.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Forms are often the gatekeepers to conversions on a site or application. Abandoned carts, partially signed-up users, and users who stop engaging with your app are often thanks to friction with forms.</p>
<p>People will also abandon a site when they don’t understand an error message, can’t navigate through fields easily, or can’t get the help they need right away.</p>
<p>This guide will show you how to create automated systems that detect these issues before your users do. Together, we'll write code that systematically checks your forms so you don’t have to go through each field by hand in the hopes of finding problems.</p>
<p>Here's what we'll build together:</p>
<ul>
<li><p>Scripts that confirm your error messages are helpful and specific rather than generic and annoying.</p>
</li>
<li><p>Automated checks that guarantee the hint text appears in the appropriate location and at the appropriate time.</p>
</li>
<li><p>Tests to ensure keyboard users can navigate your forms without becoming lost or stuck.</p>
</li>
<li><p>A comprehensive audit system that performs these checks automatically and provides you with a list of fixes ranked in order of priority.</p>
</li>
<li><p>Integration with your development process so that each time you make a code change, these checks are performed.</p>
</li>
</ul>
<p>By the end of this handbook, you'll know how to:</p>
<ul>
<li><p>Configure automated audits for any application form.</p>
</li>
<li><p>Verify error messages against accessibility guidelines and usability best practices.</p>
</li>
<li><p>Make sure the hint text aids users rather than confounds them.</p>
</li>
<li><p>Verify that all users can finish your forms by testing the keyboard navigation.</p>
</li>
<li><p>Link audit findings to actual business metrics, such as conversion rates.</p>
</li>
<li><p>As part of your deployment procedure, run these audits automatically.</p>
</li>
</ul>
<p>You can run the functional JavaScript code in each section right away. Because the examples use vanilla JavaScript, they can be used with any framework, including React, Vue, Angular, or simply HTML. Although you don't have to be an expert, you should be able to follow along with JavaScript to get this most out of this guide.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ol>
<li><p><a href="#heading-why-should-you-automate-form-audits-in-the-first-place">Why Should You Automate Form Audits in the First Place?</a></p>
</li>
<li><p><a href="#heading-how-to-define-concise-objectives-and-scope">How to Define Concise Objectives and Scope</a></p>
</li>
<li><p><a href="#heading-how-to-scope-audit-boundaries">How to Scope Audit Boundaries</a></p>
</li>
<li><p><a href="#heading-how-to-incorporate-heuristic-evaluation-and-quantitative-data">How to Incorporate Heuristic Evaluation and Quantitative Data</a></p>
</li>
<li><p><a href="#heading-how-to-define-automated-test-cases-for-form-components">How to Define Automated Test Cases for Form Components</a></p>
</li>
<li><p><a href="#heading-how-to-consistently-confirm-keyboard-navigation-flows">How to Consistently Confirm Keyboard Navigation Flows</a></p>
</li>
<li><p><a href="#heading-aiinformed-navigation-pattern-detection">AI-Informed Navigation Pattern Detection</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-why-should-you-automate-form-audits-in-the-first-place">Why Should You Automate Form Audits in the First Place?</h2>
<p>Manual testing will typically find easy problems. But it may miss the systematic issues with forms across your entire application. You know the type of errors I’m talking about – those that don’t state incorrect values, missing keyboard support for fields, or hints that don’t have a clear place for help based on static design.</p>
<p>Research on user experience design shows that an effective UX can be strategically built, refined, or designed by using continuous feedback loops (Source: <a href="https://www.researchgate.net/publication/382804453_Assessment_of_User_Experience_UX_Design_Trends_in_Mobile_Applications">Okonkwo, Research Gate</a>). Automated audits are a good way of putting this into practice, as they can help you find issues at scale before you release your product.</p>
<p>The key advantage here is coverage. You can prepare form UX audit basics so you and your teammates can check every form field, validation rule, and keyboard interaction in a systematic way.</p>
<h3 id="heading-key-form-components-to-audit"><strong>Key Form Components to Audit</strong></h3>
<p>Before we create our roadmap for the UX audit, let’s talk about the three main elements of forms that typically have issues:</p>
<ul>
<li><p><strong>Error states</strong> indicate what went wrong, as well as how to fix it. The clearer they are, the better: the confusion of generic messages like "Invalid input" frustrates users. In contrast, specific feedback like "Email must contain @ symbol" is specific enough to help them get things completed.</p>
</li>
<li><p><strong>Hint text</strong> helps prevent errors before users can input anything. Proactive guidance like "Password must contain 8+ characters" provides realistic expectations. Placing hint text in the right location and conveying its message at the appropriate time also determines how useful the hint text will be.</p>
</li>
<li><p><strong>Keyboard flows</strong> distinguish accessible forms from broken forms. Tab order, focus order, and keyboard shortcuts dictate whether a user can complete their form without a mouse.</p>
</li>
</ul>
<p>To guide you through the audit workflow for each component, we’ll focus on three user flows: a sign-up form, checkout, and a contact form. All of these affect the conversion rate of a product’s users. And each of these elements maps to multiple metrics you can track.</p>
<p>We’ll use Nielsen Norman Group's 10 usability heuristics (Source: <a href="https://www.nngroup.com/articles/ten-usability-heuristics/">Jakob Nielsen, NNgroup</a>) as a foundation while incorporating any data indicators. These heuristics are:</p>
<ul>
<li><p><strong>System status visibility</strong>: Users should always be aware of what's going on thanks to prompt feedback</p>
</li>
<li><p><strong>System and real-world match</strong>: Use the user's language instead of technical terms</p>
</li>
<li><p><strong>User freedom and control</strong>: Enable users to quickly correct errors</p>
</li>
<li><p><strong>Consistency and standards</strong>: Adhere to platform norms so users don't have to question whether various terms or behaviors have the same meaning.</p>
</li>
<li><p><strong>Error prevention</strong>: Design to stop issues before they start</p>
</li>
<li><p><strong>Recognition rather than recall</strong>: Show options rather than making users memorize details.</p>
</li>
<li><p><strong>Flexibility and efficiency of use</strong>: Provide shortcuts for experienced users while keeping things simple for beginners.</p>
</li>
<li><p><strong>Aesthetic and minimalist design</strong>: Don't clutter interfaces with irrelevant information.</p>
</li>
<li><p><strong>Help users recognize, diagnose, and recover from errors</strong>: Error messages should clearly explain the problem and suggest a solution.</p>
</li>
<li><p><strong>Help and documentation</strong>: Provide easy-to-search help when needed.</p>
</li>
</ul>
<p>For form audits, we'll focus especially on heuristics #5 (error prevention through hint text), #6 (making requirements visible), and #9 (clear error recovery). These directly affect whether users can complete forms successfully.</p>
<p>There’s one more important thing to note before we proceed. For each component, you’ll want to identify what tests you can automate as part of your test case. For each form component, start building checks programmatically for several criteria:</p>
<ul>
<li><p><strong>Accessibility</strong> – for contrast ratios, ARIA labels, and focus indicators.</p>
</li>
<li><p><strong>Performance</strong> – for the time it takes to validate fields and render error messages.</p>
</li>
<li><p><strong>Visual consistency</strong> – for error state styling, hint text patterns, and keyboard focus.</p>
</li>
</ul>
<p>We’ll prioritize fixes with a high impact that will help prevent users from abandoning a form. Also, each audit will produce actionable information along with a line of code, acceptance criteria, and example code for fixes.</p>
<p>Now, let’s start building our form UX audit workflow.</p>
<h2 id="heading-how-to-define-concise-objectives-and-scope">How to Define Concise Objectives and Scope</h2>
<p>Before you even put your fingers to the keyboard, you'll want to determine what exactly you’re measuring and for what purpose. There’s a golden rule: ambiguous statements will yield ambiguous results. Having specific objectives linked to user behavior and business metrics is what creates actionable audits.</p>
<h3 id="heading-identifying-form-types-and-objectives">Identifying Form Types and Objectives</h3>
<p>First, you need to know what form you’re going to audit. Let’s start by mapping the three forms that have the greatest direct impact on the success of your application and defining the nuances and objectives of each one.</p>
<h4 id="heading-1-a-signup-forms-key-goal-is-user-acquisition">1. A signup form’s key goal is user acquisition.</h4>
<p>Every friction point - like poorly defined password requirements, vague expectations for email formats, and a lack of field labels - means a user will likely abandon the signup process. Your audit must identify these user flows.</p>
<h4 id="heading-2-checkout-forms-are-aimed-at-bringing-revenue">2. Checkout forms are aimed at bringing revenue</h4>
<p>User engagement improves significantly when applications use user-centered design principles – shocking, right? (Source: <a href="https://www.researchgate.net/publication/382804453_Assessment_of_User_Experience_UX_Design_Trends_in_Mobile_Applications">Okonkwo, ResearchGate</a>). In checkout flows, this means that error messages appear inline, hints show requirements before a user can type, and keyboard navigation follows the expected tab order from shipping to payment fields.</p>
<h4 id="heading-3-contact-forms-have-the-objective-of-establishing-communication">3. Contact forms have the objective of establishing communication</h4>
<p>Often, devs pay less attention to contact forms than to transactional forms. But contact forms are crucial for proposal requests, customer support requests, collaboration requests, and so on. A broken contact form can mean lost business.</p>
<p>Now that you’ve identified the type of form and its key objective, let’s do something equally important: link these high-level objectives to specific, measurable metrics.</p>
<h3 id="heading-connecting-objectives-and-metrics-to-track">Connecting Objectives and Metrics to Track</h3>
<p>Every objective for the audit maps to a set of measurements. Here’s how to build this continuum:</p>
<h4 id="heading-error-message-metrics">Error message metrics</h4>
<p>Your audit should evaluate whether error messages fulfill three criteria:</p>
<ol>
<li><p>Specificity (Does the message indicate what the problem is?),</p>
</li>
<li><p>Timing (In what context will the user see the error?), and</p>
</li>
<li><p>Actionable (Does the message explain what the user should do to recover?).</p>
</li>
</ol>
<p>For example, "password too weak" does not provide actionable information and isn’t specific enough, while "Password must have at least one uppercase character, one number, and one special character" is highly actionable and provides clear information on what the user needs to do.</p>
<p><strong>You can measure this by tracking two things:</strong> what percentage of your error messages mention the specific field name, and how many errors show up immediately when users leave a field versus only appearing after they hit submit.</p>
<h4 id="heading-hints-can-reduce-user-errors-before-they-occur-your-audit-should-validate">Hints can reduce user errors before they occur. Your audit should validate:</h4>
<ul>
<li><p>Whether a hint’s positioning is proactive (for instance, is the hint text located before users engage the provided input field?).</p>
</li>
<li><p>Visual proximity (Is the hint text next to the input field?).</p>
</li>
<li><p>Accessibility (for example, are the hints connected to the fields programmatically using aria-describedby?)</p>
</li>
</ul>
<p>The latter is especially important, as screen readers require explicit connections to announce hints at the correct time. You’ll want to measure these using metrics like the time to display a hint (immediate vs delayed) and the number of ARIA-associated hint elements.</p>
<h4 id="heading-keyboard-flow-metrics-are-also-directly-connected-to-accessibility">Keyboard flow metrics are also directly connected to accessibility</h4>
<p>Your audit should define if there is a correct order logic. Do fields follow the visual layout? When the tabindex jumps around sporadically across the screen, users lose context.</p>
<p>It should also determine if the focus visibility is the current field and if that’s clearly indicated. When the focus is invisible or subtle, you end up making your users guess where they are.</p>
<p>Keyboard shortcuts let users submit forms, clear fields, or jump between sections without touching their mouse. Everyone benefits from this, not just people using screen readers or other assistive technology.</p>
<p>You will want to measure these with the tabindex sequence alignment to the visual layout, focus indicator contrast ratios, and by defining if there are keyboard shortcuts for submitting, clearing, and moving about the forms.</p>
<p>Now that you have outlined the key metrics and principles applicable to the form you’re auditing, it’s time to define the specific area you want to focus on.</p>
<h2 id="heading-how-to-scope-audit-boundaries">How to Scope Audit Boundaries</h2>
<p>You’ll need to define exactly what forms and fields your audit will cover. You’ll want to start small in order to build working automations and then slowly expand.</p>
<h3 id="heading-setting-the-initial-scope">Setting the Initial Scope</h3>
<p>You should start with forms in your application’s critical user paths. We’ll use the below JavaScript snippet as an example. Paste this configuration object into your audit automation tool or script:</p>
<pre><code class="language-python">const auditScope = {
  signupForm: {
    url: '/signup',
    fields: ['email', 'password', 'confirmPassword'],
    priority: 'critical'
  },
  checkoutForm: {
    url: '/checkout',
    fields: ['cardNumber', 'expiryDate', 'cvv', 'billingZip'],
    priority: 'critical'
  },
  contactForm: {
    url: '/contact',
    fields: ['name', 'email', 'subject', 'message'],
    priority: 'high'
  }
};
</code></pre>
<p>In the code above, the auditScope object lists critical forms (signup, checkout, and contact forms), specifies their URLs along with important fields to check (such as email, password, cardNumber), and lists their priority level. This helps the auditing system see what resources to prioritize.</p>
<p>The priority property allows your audit system to prioritize high-impact forms first when protocol limits the resources available. Such a method prevents your system from being overwhelmed with too broad a scope.</p>
<h3 id="heading-setting-the-audit-execution-plan">Setting the Audit Execution Plan</h3>
<p>Your objectives dictate how the audit system will act. After you have defined the scope, you can divide the workflow into several definite phases.</p>
<ul>
<li><p><strong>Phase 1:</strong> Static analysis will address the structure of the HTML document and verify the ARIA attributes, tab indices, and markup patterns for semantic use. This can discover structural issues that require no user interaction.</p>
</li>
<li><p><strong>Phase 2:</strong> Dynamic testing simulates user interactions to identify the occurrence of errors, when hints are displayed, and when a keyboard user would navigate to which elements. This can identify runtime problems that were not revealed through static analysis.</p>
</li>
<li><p><strong>Phase 3:</strong> Visual verification measures spacing, contrast ratios, or positioning of elements, but using visually computed styles. This allows us to examine the visual design in relation to meeting either UX or UI objectives.</p>
</li>
</ul>
<p>Each phase will result in findings that continually tie back to your defined objectives. In the case of a failed check, the audit report will show which objective contributed to the finding, the current state of the component, the target states, and recommendations for the solution.</p>
<h3 id="heading-field-level-objectives">Field-level objectives</h3>
<p>For each field, you should tell the script or the automated tool what the audit is actually checking. Each check is a specific automated test. When the audit is executed, every check is confirmed for every field, and failures are noted.</p>
<p>For instance, in the following snippet, for each field we provide (like "email" or "password"), we list three categories of checks: errorChecks, hintChecks, and keyboardChecks. When you link this object to your audit automation system (for example, UI testing frameworks, accessibility checkers), your audit automation system runs all these tests on every field. It will report any test failures so you can see which part of the input is broken or missing.</p>
<pre><code class="language-python">const fieldObjectives = {
  email: {
    errorChecks: [
      'hasInlineValidation',
      'errorMessageSpecific',
      'showsValidFormatExample'
    ],
    hintChecks: [
      'hintVisibleBeforeInteraction',
      'hintExplainsFormat',
      'hintAccessible'
    ],
    keyboardChecks: [
      'tabOrderCorrect',
      'focusVisible',
      'supportsAutocomplete'
    ]
  },
  password: {
    errorChecks: [
      'hasInlineValidation',
      'listsAllRequirements',
      'showsProgressIndicator'
    ],
    hintChecks: [
      'hintListsRequirements',
      'hintVisibleBeforeTyping',
      'hintPersistsDuringTyping'
    ],
    keyboardChecks: [
      'tabOrderCorrect',
      'focusVisible',
      'supportsPasswordManagers'
    ]
  }
};
</code></pre>
<p>This leads to a very granular, consistent, and repeatable test of important user input components so you can validate usability, accessibility, and correctness of the form.</p>
<h3 id="heading-linking-objectives-to-business-kpis">Linking Objectives to Business KPIs</h3>
<p>The findings of the audit should link back to business impact. Let’s look at how our objectives map to key performance indicators.</p>
<p>The impact on conversion rate should be direct and specific. For instance, improved error messages directly impact the form completion rate:</p>
<ul>
<li><p><strong>Baseline</strong>. The current abandonment rate for forms.</p>
</li>
<li><p><strong>Target</strong>. Decrease abandonment by 15% due to clear error messages.</p>
</li>
<li><p><strong>Audit validation.</strong> 100% of error messages are clear and specific.</p>
</li>
</ul>
<p>Reducing support tickets is another priority. In this case, better hint text provides less confusion:</p>
<ul>
<li><p><strong>Baseline</strong>. Volume of support requests related to forms currently.</p>
</li>
<li><p><strong>Target</strong>. Decrease the number of support tickets by 25%.</p>
</li>
<li><p><strong>Audit validation.</strong> 100% of complex fields have proactive hints.</p>
</li>
</ul>
<p>Accessibility compliance is another important goal. Keyboard navigation has a direct impact on the legal compliance and reach of a wider base of users. The example of the KPIs here can be the following:</p>
<ul>
<li><p><strong>Baseline</strong>. Current level of WCAG conformance.</p>
</li>
<li><p><strong>Target</strong>. All forms to be WCAG 2.1 AA compliant.</p>
</li>
<li><p><strong>Audit validation.</strong> 100% of fields work with a keyboard only.</p>
</li>
</ul>
<p>After you create such measurable criteria for success, you should actually give them as thresholds to your system. Here’s an example of how you can provide defined measures for passing and failing for the automated audit:</p>
<pre><code class="language-python">const auditThresholds = {
  errorMessages: {
    specificityScore: 0.9,  // 90% must include field-specific details
    inlineValidationPercentage: 1.0,  // 100% must show inline errors
    actionabilityScore: 0.85  // 85% must include fix instructions
  },
  hintText: {
    proactiveDisplayPercentage: 1.0,  // 100% must appear before interaction
    proximityMaxPixels: 8,  // Hints must be within 8px of fields
    ariaAssociationPercentage: 1.0  // 100% must have aria-describedby
  },
  keyboardFlow: {
    tabOrderCorrectnessScore: 1.0,  // 100% must follow visual layout
    focusContrastRatio: 3.0,  // Minimum 3:1 contrast for focus indicators
    shortcutCoveragePercentage: 0.8  // 80% of actions must have shortcuts
  }
};
</code></pre>
<p>As you can see, under errorMessages, the specificityScore of 0.9 states that 90% of error messages must include details specific to each field. Also, within the hintText directions, the proactiveDisplayPercentage of 1.0 demands that the hints appear before the user starts typing, not after, for the test to be passed.</p>
<h3 id="heading-the-5cs-framework-for-audit-documentation">The 5Cs Framework for Audit Documentation</h3>
<p>When you document your audit results, use the 5Cs framework to make sure your reports are actually useful:</p>
<ul>
<li><p><strong>Coverage</strong>: Which forms and fields did you test? Be specific about the scope.</p>
</li>
<li><p><strong>Completion</strong>: Did you run all the planned checks? Note any that were skipped and why.</p>
</li>
<li><p><strong>Consistency</strong>: Do your results use the same format and terminology throughout? This makes reports easier to scan.</p>
</li>
<li><p><strong>Clarity</strong>: Can a developer read the failure report and know exactly what to fix? Include the current state, expected state, and code examples.</p>
</li>
<li><p><strong>Correctness</strong>: Are your checks testing the right things? Validate that failures match real user problems, not just technical violations.</p>
</li>
</ul>
<p>This framework helps both designers and developers understand audit results and take action quickly.</p>
<h2 id="heading-how-to-incorporate-heuristic-evaluation-and-quantitative-data">How to Incorporate Heuristic Evaluation and Quantitative Data</h2>
<p>Once you've established objectives, you’ll want to base your audit on the usability principles and behavior patterns of users. This combination of established usability heuristics along with quantitative data can help you verify that your automated checks are surfacing issues that users actually experience – and not fictional edge cases (Source: <a href="https://www.nngroup.com/articles/ten-usability-heuristics/">Jakob Nielsen, NNgroup</a>).</p>
<p>The best way to do this is to map the issues to the common standard of the Web Content Accessibility Guidelines and use that as the basis for your audit (Source: <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1, W3C Recommendation</a>). For every identified problem, identify the WCAG success criteria that the issue violates. This will give you clear requirements that you can use as an automated check and that should be commonly understood by everyone working on the audits.</p>
<p>In the automated script, you can use the following code to map these guidelines:</p>
<pre><code class="language-python">const wcagMapping = {
  errorIdentification: {
    criterion: '3.3.1',
    level: 'A',
    requirement: 'Error messages must identify the item in error',
    testFunction: 'checkErrorIdentification'
  },
  errorSuggestion: {
    criterion: '3.3.3',
    level: 'AA',
    requirement: 'Error messages must provide suggestions for correction',
    testFunction: 'checkErrorSuggestions'
  },
  labelInName: {
    criterion: '2.5.3',
    level: 'A',
    requirement: 'Accessible name must contain visible label text',
    testFunction: 'checkLabelInName'
  },
  focusOrder: {
    criterion: '2.4.3',
    level: 'A',
    requirement: 'Focus order must preserve meaning and operability',
    testFunction: 'checkFocusOrder'
  }
};
</code></pre>
<p>If any part of the form violates WCAG 3.3.1 – perhaps it doesn’t identify the field when it provides an error – your audit report can document it as follows:</p>
<p>"The error message displayed on the email field said 'Invalid input' but didn’t identify the requirement it didn't meet. WCAG 3.3.1 states that the error must identify the field with the error (potential fix: 'Email must have an @ symbol'). 3.3.1 is the mapped criteria."</p>
<p>Nielsen's heuristics tell you what good usability looks like. But tools like Google Analytics and Hotjar show you what users actually do on your forms. When you combine both – usability principles and real user behavior data – you make better decisions about which problems to fix first.</p>
<p>Research shows this combined approach catches more real issues than either method alone (Sources: <a href="https://www.researchgate.net/publication/334385231_User_Experience_Evaluation_Using_Mouse_Tracking_and_Artificial_Intelligence">Souza and others, IEEE Access</a>). Use both types of data to prioritize what your automated audits should check.</p>
<h3 id="heading-define-binary-passfail-criteria">Define Binary Pass/Fail Criteria</h3>
<p>Automation requires clear results. Your audit must document whether a component passed or failed its WCAG criteria. The process must yield binary results – yes/no, positive/negative.</p>
<p>For instance, the following code incorporates a definite, binary audit check to verify whether a form field is compliant with WCAG 3.3.1 criteria for error identification (Source: <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1, W3C Recommendation</a>):</p>
<pre><code class="language-python">function checkErrorIdentification(field) {
  const errorElement = field.querySelector('[role="alert"], .error-message');
  
  if (!errorElement) {
    return {
      pass: false,
      wcag: '3.3.1',
      severity: 'critical',
      message: `Field "${field.name}" lacks error message element`,
      fix: 'Add aria-describedby pointing to error container'
    };
  }
  
  const errorText = errorElement.textContent.trim();
  const hasFieldReference = errorText.toLowerCase().includes(field.name.toLowerCase()) 
    || errorText.toLowerCase().includes(field.labels[0]?.textContent.toLowerCase());
  
  if (!hasFieldReference) {
    return {
      pass: false,
      wcag: '3.3.1',
      severity: 'major',
      message: `Error message "${errorText}" doesn't identify field`,
      fix: `Include field name in error: "\({field.labels[0]?.textContent} \){errorText}"`
    };
  }
  
  return { pass: true, wcag: '3.3.1' };
}
</code></pre>
<p>The error message identified the error, and each component definitively passes or fails the criterion. There’s no room for interpretation or doubt.</p>
<h3 id="heading-standardize-error-messages-and-hint-messages">Standardize Error Messages and Hint Messages</h3>
<p>Keep in mind that automated functions require familiarity and consistency. You can create centralized repositories with libraries of accepted usage patterns and approval that developers can refer to, and your tests can check that your forms are using standardized messages.</p>
<p>For example, in the following snippet, the errorLibrary object provides a single source of truth for every acceptable error message your application will display for specific fields. For each field, there are appropriately defined message strings for common validation issues (like empty, invalid, duplicate). Similarly, the hintLibrary object provides a single source of truth for every hint text that prefaces user input.</p>
<pre><code class="language-python">const errorLibrary = {
  email: {
    empty: 'Email address is required',
    invalid: 'Email must include @ symbol and domain (example: user@domain.com)',
    duplicate: 'This email is already registered. Try signing in instead.'
  },
  password: {
    empty: 'Password is required',
    tooShort: 'Password must be at least 8 characters',
    missingUppercase: 'Password must include at least one uppercase letter',
    missingNumber: 'Password must include at least one number',
    missingSpecial: 'Password must include at least one special character (!@#$%^&amp;*)'
  }
};

const hintLibrary = {
  email: 'Enter your email address (example: name@company.com)',
  password: 'Create a password with 8+ characters, including uppercase, number, and special character',
  phone: 'Enter 10-digit phone number without dashes (example: 5551234567)'
};
</code></pre>
<p>By having a single source of these patterns, you won’t run the risk of writing duplicate or misaligned messages in different areas across the app, increasing maintainability.</p>
<h3 id="heading-utilize-definite-aria-identifiers">Utilize Definite ARIA Identifiers</h3>
<p>If you use consistent ARIA attributes, you can automate checks. As an example, in the following snippet, we explicitly define which ARIA attributes connect error messages, hints, and fields. The auditARIAImplementation function checks whether each field has an accessible label (either an HTML element or an aria-label attribute) to meet WCAG 1.3.1. Without it, users will likely find it hard to identify the purpose of the input.</p>
<pre><code class="language-python">function auditARIAImplementation(field) {
  const checks = {
    hasLabel: !!field.labels?.length || !!field.getAttribute('aria-label'),
    hasDescription: !!field.getAttribute('aria-describedby'),
    hasErrorMessage: !!field.getAttribute('aria-errormessage'),
    hasRequired: field.hasAttribute('required') || field.getAttribute('aria-required') === 'true'
  };
  
  const failures = [];
  
  if (!checks.hasLabel) {
    failures.push({
      wcag: '1.3.1',
      message: `Field "${field.name}" lacks accessible label`,
      fix: 'Add &lt;label&gt; element or aria-label attribute'
    });
  }
  
  if (!checks.hasDescription) {
    failures.push({
      wcag: '3.3.2',
      message: `Field "${field.name}" lacks hint text association`,
      fix: 'Add aria-describedby pointing to hint element ID'
    });
  }
  
  if (field.dataset.validatable &amp;&amp; !checks.hasErrorMessage) {
    failures.push({
      wcag: '3.3.1',
      message: `Validatable field "${field.name}" lacks aria-errormessage`,
      fix: 'Add aria-errormessage attribute pointing to error container ID'
    });
  }
  
  return {
    pass: failures.length === 0,
    failures
  };
}
</code></pre>
<p>This function checks for specific ARIA-tied patterns that your design system prescribes. The great thing is that you can check the code in CI/CD pipelines to maintain implementation standards.</p>
<p>The next section will help you identify specific automated test cases for errors, hints, and keyboard flows.</p>
<h2 id="heading-how-to-define-automated-test-cases-for-form-components">How to Define Automated Test Cases for Form Components</h2>
<p>Now that you've established a baseline with WCAG standards, heuristics, and user data, you can create specific automated test cases. Each test case targets a measurable quality for the error messages, hint text, or keyboard navigation functionality of each form component. The following sections will provide concrete ways to automate these checks for each form component.</p>
<h3 id="heading-automated-error-message-checks">Automated Error Message Checks</h3>
<p>Error messages are crucial conversion points. Your automated checks should ensure that each error message meets the specificity, timing, and actionability criteria. Every error message must convey what exactly has gone wrong. Generic error messages don’t pass this quality check.</p>
<p>For automation, your checks can provide insight as to whether the error message references the specific construction of the field. For instance, in the following snippet, the method verifies that every error message that appears in a form serves to pinpoint the input problem, not use a vague general expression.</p>
<pre><code class="language-python">function auditErrorSpecificity(formId) {
  const form = document.getElementById(formId);
  const fields = form.querySelectorAll('input, textarea, select');
  const failures = [];
  
  fields.forEach(field =&gt; {
    const errorElement = document.querySelector(`[aria-describedby="${field.id}-error"]`);
    if (errorElement) {
      const errorText = errorElement.textContent.toLowerCase();
      const genericTerms = ['invalid', 'error', 'wrong', 'incorrect'];
      const isGeneric = genericTerms.some(term =&gt; 
        errorText.includes(term) &amp;&amp; errorText.split(' ').length &lt; 4
      );
      
      if (isGeneric) {
        failures.push({
          field: field.id,
          message: errorText,
          issue: 'Generic error message lacks specificity',
          wcag: '3.3.1 Error Identification'
        });
      }
    }
  });
  
  return failures;
}
</code></pre>
<p>It looks through field-specific error messages and flags any that use common generic terms in a short, vague message, as ambiguous messages don’t serve the purposes of WCAG 3.3.1 since they don’t provide an actionable correction for user input (Source: <a href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-identified.html">WCAG 3.3.1, WC3</a>).</p>
<h3 id="heading-validation-timing-of-inline-messages">Validation Timing of Inline Messages</h3>
<p>Error messages should show the message when the user leaves the field and not just at submission. Research demonstrates that even just the timing of the feedback alone significantly disrupts user performance. Your automation should check whether errors are displayed inline (Source: <a href="https://www.researchgate.net/publication/282715275_Soft_Keyboard_UX_Evaluation_An_Eye_Tracking_Study">Al-Khalifa and others, ResearchGate</a>).</p>
<p>This code verifies that error messages appear immediately when a user leaves a required input field on blur, as opposed to only showing errors after submitting the entire form.</p>
<pre><code class="language-python">function auditErrorTiming(formId) {
  const form = document.getElementById(formId);
  const fields = form.querySelectorAll('input[required], textarea[required]');
  const failures = [];
  
  fields.forEach(field =&gt; {
    let hasBlurValidation = false;
    let hasInputValidation = false;
    
    // Check for blur event listeners
    const blurListeners = getEventListeners(field).blur || [];
    hasBlurValidation = blurListeners.length &gt; 0;
    
    // Check for input event listeners
    const inputListeners = getEventListeners(field).input || [];
    hasInputValidation = inputListeners.length &gt; 0;
    
    if (!hasBlurValidation &amp;&amp; !hasInputValidation) {
      failures.push({
        field: field.id,
        issue: 'No inline validation detected',
        recommendation: 'Add blur or input event validation',
        wcag: '3.3.1 Error Identification'
      });
    }
  });
  
  return failures;
}
</code></pre>
<p>This audit verifies that an event handler which runs against an inline validator is in place for blur and input events on input fields. It also checks that it flags any that fail to have these interactions as a failure under the WCAG 3.3.1 Error Identification guideline (Source: <a href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/minimize-error-identified.html">WCAG 3.3.1, WC3</a>).</p>
<h3 id="heading-confirm-actionable-recovery-guidance">Confirm Actionable Recovery Guidance</h3>
<p>Error messages must clarify how to recover from any faults or issues. "Password too weak" fails the user. In contrast, the error message "Password must be 8+ characters, contain at least 1 upper case letter, and one numeric character" gives the user enough information to make the proper adjustment.</p>
<p>Let’s take an example of some code that audits password fields to ensure that the error message includes specific actionable recovery instructions outlining character types or character length and clearly guides users on how to correct input errors (Source: <a href="https://www.w3.org/WAI/WCAG21/Understanding/error-suggestion.html">WCAG 3.3.3, WC3</a>).</p>
<pre><code class="language-python">function auditErrorActionability(formId) {
  const form = document.getElementById(formId);
  const failures = [];
  const passwordFields = form.querySelectorAll('input[type="password"]');
  
  passwordFields.forEach(field =&gt; {
    const errorElement = document.getElementById(`${field.id}-error`);
    if (errorElement) {
      const errorText = errorElement.textContent;
      const hasSpecificRequirements = /\d+/.test(errorText) || 
        /uppercase|lowercase|special|character|digit/.test(errorText.toLowerCase());
      
      if (!hasSpecificRequirements) {
        failures.push({
          field: field.id,
          message: errorText,
          issue: 'Error lacks specific recovery guidance',
          wcag: '3.3.3 Error Suggestion'
        });
      }
    }
  });
  
  return failures;
}
</code></pre>
<p>If the error message doesn’t contain these characters or meet the length requirement, the function will flag the instance as a failure with a corresponding WCAG reference.</p>
<h3 id="heading-automated-rich-hint-text-auditing">Automated Rich Hint Text Auditing</h3>
<p>Rich text hints help prevent errors (and hopefully the need for an error message). Research suggests that users who are given proactive guidance improve form completion by up to 23% (Source: <a href="https://www.researchgate.net/publication/382804453_Assessment_of_User_Experience_UX_Design_Trends_in_Mobile_Applications">Okonkwo, ResearchGate</a>). Your automation should confirm that the right hints appear at the right time, in the right places, with appropriate accessible information and presentation.</p>
<p>This function audits form fields to ensure that hint text is seen before the user interacts with the form, helping avoid user errors while filling out the form.</p>
<pre><code class="language-python">function auditHintTiming(formId) {
  const form = document.getElementById(formId);
  const complexFields = form.querySelectorAll('input[type="password"], input[pattern]');
  const failures = [];
  
  complexFields.forEach(field =&gt; {
    const hintElement = document.querySelector(`[id="${field.getAttribute('aria-describedby')}"]`);
    
    if (!hintElement) {
      failures.push({
        field: field.id,
        issue: 'Complex field missing proactive hint text',
        wcag: '3.3.2 Labels or Instructions'
      });
    } else {
      // Check if hint is visible before interaction
      const computedStyle = window.getComputedStyle(hintElement);
      const isVisible = computedStyle.display !== 'none' &amp;&amp; 
        computedStyle.visibility !== 'hidden';
      
      if (!isVisible) {
        failures.push({
          field: field.id,
          issue: 'Hint text hidden until interaction',
          wcag: '3.3.2 Labels or Instructions'
        });
      }
    }
  });
  
  return failures;
}
</code></pre>
<p>If the hint doesn't exist or doesn’t show until after the user interacts with the form, the function will flag it as a failure to satisfy WCAG 3.3.2 Labels or Instructions for a generally consistent and user-friendly form design (Source: <a href="https://www.w3.org/WAI/WCAG21/Understanding/labels-or-instructions.html">WCAG 3.3.3, WC3</a>).</p>
<h2 id="heading-how-to-consistently-confirm-keyboard-navigation-flows">How to Consistently Confirm Keyboard Navigation Flows</h2>
<p>Keyboard accessibility is required for WCAG compliance, and you can automate most of the testing with code. When people can't use your forms with just a keyboard, they often give up and leave. This directly hurts your conversion rates.</p>
<p>When you document how keyboard navigation should work, you can write automated tests to check if it actually works that way.</p>
<h3 id="heading-outlining-the-interactions">Outlining the Interactions</h3>
<p>Your audit should document expected keyboard interactions for each component. If there's a modal dialog, note that Tab will cycle through the elements within the modal, Escape closes the modal, and the focus returns to the element that opened the modal.</p>
<p>For forms, note that Tab advances through the fields in visual order, Shift+Tab will go back, and Enter will submit the form from the submit button only.</p>
<p>You should document these flows in a data-driven way. A structured format with columns for "Component," "Keystroke," "Expected Action," and "Relevant WCAG" will make both human review and automated parsing much easier:</p>
<pre><code class="language-python">const keyboardPatterns = {
  'modal-dialog': {
    tab: 'Focus next element within modal',
    shiftTab: 'Focus previous element within modal',
    escape: 'Close modal and return focus',
    wcag: '2.1.1 Keyboard, 2.4.3 Focus Order'
  },
  'form-field': {
    tab: 'Move to next field in visual order',
    shiftTab: 'Move to previous field',
    enter: 'Submit only from submit button',
    wcag: '2.1.1 Keyboard, 2.4.3 Focus Order'
  },
  'dropdown-select': {
    space: 'Open dropdown menu',
    arrowDown: 'Navigate to next option',
    arrowUp: 'Navigate to previous option',
    enter: 'Select current option',
    escape: 'Close dropdown without selection',
    wcag: '2.1.1 Keyboard, 4.1.2 Name, Role, Value'
  }
};
</code></pre>
<p>This code aligns these patterns with WCAG guidelines 2.1.1 (Keyboard Accessibility), 2.4.3 (Focus Order), and 4.1.2 (Name, Role, Value) to support users who rely on keyboard inputs (Source: <a href="https://www.w3.org/WAI/WCAG21/Understanding/">WCAG Understanding Docs, WC3</a>).</p>
<h3 id="heading-automated-tab-order-validation">Automated Tab Order Validation</h3>
<p>Tab order should follow the visual layout of the page. When focus jumps around randomly, users get lost. The code below checks that focus moves through fields in the order people expect – top to bottom, left to right. It catches cases where focus skips around even when the tabindex values look correct.</p>
<pre><code class="language-python">function auditTabOrder(formId) {
  const form = document.getElementById(formId);
  const focusableElements = form.querySelectorAll(
    'input, select, textarea, button, a[href], [tabindex]:not([tabindex="-1"])'
  );
  const failures = [];
  
  const elements = Array.from(focusableElements).map(el =&gt; ({
    element: el,
    position: el.getBoundingClientRect(),
    tabindex: parseInt(el.getAttribute('tabindex')) || 0
  }));
  
  for (let i = 1; i &lt; elements.length; i++) {
    const prev = elements[i - 1];
    const curr = elements[i];
    
    // Check if tab order follows visual top-to-bottom, left-to-right
    const visuallyBefore = prev.position.top &lt; curr.position.top || 
      (prev.position.top === curr.position.top &amp;&amp; prev.position.left &lt; curr.position.left);
    
    if (!visuallyBefore &amp;&amp; prev.tabindex === curr.tabindex) {
      failures.push({
        field: curr.element.id,
        issue: 'Tab order does not match visual layout',
        wcag: '2.4.3 Focus Order'
      });
    }
  }
  
  return failures;
}
</code></pre>
<p>In essence, the audit identifies failures to adhere to WCAG 2.4.3 Focus Order while preserving context and the ability to interact with keyboard users as they interact with form fields (Source: <a href="https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html">WCAG 2.4.3, WC3</a>).</p>
<h3 id="heading-verify-focus-visibility">Verify Focus Visibility</h3>
<p>Focus indicators need to be clearly identifiable. According to research, visible focus indicators improved typing and reduced errors significantly (Source: <a href="https://www.researchgate.net/publication/392950031_Enhancing_User_Experience_of_Virtual_Keyboard_Through_Collaborative_and_Speed-Adaptive_Auditory-Vibrotactile_Feedback">Liu and others, ResearchGate</a>).</p>
<p>For instance, focus styles can include an outline, border, or shadow, but they must have enough contrast against the background (at least 3:1) to satisfy WCAG 2.4.7 Focus Visible and 1.4.11 Non-text Contrast (Source: <a href="https://www.w3.org/WAI/WCAG21/Understanding/">WCAG Understanding Docs, WC3</a>). Consider the following example:</p>
<pre><code class="language-python">function auditFocusVisibility(formId) {
  const form = document.getElementById(formId);
  const focusableElements = form.querySelectorAll('input, select, textarea, button');
  const failures = [];
  
  focusableElements.forEach(element =&gt; {
    element.focus();
    const computedStyle = window.getComputedStyle(element);
    
    // Check for visible focus indicator
    const hasOutline = computedStyle.outline !== 'none' &amp;&amp; 
      computedStyle.outlineWidth !== '0px';
    const hasBorder = computedStyle.borderStyle !== 'none';
    const hasBoxShadow = computedStyle.boxShadow !== 'none';
    
    // Check contrast ratio for focus indicator
    const outlineColor = computedStyle.outlineColor;
    const backgroundColor = computedStyle.backgroundColor;
    const contrastRatio = calculateContrastRatio(outlineColor, backgroundColor);
    
    if (!hasOutline &amp;&amp; !hasBorder &amp;&amp; !hasBoxShadow) {
      failures.push({
        field: element.id,
        issue: 'No visible focus indicator',
        wcag: '2.4.7 Focus Visible'
      });
    } else if (contrastRatio &lt; 3) {
      failures.push({
        field: element.id,
        contrastRatio: contrastRatio.toFixed(2),
        issue: 'Focus indicator contrast ratio below 3:1',
        wcag: '1.4.11 Non-text Contrast'
      });
    }
  });
  
  return failures;
}
</code></pre>
<p>Good focus styling helps keyboard users see where they are on the page. This code checks that focus indicators are visible and have enough contrast against the background so users can tell which field they're currently in.</p>
<h3 id="heading-using-automated-testing-tools">Using Automated Testing Tools</h3>
<p>To speed up and automate audits even more efficiently, you can use tools such as <a href="https://developer.chrome.com/docs/lighthouse/overview">Google Lighthouse</a>, <a href="https://chromewebstore.google.com/detail/axe-devtools-web-accessib/lhdoppojpmngadmnindnejefpokejbdd">axe-core</a>, and <a href="https://www.cypress.io/">Cypress</a>. You can also combine these tools to be even more powerful – for example, using axe-core in conjunction with Cypress is the most comprehensive test suite I can suggest.</p>
<p>Here is a workflow you can follow:</p>
<pre><code class="language-python">async function runAccessibilityAudit(formId) {
  const form = document.getElementById(formId);
  const results = await axe.run(form, {
    rules: {
      'label': { enabled: true },
      'aria-required-attr': { enabled: true },
      'aria-valid-attr-value': { enabled: true },
      'keyboard-navigation': { enabled: true },
      'focus-order-semantics': { enabled: true }
    }
  });
  
  const violations = results.violations.map(violation =&gt; ({
    rule: violation.id,
    impact: violation.impact,
    description: violation.description,
    wcag: violation.tags.filter(tag =&gt; tag.startsWith('wcag')),
    elements: violation.nodes.map(node =&gt; node.target)
  }));
  
  return violations;
}
</code></pre>
<p>When you document keyboard patterns and automate the checks, you make sure everyone can complete your forms – whether they use a mouse, keyboard, or screen reader. Run these checks in your CI/CD pipeline to catch problems before they go live.</p>
<h2 id="heading-ai-informed-navigation-pattern-detection">AI-Informed Navigation Pattern Detection</h2>
<p>Machine learning models can help identify strange keyboard navigation sequences that signal potential usability challenges. Combining mouse tracking with artificial intelligence can give you insights into user behavior that aren't possible using traditional strategies (Sources: <a href="https://www.researchgate.net/publication/334385231_User_Experience_Evaluation_Using_Mouse_Tracking_and_Artificial_Intelligence">Souza and others, IEEE Access</a>).</p>
<p>The logic could similarly be applied to keyboard navigation studies, as AI models can be trained on typical successful navigation patterns to signal when users are struggling in their keyboard flows.</p>
<h3 id="heading-pattern-detection-for-navigation-using-ai">Pattern Detection for Navigation Using AI</h3>
<p>AI tools can assess keyboard navigation to find non-compliant actions during testing. For instance, through machine learning, the code sample below captures, evaluates, and categorizes unusual or problematic keyboard navigation behavior on web forms and detects unique keyboard navigation or k-action sequences that are likely indicative of usability concerns.</p>
<pre><code class="language-python">async function detectNavigationAnomalies(formId) {
  const navigationData = captureKeyboardSequence(formId);
  const features = extractNavigationFeatures(navigationData);
  
  // Use machine learning to classify navigation patterns
  const model = await loadTrainedModel('keyboard-anomaly-detection');
  const predictions = await model.predict(features);
  
  const anomalies = predictions.filter(p =&gt; p.confidence &gt; 0.8).map(p =&gt; ({
    sequence: p.keystrokes,
    anomalyType: p.classification,
    severity: p.confidence,
    recommendation: generateRecommendation(p.classification)
  }));
  
  return anomalies;
}

function extractNavigationFeatures(data) {
  return {
    tabSequenceLength: data.tabs.length,
    backtrackCount: countBacktracks(data.tabs),
    focusTrapIndicators: detectFocusTraps(data),
    averageTimePerElement: calculateAverageTime(data),
    sequenceDeviation: measureSequenceDeviation(data)
  };
}
</code></pre>
<p>The code structure captures the user's keyboard navigation actions on specified forms using <code>captureKeyboardSequence(formId)</code>, which means that the user has entered keystrokes, such as tabbing and focusing between fields.</p>
<p>Using <code>extractNavigationFeatures(data)</code>, the code processes the Raw keyboard navigation sequence by calculating metrics. Lastly, the code loads a previously-trained ML model and provides the extracted features to the model to be classified into their respective categories along with a confidence score.</p>
<p>Any keyboard navigation anomalies detected with a confidence score of 0.8 or higher will be filtered. The detected keystroke sequences, type of anomaly, severity, and specific suggestions will be included in the final results report for later review by the user.</p>
<h3 id="heading-fuzzy-logic-paradigm-for-navigation-performance-scoring">Fuzzy Logic Paradigm for Navigation-Performance Scoring</h3>
<p>Collaborative feedback systems can help improve the virtual keyboard user experience. Fuzzy logic lets you combine multiple measurements – like focus visibility and tab order – into a single score that better reflects the actual user experience. (Source: <a href="https://www.researchgate.net/publication/392950031_Enhancing_User_Experience_of_Virtual_Keyboard_Through_Collaborative_and_Speed-Adaptive_Auditory-Vibrotactile_Feedback">Liu and others, ResearchGate</a>).</p>
<p>For example, the following code creates a fuzzy inference system that evaluates two inputs: Focus Visibility and Tab Order Logic. These two inputs are evaluated using fuzzy logic and associated rules to arrive at a single score representing the total efficiency of keyboard navigation for a given application.</p>
<pre><code class="language-python">function createKeyboardNavigationFuzzySystem() {
  // Define fuzzy variables
  const focusVisibility = new FuzzyVariable('focusVisibility', 0, 100);
  focusVisibility.addTerm('poor', new TriangularMF(0, 0, 40));
  focusVisibility.addTerm('adequate', new TriangularMF(30, 50, 70));
  focusVisibility.addTerm('excellent', new TriangularMF(60, 100, 100));
  
  const tabOrderLogic = new FuzzyVariable('tabOrderLogic', 0, 100);
  tabOrderLogic.addTerm('confusing', new TriangularMF(0, 0, 40));
  tabOrderLogic.addTerm('acceptable', new TriangularMF(30, 50, 70));
  tabOrderLogic.addTerm('intuitive', new TriangularMF(60, 100, 100));
  
  const navigationEfficiency = new FuzzyVariable('navigationEfficiency', 0, 100);
  navigationEfficiency.addTerm('inefficient', new TriangularMF(0, 0, 40));
  navigationEfficiency.addTerm('moderate', new TriangularMF(30, 50, 70));
  navigationEfficiency.addTerm('efficient', new TriangularMF(60, 100, 100));
  
  // Define fuzzy rules
  const rules = [
    'IF focusVisibility IS excellent AND tabOrderLogic IS intuitive THEN navigationEfficiency IS efficient',
    'IF focusVisibility IS poor OR tabOrderLogic IS confusing THEN navigationEfficiency IS inefficient',
    'IF focusVisibility IS adequate AND tabOrderLogic IS acceptable THEN navigationEfficiency IS moderate'
  ];
  
  return new FuzzyInferenceSystem([focusVisibility, tabOrderLogic], navigationEfficiency, rules);
}
</code></pre>
<p>The resulting score is meant to be more representative of a user’s experience with the keyboard navigation system than other existing methods of measuring user satisfaction. This fuzzy inference system assesses keyboard navigation quality based on multiple dimensions at the same time, yielding scores in a similar range to user-reported satisfaction measurements (Source: <a href="https://www.researchgate.net/publication/392950031_Enhancing_User_Experience_of_Virtual_Keyboard_Through_Collaborative_and_Speed-Adaptive_Auditory-Vibrotactile_Feedback">Liu and others, ResearchGate</a>).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article has given you the tools and resources you’ll need to automate a UX audit of your app’s forms – from the initial setup through continuous integration.</p>
<p>You’ve learned:</p>
<ul>
<li><p>Three essential components of forms and the importance of addressing each component systematically.</p>
</li>
<li><p>How to link objectives directly with business KPIs and user outcomes.</p>
</li>
<li><p>How to set up automated testing for the specificity of errors, the timing of validation, and the direction for the next actionable step after an error.</p>
</li>
<li><p>How to review the hint text on your forms: proactive display, proximity to the field it assists, and accessibility.</p>
</li>
<li><p>How to validate keyboard navigation through verification of tab order, testing focus visibility, and using ARIA tags.</p>
</li>
<li><p>How to incorporate WCAG criteria into your automated tests and to incorporate Nielsen's 10 Heuristics.</p>
</li>
<li><p>How to prepare documentation for the handoff between Design and Development, using the "5Cs": Coverage, Completion, Consistency, Clarity, and Correctness.</p>
</li>
</ul>
<p><strong>Next steps:</strong></p>
<ul>
<li><p>Begin with a single high-impact form in your application (signup or checkout) and run the basic automated checks on error messages and keyboard flows.</p>
</li>
<li><p>Integrate one or two audit functions into your existing test suite to see how they surface issues you might have missed manually.</p>
</li>
<li><p>Create a centralized error and hint message library for your most critical forms to ensure consistency.</p>
</li>
<li><p>Gradually expand your audit coverage to additional forms and fields as you refine your automation approach.</p>
</li>
<li><p>Add these checks to your CI/CD pipeline so they run automatically on every pull request, catching regressions before they reach users.</p>
</li>
<li><p>Experiment with AI-powered anomaly detection for keyboard navigation patterns once your foundational audits are stable.</p>
</li>
</ul>
<p>Most importantly, remember that form UX optimization is an ongoing practice, not a one-time fix. Start with the forms that directly impact your conversion metrics, validate that your automated checks catch real user problems, and expand your coverage systematically. Your goal should be to build a reliable system that continuously protects your users from friction.</p>
<h3 id="heading-about-the-author">About the author</h3>
<p>Hope you enjoyed the article and found it helpful. I’ve been a contributor to freeCodeCamp for more than 8 years, and to make this piece more precise and detailed, I used some expert help.</p>
<p>I sincerely thank skilled designers from <a href="https://coaxsoft.com/">COAX Software</a> for the UX and accessibility guidelines, and the developers who provided the audit automation code snippets (who both wished to stay anonymous). The company has a deep expertise in <a href="https://coaxsoft.com/services/ux-audit-services">UI/UX design</a>.</p>
<p>To find out more about me and read more content on tech and digital, visit <a href="https://www.linkedin.com/in/oleg-romanyuk/">https://www.linkedin.com/in/oleg-romanyuk/</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Prioritize as a Product Manager – Product Prioritization Frameworks Explained ]]>
                </title>
                <description>
                    <![CDATA[ Prioritization in product management is hardly about what metric takes precedence over the other. Of all the roles a product manager plays, one of the most difficult is deciding what to work on next. Why? Because everything feels urgent. Engineers wa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-prioritize-as-a-product-manager/</link>
                <guid isPermaLink="false">697bf73e79322b4a93ac0fb8</guid>
                
                    <category>
                        <![CDATA[ Product Management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agile ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ product development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Onyinyechi Nwaucha ]]>
                </dc:creator>
                <pubDate>Fri, 30 Jan 2026 00:11:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769731875495/c2d3feca-a09e-454c-93de-bc00607ac517.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Prioritization in product management is hardly about what metric takes precedence over the other. Of all the roles a product manager plays, one of the most difficult is deciding what to work on next. Why? Because everything feels urgent. Engineers want to fix technical debt, users want improvements, stakeholders have more opinions than practical solutions, and so on. With that kind of pressure and without a proper framework, it is easy to focus on the wrong thing.</p>
<p>And this is where prioritization comes in. In this article, you'll learn how product managers prioritize work, what guides their decisions, and the frameworks they use to decide what does and does not get built.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-prioritization-is-difficult">Why Prioritization is Difficult</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-do-product-managers-really-prioritize">What Do Product Managers Really Prioritize?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-user-problems-and-not-feature-ideas">User Problems and Not Feature Ideas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-business-outcomes-not-tasks">Business Outcomes, not Tasks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-trade-offs-not-preferences">Trade-offs, not Preferences</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-inputs-drive-product-prioritization">What Inputs Drive Product Prioritization?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-user-feedback-and-research">User Feedback and Research</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-product-metrics">Product Metrics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-business-goals">Business Goals</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-technical-limits-and-demands">Technical Limits and Demands</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-prioritization-frameworks">Prioritization Frameworks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-do-prioritization-frameworks-exist">Why Do Prioritization Frameworks Exist?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-rice-scoring">RICE Scoring</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-kano-model">Kano Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-moscow-method">MoSCoW Method</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-ice-framework">ICE Framework</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-impact-vs-effort-matrix">Impact vs Effort Matrix</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-choose-the-right-framework">How to Choose the Right Framework</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-prioritization-mistakes">Common Prioritization Mistakes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-prioritization-is-difficult">Why Prioritization is Difficult</h2>
<p>Prioritization can be difficult because product managers must almost always choose between multiple viable options. Between requests centered on optimization and bug fixing, increasing revenue, and reliability concerns from users, stakeholders, the design team, engineers, and sales departments, we can see how easy it is to build the wrong thing.</p>
<p>Each of the requests would be entirely legitimate when considered separately, but determining which is more crucial at any given moment can be challenging. Prioritization decisions are frequently made based on incomplete information, such as not knowing which features users will actually adopt, whether a request represents a real problem or just a loud minority, or whether it will significantly improve a metric. which, in contrast to engineering tasks, are simple: is the bug there or not? Does the code work or not?</p>
<p>The fact that the trade-offs are not immediately apparent is another factor making prioritization challenging. Other worthwhile projects are postponed or abandoned when a product manager gives priority to one. Although these lost chances don't immediately result in failures, their effects can mount up over time and affect long-term results, growth, and product quality.</p>
<h2 id="heading-what-do-product-managers-really-prioritize">What Do Product Managers Really Prioritize?</h2>
<p>A common misconception is that product managers only prioritize features. PMs don’t just choose between whether to ‘add dark mode’ or ‘add in-app messaging’, they also choose between reducing churn and improving retention. Therefore, what PMs are really prioritizing are:</p>
<h3 id="heading-user-problems-and-not-feature-ideas">User Problems and Not Feature Ideas</h3>
<p>At face value, user feedback consists of solutions and not problems, like:</p>
<ul>
<li><p>Add dark mode</p>
</li>
<li><p>I want to be able to edit a typo after posting on my story</p>
</li>
<li><p>Can you make it possible to export this as a PDF?</p>
</li>
</ul>
<p>A PM won’t just take these requests as they are but will ask why, how, and what.</p>
<ul>
<li><p>What pain point is the user experiencing?</p>
</li>
<li><p>How many users are experiencing this?</p>
</li>
<li><p>Why does the user want this?</p>
</li>
</ul>
<p>After asking these questions, the PM doesn’t just prioritize being adding the dark mode feature but rather, evaluates that users requested for that feature because, apart from personal preference, they struggle to use the product for long periods especially at night and that dark mode is one possible solution and there may be others</p>
<h3 id="heading-business-outcomes-not-tasks">Business Outcomes, not Tasks</h3>
<p>We can look at it this way: a task is something the team does and an outcome is the result the business wants. Some examples of tasks are:</p>
<ul>
<li><p>Fixing bugs</p>
</li>
<li><p>Redesigning the onboarding process</p>
</li>
<li><p>Adding dark mode</p>
</li>
</ul>
<p>Increasing activation rate is one example of an outcome. Product managers prioritize outcomes because they are measurable, connect work to business goals, and tasks alone do not guarantee impact. So, when deciding what to prioritize, a PM considers whether it will increase engagement, improve retention, or reduce churn.</p>
<h3 id="heading-trade-offs-not-preferences">Trade-offs, not Preferences</h3>
<p>This is where things get complicated because, as a product manager, you occasionally have to make trade-offs between things like speed and quality, features and time, resources and scope, quality and cost, technical debt and new features, and so forth.</p>
<p>What this means is that PMs don’t make preference-based decisions when they have to prioritize, but rather, their decisions are constraint-based. This implies that instead of saying, “I prefer this idea,” they are saying, “If we do this now, we can’t do that.”</p>
<h2 id="heading-what-inputs-drive-product-prioritization">What Inputs Drive Product Prioritization?</h2>
<p>Before anything is prioritized, PMs gather input from multiple sources like:</p>
<h3 id="heading-user-feedback-and-research"><strong>User Feedback and Research</strong></h3>
<p>These include interviews, usability tests, user reviews, social media comments, direct feature requests, quantitative data from product analytics, and metrics like Net Promoter Score (NPS) or customer satisfaction (CSAT) scores, which offer a human-centered approach that relies heavily on customer needs, behaviors, and pain points.</p>
<h3 id="heading-product-metrics"><strong>Product Metrics</strong></h3>
<p>Metrics like activation rate, churn, feature adoption, retention, session duration, Daily Active Users (DAU) and Monthly Active Users (MAU), session frequency, and so on, help PMs understand if and where the product is underperforming.</p>
<h3 id="heading-business-goals"><strong>Business Goals</strong></h3>
<p>Prioritization has to align with the company’s strategy and vision because every product exists to serve a business objective, and those objectives directly influence what a product manager prioritizes at any point in time. Business objectives have a big impact on product priorities. While a revenue-focused company might invest in monetization features, a growth-oriented company might prioritize onboarding and acquisition. Product managers frequently give core user experience enhancements and dependability top priority when retention is the aim. Because of these changing objectives, prioritization is dynamic and changes as the company does. So, it depends on whether the business is focused on growth, retention, cost optimization, or revenue.</p>
<h3 id="heading-technical-limits-and-demands"><strong>Technical Limits and Demands</strong></h3>
<p>Simply put, even if an idea is great, it may not be possible at the moment because, even if they sound simple, they may be quite complex to implement. For example, a product that supports offline mode. Also, many features depend on another being completed. Lastly, there is technical debt, which basically refers to shortcuts taken that make a system hard to change in the future. An example includes poor documentation or design. Technical debt, amongst other things, increases bugs and slows down development.</p>
<h2 id="heading-prioritization-frameworks">Prioritization Frameworks</h2>
<p>Prioritization frameworks are structured approaches that product managers use to make explainable and rational decisions. They are decision aids that help PMs compare options. Prioritization frameworks eliminate guesswork from decision-making in product management, thereby helping the product manager make informed guesses instead of instinctive ones.</p>
<p>Let us have a look at some of these frameworks, why they exist, why and when to use them.</p>
<h2 id="heading-why-do-prioritization-frameworks-exist">Why Do Prioritization Frameworks Exist?</h2>
<p>Prioritization frameworks are like aids, not formulas that help a product manager make defensible choices. These frameworks exist to enable PMs make informed decisions, and they do that by asking ‘who?’ and ‘how?’.</p>
<ul>
<li><p>Who does this help?</p>
</li>
<li><p>How does it help?</p>
</li>
<li><p>How is it going to be built?</p>
</li>
<li><p>How does it benefit/align with the company vision?</p>
</li>
</ul>
<p>So no, frameworks do not replace judgement, they only support it. Therefore, instead of making decisions based on whether they think something will be a good idea, PMs they ask the hows and the whats. Some prioritization frameworks in product management include:</p>
<h3 id="heading-rice-scoring">RICE Scoring</h3>
<p>Developed by Sean McBride at Intercom, RICE stands for the four elements—reach, impact, confidence, and effort—that are taken into consideration when developing a product idea. This framework provides an answer to the question of how much value will be produced relative to the effort by comparing each element.</p>
<ul>
<li><p><strong>Reach:</strong> Measures how many users will be affected by the product in a given time period. It is about scale and must always include a timeframe, otherwise the numbers become useless. For example, 5000 users monthly or 35% of weekly active users.</p>
<p>  A small improvement for many users can outweigh a large improvement for very few users. This is evident when comparing the reach of two features, like Feature A, which affects 20,000 users monthly, and Feature B, which affects 500 users monthly. It is evident that even though the improvement is small, its reach surpasses that of the big improvement, therefore making it more significant.</p>
</li>
<li><p><strong>Impact:</strong> Measures how those customers will be affected by the product. Unlike reach which is about scale, impact is about depth or value. <a target="_blank" href="https://www.intercom.com/blog/rice-simple-prioritization-for-product-managers/#:~:text=estimate%20the%20impact%20on%20an%20individual%20person.%20For%20my%20team%2C%20it%E2%80%99s%20%E2%80%9Chow%20much%20will%20this%20project%20increase%20conversion%20rate%20when%20a%20customer%20encounters%20it%3F%E2%80%9D.%20Your%20team%20might%20replace%20this%20with%20another%20goal%2C%20such%20as%20%E2%80%9Cincrease%20adoption%E2%80%9D%2C%20or%20%E2%80%9Cmaximize%20delight%E2%80%9D.">Sean McBride</a> suggests estimating the impact on an individual person. Like, “How much will this project increase the conversion rate when a customer encounters it?” And points out that this can be replaced with other goals, such as “increase adoption” or “maximize delight”. Seeing as impact can be difficult to measure, a scale of 0.25 to 3 is used, where 0.25 is minimal, 0.5 is low, 1 is medium, 2 is high and 3 is massive impact.</p>
</li>
<li><p><strong>Confidence:</strong> This measures the level of confidence in executing ideas and is gotten from sources like user feedback and A/B testing and uses a 100% scale, where 50% is ‘low confidence’, 80% is ‘medium confidence’ and 100% is ‘high confidence’. This accounts for uncertainty and risk, and it helps to answer the question, “How sure are we about this idea?” and prevents speculative ideas from ranking too highly.</p>
</li>
<li><p><strong>Effort:</strong> Refers to the time it will take the team to execute an idea. Effort is like the investment, and the other elements, reach, confidence and impact, are potential benefits. Effort is usually measured in person-months or person-weeks and puts things like engineering time and design work into consideration. This is the most important element in this framework because two ideas with similar value may differ greatly in effort, and a lower effort increases the RICE score.</p>
</li>
</ul>
<p>The RICE formula is:</p>
<p>RICE Score = (<strong>R</strong>each × <strong>I</strong>mpact × <strong>C</strong>onfidence) ÷ <strong>E</strong>ffort</p>
<p>While the RICE scoring method of prioritization is excellent for data-driven teams, it can be too long and difficult to understand.</p>
<h3 id="heading-kano-model"><strong>Kano Model</strong></h3>
<p>This categorizes features based on customer needs and how they affect user satisfaction and was developed by Noraki Kano in 1984. It has three parts, namely:</p>
<ul>
<li><p>Basic needs: Features that customers expect your product to have, like core reliability and login functionality. Sometimes, users don’t notice them when they exist but when they don’t exist, it leads to dissatisfaction.</p>
</li>
<li><p>Performance needs: Features where better performance increases satisfaction. This means that this features scale satisfaction linearly, that is the better they perform, the happier users are. An example is faster load time.</p>
</li>
<li><p>Delighters: Unexpected features that create excitement. These are unexpected features that make users feel heard and happy like TikTok allowing users post images in comment sections.</p>
</li>
</ul>
<p>One of the cons of the kano model is that delighters eventually become basic expectations.</p>
<h3 id="heading-moscow-method"><strong>MoSCoW Method</strong></h3>
<p>This framework was developed by Dai Clegg while working at Oracle in 1994 and it categorizes work into:</p>
<ul>
<li><p>Must have: Essential for the release and can either make, or mar the product.</p>
</li>
<li><p>Should have: Important but not critical. That is, the success of the product is not dependent on it.</p>
</li>
<li><p>Could have: Nice to have but not important.</p>
</li>
<li><p>Won’t have: These are not relevant to the product.</p>
</li>
</ul>
<p>This framework is very easy to use and understand, and that is one of its biggest advantages and it is particularly useful for release planning, MVP definition, and scope control. But the disadvantage is that without proper categorization, every feature can start to look like a must have and categorizing features as a ‘won’t have’ can also prove difficult.</p>
<h3 id="heading-ice-framework"><strong>ICE Framework</strong></h3>
<p>The ICE framework is a lighter-weight alternative to RICE, commonly used when data is limited or speed matters more than precision.</p>
<p>ICE evaluates initiatives based on:</p>
<ul>
<li><p><strong>Impact:</strong> How strongly the initiative is expected to move a key metric.</p>
</li>
<li><p><strong>Confidence:</strong> How certain the team is about the expected impact.</p>
</li>
<li><p><strong>Ease:</strong> How easy the initiative is to implement relative to others.</p>
</li>
</ul>
<p>Each dimension is typically scored from 1–10 with the total average being the ICE score.</p>
<p>ICE works well when:</p>
<ul>
<li><p>Products are early-stage</p>
</li>
<li><p>Teams need quick prioritization</p>
</li>
<li><p>Detailed effort estimates are unavailable</p>
</li>
</ul>
<p>A disadvantage of this framework is that it sacrifices accuracy for speed and it can give biased results.</p>
<h3 id="heading-impact-vs-effort-matrix"><strong>Impact vs Effort Matrix</strong></h3>
<p>This is also known as the <strong>value vs effect</strong> or <strong>value vs effort matrix</strong> and is one of the best suited for visual team discussions and quick alignment with stakeholders.</p>
<p>Initiatives are plotted on a 2×2 grid that is based on the value it will yield (impact) and what it will take to achieve it (effort):</p>
<ul>
<li>High impact/Low effort → They are top priority and also known as big wins.</li>
</ul>
<ul>
<li><p>High impact/High effort → The features that fall into this quadrant are high yielding features but must be planned carefully, hence, the high effort.</p>
</li>
<li><p>Low impact/Low effort → Features that fall here are known as fill-ins. They are optional or nice to have. They require low effort and yield low impact and should only be worked on when high priority tasks are complete.</p>
</li>
<li><p>Low impact/High effort → Low impact and high value? Nobody wants their team to waste time on such features so they should be avoided at all costs.</p>
</li>
</ul>
<p>The elements that make up this framework are much like that of MoSCoW which contributes to its simplicity. It is also intuitive and can serve as a more flexible and visual substitute for RICE. A disadvantage is that it can be subjective and both impact and effort are estimates.</p>
<p>Other frameworks include:</p>
<ul>
<li><p><strong>Weighted Scoring Model</strong>: Prioritizes ideas by scoring them across multiple criteria, such as user impact, strategic alignment, effort, risk, and key metrics, with each criterion weighted according to its importance to the business.</p>
</li>
<li><p><strong>Opportunity Scoring</strong>: Identifies aspects of the product that are of importance to the users but are not being satisfied. The formula for opportunity scoring is: <strong>I</strong>mportance + <strong>(I</strong>mportance – <strong>S</strong>atisfaction<strong>)</strong> = <strong>O</strong>pportunity</p>
</li>
<li><p><strong>Desirability, Feasibility and Viability Framework</strong>: Evaluates ideas based on desirability, feasibility and viability. That is whether users want them, if the team can build them, and finally, if the business can sustain them.</p>
</li>
<li><p><strong>Cost of Delay</strong>: This measures the financial impact of postponing a task and is an excellent framework when dealing with time-sensitive products.</p>
</li>
<li><p><strong>Product Tree</strong>: Is a visual method that replaces the roots, trunk, branches and leaves with systems, current set of features (core functionality), features/enhancements, and new ideas respectively. This helps teams explore ideas and visualize growth in a structured way.</p>
</li>
</ul>
<h2 id="heading-how-to-choose-the-right-framework">How to Choose the Right Framework</h2>
<p>No prioritization framework works in every situation, so choosing the right one depends on the team’s goal at that point in time. Factors that affect how to choose the right framework include:</p>
<ul>
<li><p><strong>The product stage</strong> has significant impacts on framework selection. Early-stage products frequently lack accurate data, therefore a lightweight framework such as Desirability-Feasibility-Viability will be more effective for rapid learning. Growth-stage products often have enough users and analytics to provide structured comparisons, making RICE, Weighted Scoring, and Impact vs Effort more useful. Finally, mature goods prioritize optimization, revenue, and risk, where timing is critical. Frameworks such as the Cost of Delay help to assess added value and urgency.</p>
</li>
<li><p><strong>Data availability</strong> is also an important factor because frameworks presuppose varying degrees of confidence. Using data-driven frameworks without accurate data can result in false precision. When metrics and past performance are available, scoring frameworks become more useful. When they are not, simpler qualitative models are more reliable and honest.</p>
</li>
<li><p><strong>Stakeholder and team requirements</strong> also have to be considered because frameworks are useful for communication as well. They aid persuading stakeholders' decisions, collaboration between teams like design and research is facilitated by user-centered frameworks like the Kano model. Also, teams and stakeholders can rapidly align with frameworks such as the product tree.</p>
</li>
<li><p><strong>Adaptation over rigid use</strong> basically means that frameworks should be modified, instead of using them in a rigid manner. They should be used as guidelines for thinking not a direct path to certainty. That being said, frameworks are often adjusted, simplified, or combined to fit the product stage, available data, and team context.</p>
</li>
</ul>
<h2 id="heading-common-prioritization-mistakes">Common Prioritization Mistakes</h2>
<p>Prioritization can still go wrong even with excellent frameworks and data. Such mistakes are common, but they can have a big impact on long-term results, team trust, and product quality. Some of these mistakes include:</p>
<ul>
<li><p>Prioritizing the loudest speaker is a common mistake in product prioritization which happens more than you’d think. Some stakeholders or team members, for example, speak loudly and frequently that they unintentionally dominate the roadmap even when their ideas or requests don’t align with the user interests or company goals. It is necessary to distinguish between the person making the request and its actual value in order to prioritize tasks effectively.</p>
</li>
<li><p>Confusing urgency with importance is another very common mistake in prioritization because sometimes, everything looks like a must have, but not everything that looks urgent is important. Urgent tasks come with deadlines or short-term pressure—for example, an upcoming demo presentation—but important tasks yield long-term value like reducing technical debt.</p>
</li>
<li><p>Relying on instinct alone and not fact will most likely not get you the desired results as a product manager. If you make a decision just because it feels like a good idea, without validating it through research or grounding it in a prioritization framework, the effort required to fix the resulting mistakes can be significant, often costing more time, resources, and trust.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Finding the ideal framework or making error-free choices are not the goals of prioritization. It is about making conscious trade-offs with limited time, data, and resources—and being able to explain why those trade-offs were necessary. Prioritization frameworks are used as tools to eliminate bias, reveal assumptions, and encourage better decision-making in the face of uncertainty rather than as rigid rules.<br>Finally, because effective prioritization is a constant process, priorities must be revisited often, assumptions must be validated early, and decisions must be documented.</p>
<p>As products evolve, user needs change, and company goals shift, priorities must be examined and adjusted. Product managers who constantly prioritize clarity, proof, and intent are considerably more likely to create solutions that add genuine value for both users and businesses.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Boost Conversions with UX-Focused Microcopy ]]>
                </title>
                <description>
                    <![CDATA[ In this article, I’ll walk you through what microcopy is, why it matters more than you think, and how to write effective, empathetic, and high-converting microcopy. Whether you're a designer, writer, or product builder, you’ll learn practical tips, r... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-boost-conversions-with-ux-focused-microcopy/</link>
                <guid isPermaLink="false">68880d881bd0d07a1731c788</guid>
                
                    <category>
                        <![CDATA[ Microcopy ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux designer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mfonobong Umondia ]]>
                </dc:creator>
                <pubDate>Mon, 28 Jul 2025 23:53:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753746811969/e16afede-4e1a-4499-bb10-ed2a8de8c159.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, I’ll walk you through what microcopy is, why it matters more than you think, and how to write effective, empathetic, and high-converting microcopy.</p>
<p>Whether you're a designer, writer, or product builder, you’ll learn practical tips, real-world examples, and actionable principles you can start applying today.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-problem">The Problem</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-so-what-exactly-is-microcopy">So, What Exactly Is Microcopy?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-designers-should-be-microcopy-maestros">Why Designers Should Be Microcopy Maestros</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-does-microcopy-matter-more-than-you-think">Why Does Microcopy Matter More Than You Think?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-five-principles-for-crafting-microcopy-that-converts">Five Principles for Crafting Microcopy That Converts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-everyday-microcopy-examples-that-boost-ux">Everyday Microcopy Examples That Boost UX</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-the-problem"><strong>The Problem</strong></h2>
<p>Recently, my team and I were redesigning the onboarding and sign-up experience for a kids’ streaming platform that we planned to test in the market. The platform was completely free, but there was one big problem: people weren’t signing up. Many potential users seemed to assume it was just another paid streaming service and dropped off before even trying it.</p>
<p>During one of our team sessions, a designer suggested something simple yet powerful: <strong>“Maybe people think it’s not free. What if we just say that clearly?”</strong> So we added five little words right below the sign-up button: <strong>“No credit card required.”</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749123395863/b1dd1769-491c-4511-b13d-8742b438d824.png" alt="b1dd1769-491c-4511-b13d-8742b438d824" class="image--center mx-auto" width="2160" height="1517" loading="lazy"></p>
<p>That’s it.</p>
<p>The results? Sign-ups jumped by <strong>19%</strong>. Not after a full redesign. Not after a marketing push. But just from one tiny line.</p>
<p>That, my friends, is the power of <strong>microcopy</strong>.</p>
<p>A few well-chosen words can smooth the path, ease anxieties, and encourage users to take action. For designers, mastering microcopy is like unlocking a secret language of persuasion, one that quietly transforms hesitant visitors into loyal users.</p>
<h2 id="heading-so-what-exactly-is-microcopy"><strong>So, What Exactly Is Microcopy?</strong></h2>
<p>In simple terms, microcopy is the text that guides users throughout your product. Think of microcopy as the tiny linguistic signposts and friendly nudges that guide users within your interface. Examples of microcopy include a:</p>
<ul>
<li><p>Call to Action (CTA) on a button: <strong>"Add to Cart"</strong> vs <strong>"Get Yours Now!"</strong></p>
</li>
<li><p>Hint text in a form field: "e.g., <a target="_blank" href="mailto:name@example.com">name@example.com</a>"</p>
</li>
<li><p>Error messages: <strong>"Oops! That password isn't quite right. Try again?"</strong></p>
</li>
<li><p>Success messages: <strong>"Woohoo! Your order is confirmed!"</strong></p>
</li>
<li><p>Tooltips and helper text: <strong>"Your CVV is the 3-digit code on the back of your card."</strong></p>
</li>
<li><p>Loading messages: <strong>"Brewing your results..."</strong></p>
</li>
<li><p>Empty states: <strong>"No projects yet. Ready to create your first masterpiece?"</strong></p>
</li>
</ul>
<p>It’s the voice of your product, speaking directly to your user at crucial moments.</p>
<h2 id="heading-why-designers-should-be-microcopy-maestros"><strong>Why Designers Should Be Microcopy Maestros</strong></h2>
<p><em>“But I'm a designer, not a writer!”</em> I hear you. And that's fair enough. But microcopy is closely tied to the user experience, so it should be part of a designer's toolkit. Here’s why:</p>
<h3 id="heading-it-sets-expectations-amp-provides-clarity">It Sets Expectations &amp; Provides Clarity</h3>
<p>Good microcopy tells users what will happen next.</p>
<p>For instance, “Proceed” is vague, while “Pay Securely Now” is crystal clear. This clarity reduces anxiety and builds trust.</p>
<h3 id="heading-it-guides-and-reduces-friction">It Guides and Reduces Friction</h3>
<p>Let’s say Bella is trying to sign up on a new platform and gets an error message. Instead of "Error 404," imagine: "Hmm, that email address is already in our system. Did you mean to log in?"</p>
<p>Suddenly, she now knows exactly what to do because the system has reminded her. Friction <strong>gone</strong>.</p>
<h3 id="heading-it-humanizes-your-product-amp-builds-brand-voice">It Humanizes Your Product &amp; Builds Brand Voice</h3>
<p>This is where you can inject personality. Is your brand playful? "Got it! We're packing your goodies!" Serious and professional? "Your transaction has been successfully processed."</p>
<p>Microcopy helps establish and reinforce that voice.</p>
<h3 id="heading-it-motivates-action-hello-conversions">It Motivates Action (Hello, Conversions!)</h3>
<p>The right words can be incredibly persuasive. For instance, "Start Your Free Trial" is good. "Start Your 14-Day Free Trial – No Credit Card Required!" is even better because it addresses potential objections upfront.</p>
<h3 id="heading-it-can-delight-and-surprise">It Can Delight and Surprise</h3>
<p>A little charm in unexpected places (like a 404 page or a loading spinner) can turn a moment of potential frustration into a smile. For example, Mailchimp’s "This is your moment of glory" before you send a campaign? Genius.</p>
<h2 id="heading-why-does-microcopy-matter-more-than-you-think"><strong>Why Does Microcopy Matter More Than You Think?</strong></h2>
<p>Good microcopy…</p>
<ul>
<li><p><strong>Reduces friction</strong>: It clearly instructs users on what to do (and what not to do).</p>
</li>
<li><p><strong>Builds trust</strong>: Clear copy helps users feel safe, informed, and in control.</p>
</li>
<li><p><strong>Encourages action</strong>: A well-crafted CTA (Call to Action) can be the nudge someone needs to take the desired action.</p>
</li>
<li><p><strong>Reflects your brand personality</strong>: Whether you’re fun, formal, or friendly, microcopy sets the tone.</p>
</li>
</ul>
<h2 id="heading-five-principles-for-crafting-microcopy-that-converts"><strong>Five Principles for Crafting Microcopy That Converts</strong></h2>
<p>Let’s get into the practical stuff. Here are five field-tested principles I live by:</p>
<h3 id="heading-1-be-clear-first-clever-second">1. Be Clear First, Clever Second</h3>
<p>Yes, wordplay is fun. But if users don’t get it, it’s useless. Always prioritize clarity, and avoid using jargon or overly witty phrases that obscure meaning. The user's understanding comes first.</p>
<p>Also, remember that brevity is your friend. Every word should earn its place. If you can say it in three words instead of five, do it.</p>
<p>Finally, context is king. The same button may require different text depending on its location in the user flow. For example, "Sign Up" on a homepage is fine, while "Create My Profile" on the final step of a registration form might be better.</p>
<p><strong>Example:</strong><br><strong>Instead of:</strong> <em>“Oops! Something went sideways.”</em><br><strong>Try:</strong> <em>“We couldn’t save your changes. Please try again.”</em></p>
<h3 id="heading-2-talk-like-a-human">2. Talk Like a Human</h3>
<p>I get it, you want to be professional. But you must remember that people want to feel like they’re talking to someone who genuinely understands them. Avoid robotic language and internal jargon. Nobody outside your company knows what <em>"Synergize User Data Stream"</em> means on a button.</p>
<p><strong>Before:</strong> <em>“Password does not meet the required criteria.”</em><br><strong>After:</strong> <em>“Oops! Your password needs at least eight characters, one number, and one capital letter.”</em></p>
<p>Imagine explaining it to a friend. That’s the tone you should use.</p>
<h3 id="heading-3-guide-dont-scold">3. Guide, Don’t Scold</h3>
<p>Users make mistakes. That’s okay. Your copy shouldn’t make them feel dumb.</p>
<p><strong>Bad:</strong> <em>“Invalid email.”</em><br><strong>Better:</strong> <em>“Hmm, that email doesn’t look right. Mind checking for typos?”</em></p>
<p>Use microcopy to guide users back on track, rather than chastising them.</p>
<h3 id="heading-4-write-with-purpose">4. Write with Purpose</h3>
<p>Every word should earn its place. What does the user need to know at this exact moment?</p>
<p>Instead of: <em>“Click here to submit your application for review.”</em><br>Use: <em>“Submit Application.”</em></p>
<p>Shorter. Clearer. More direct.</p>
<h3 id="heading-5-infuse-personality-in-the-right-moments">5. Infuse Personality (In the Right Moments)</h3>
<p>There’s a time and place to sprinkle in your brand’s voice. Confirmation pages, 404s, empty states, tooltips, these are great moments for charm.</p>
<p><strong>Example 404:</strong><br><em>“We lost the page (and possibly our minds). Try heading home.”</em></p>
<p>Humor and friendliness create a connection. But when users are frustrated (as during an error), they tend to lean toward helpful and empathetic responses instead.</p>
<h3 id="heading-6-be-action-oriented">6. Be Action-Oriented</h3>
<p>Start with a verb when possible, especially for CTAs. For example, try phrases like "Download Report," "Explore Features," and "Save Changes."</p>
<p>Also, inform users about what they can do, not about what the system does. "View Your Orders" is better than "Orders Will Be Displayed."</p>
<h3 id="heading-7-anticipate-needs-and-alleviate-fears">7. Anticipate Needs and Alleviate Fears</h3>
<p>If a form asks for a phone number, a little note like "We'll only use this for order updates" can make all the difference.</p>
<p>For a "Delete" button, try adding text like "Are you sure? This action cannot be undone." Crucial.</p>
<h3 id="heading-8-test-test-test-and-iterate">8. Test, Test, Test (and Iterate!)</h3>
<p>Don't just set it and forget it. A/B test different versions of your microcopy, especially for key CTAs or tricky form fields.</p>
<p>Watch user testing sessions. Where do people hesitate? What questions do they ask out loud? Those are your cues for better microcopy.</p>
<p>Sometimes, what you think is clear <strong>isn’t</strong>. Humility is a virtue here.</p>
<h2 id="heading-everyday-microcopy-examples-that-boost-ux">Everyday Microcopy Examples That Boost UX</h2>
<p>Here are a few everyday examples and how minor tweaks make a significant impact:</p>
<p>✅ <strong>Form Labels:</strong><br><strong>Instead of:</strong> <em>“Username”</em><br><strong>Use:</strong> <em>“Pick a name your friends will recognize.”</em></p>
<p>✅ <strong>Error Messages:</strong><br><strong>Instead of:</strong> <em>“This field is required.”</em><br><strong>Use:</strong> <em>“Let’s not skip this, your email helps us contact you.”</em></p>
<p>✅ <strong>Onboarding Tips:</strong><br><strong>Instead of:</strong> <em>“Step 1 of 3”</em><br><strong>Use:</strong> <em>“Almost there! Let’s set up your account in 3 quick steps.”</em></p>
<p>✅ <strong>CTA Buttons:</strong><br><strong>Instead of:</strong> <em>“Submit”</em><br><strong>Use:</strong> <em>“Send My Application”</em></p>
<p><strong>Instead of:</strong> <em>“Next”</em><br><strong>Use:</strong> <em>“Continue to Payment”</em></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Microcopy isn't an afterthought – it’s an integral part of the design process.</p>
<p>It is everyone’s job. Designers, writers, engineers, and even product managers are all part of the microcopy puzzle.</p>
<p>If you’re designing the experience, you’re also shaping the story. Don’t wait for a copywriter to jump in. Write it yourself. Then test it. Then tweak it. Then test again.</p>
<p>Start treating every word as part of the design. Because when you do, users feel seen, supported, and confident.</p>
<p>And that’s how you build experiences that not only work, but <strong>convert</strong>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Granular Segmentation with Feature Flags ]]>
                </title>
                <description>
                    <![CDATA[ These days, SaaS has become an integral part of running many businesses. So rolling out new features that resonate with the user base is key to a business’s growth. Imagine a feature that promises to enhance user experience but that ends up resonatin... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-granular-segmentation-with-feature-flags/</link>
                <guid isPermaLink="false">6793a744fc2bd29e0ece1253</guid>
                
                    <category>
                        <![CDATA[ SaaS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kayode Adeniyi ]]>
                </dc:creator>
                <pubDate>Fri, 24 Jan 2025 14:44:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737681693640/2cd6aa99-94bf-48c6-b657-4cc0743312e3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>These days, SaaS has become an integral part of running many businesses. So rolling out new features that resonate with the user base is key to a business’s growth.</p>
<p>Imagine a feature that promises to enhance user experience but that ends up resonating with only a small subset of users. This scenario underscores the importance of precision in feature rollouts.</p>
<p>Fortunately, <a target="_blank" href="https://www.flagsmith.com/">feature flagging management tools</a> like Flagsmith can help with granular segmentation. This process helps your team make sure that new features are introduced to the most relevant audiences. Granular Segmentation makes it easier to understand your user base, leading to higher engagement and satisfaction.  </p>
<p>In this article, we will be focusing on the concept of granular user segmentation and its significance in enhancing feature rollouts. We’ll also explore some best practices, pitfalls to avoid, and will look at how Flagsmith facilitates granular segmentation with feature flags.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-flagsmith">What is Flagsmith?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-granular-segmentation">What is Granular Segmentation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-feature-flags-enable-granular-segmentation">How Feature Flags Enable Granular Segmentation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-granular-segmentation-in-flagsmith">How to Implement Granular Segmentation in Flagsmith</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-granular-segmentation-for-user-engagement">Benefits of Granular Segmentation for User Engagement</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-flagsmith">What is Flagsmith?</h2>
<p>Flagsmith is an open-source feature management platform that helps teams control feature rollouts with precision. It helps developers toggle features on or off for specific users, environments, or groups without redeploying code.</p>
<p>Ideal for A/B testing, phased rollouts, and remote configuration, Flagsmith ensures real-time adjustments and seamless feature delivery. With flexible deployment options – hosted, private cloud, or on-premises – it adapts to the needs of organizations of all sizes.</p>
<h2 id="heading-the-importance-of-granular-segmentation"><strong>The Importance of Granular Segmentation</strong></h2>
<h3 id="heading-what-is-granular-segmentation"><strong>What is Granular Segmentation?</strong></h3>
<p>Granular segmentation is a process in which the user base is divided into groups based on unique attributes such as behavior demographics or engagement levels. These groups can be identified as segments of users of a platform, and each segment is based on several traits that help teams tailor feature rollouts to meet the needs of each segment.  </p>
<p>This granular level of control over rollouts helps product teams release features that resonate with their user base. This creates a more personalized experience for the end user that can improve the effectiveness of the feature. </p>
<p>Now, let’s discuss what kind of an impact feature rollouts might have. </p>
<h3 id="heading-impact-of-feature-rollouts"><strong>Impact of Feature Rollouts</strong></h3>
<p>The advantages of granular segmentation in feature rollouts include:</p>
<ul>
<li><p><strong>Targeted relevance:</strong> Features are delivered to users who will benefit most from them, making the updates more relevant and useful. This targeted approach increases the likelihood of user engagement.</p>
</li>
<li><p><strong>Optimized user experience:</strong> Because of this targeted approach, businesses can prevent rollouts of features that overwhelm their users in any way. This means that users would receive updates according to their interests, leading to a better user experience. </p>
</li>
<li><p><strong>Higher adoption rates:</strong> All this would also lead to higher adoption rates. An increased adoption rate is a sign of good engagement from a business’s users as well as of business growth</p>
</li>
<li><p><strong>Less risk when</strong> <strong>rolling out new features</strong>: Segmenting your user base and releasing new features to, say, a select 10% of users reduces risk. Teams can see how features do with those users and adjust accordingly before rolling out to the next segment. Or they can roll back if the impact is negative, which helps them avoid incidents like the latest high-publicity one we saw with CrowdStrike.</p>
</li>
</ul>
<p>To put things into perspective, let’s discuss an example of an e-commerce store. </p>
<h4 id="heading-the-mart-example"><strong>The MART Example</strong></h4>
<p>The MART is an online store that sells various products. They want to introduce an AI-powered recommendation engine, but only to a subset of their user base that shows less engagement on the platform buying products. AI-powered recommendation engines would target this user segment to generate more sales from the platform and increase business growth.</p>
<p>Here we see the concept of segmentation in practice where a feature is dedicatedly exposed to a user group's explicit attributes, thus leading to increased relevance and user satisfaction.</p>
<p>If the feature proves to be successful with the targeted segment, the next phase would be to expand its availability to other user groups.</p>
<h2 id="heading-how-feature-flags-enable-granular-segmentation"><strong>How Feature Flags Enable Granular Segmentation</strong></h2>
<p>You can integrate Flagsmith into your development workflow by using <a target="_blank" href="https://www.flagsmith.com/sdks">SDKs</a>. The user segmentation adds a layer of granular control to the product teams with over-feature releases. This control helps product teams to minimize the risk of degradation of a new feature. They can leverage the GUI to interact with Flagsmith and roll out/roll back features according to their needs.</p>
<h3 id="heading-what-are-segments-in-feature-rollouts"><strong>What are Segments in Feature Rollouts?</strong></h3>
<p>A segment is a subset of identities, defined by a set of rules based on traits associated with identities. So a single identity can be a part of many segments and is associated with an environment, such as staging or production.</p>
<p>You might be wondering – how can product teams use segments in their feature rollouts?</p>
<p>You can use segments to create ‘overrides’ on any number of features in your application. This allows you to control the state and/or value of a feature for a selection of your users, as defined by the segment.</p>
<p>Now that you understand segments, let’s discuss what key features allow you to use detailed user segmentation.</p>
<ul>
<li><p><strong>User attributes:</strong> Flagsmith allows you to define and manage user attributes, such as location, behavior, subscription levels, or platform activity. These are attributes you can use to create highly specific user segments.</p>
</li>
<li><p><strong>Segment definitions:</strong> You can create custom segment definitions from these user attributes. For instance, you can define a segment for users who have been highly active on the platform since last month or users who live in a different region than most of your user base. This granularity ensures that you can target features to the most relevant user groups.</p>
</li>
<li><p><strong>Dynamic targeting:</strong> Dynamic targeting can help you adjust feature rollouts on the basis of user attributes. This means that you can progressively roll out features to segments of users, monitor their performance, and make adjustments to the feature accordingly.</p>
</li>
</ul>
<h3 id="heading-flexibility-and-control"><strong>Flexibility and Control</strong></h3>
<p>Flexibility and control are a rare combination when it comes to such tools, but with Flagsmith you get the best of both worlds. User segments and feature management ensure you have precision and control over your feature rollouts:</p>
<ul>
<li><p><strong>Granular control:</strong> Multiple segment creation and control access are available in Flagsmith with a variety of criteria, allowing feature rollouts that cater to specific user needs.</p>
</li>
<li><p><strong>Analytics and feedback:</strong> Analytics and feedback are an integral part of the feature testing loop. They provide tracking of how different segments interact with new features. It’s invaluable for understanding the user’s behavior on the platform which helps you make informed decisions for further rollouts.</p>
</li>
</ul>
<p>So now you’ve learned what segments are, what you can do with them, and how segments help in granular control over rollouts. Now, let’s move on and see how you can implement segmentation using Flagsmith.</p>
<h2 id="heading-how-to-implement-granular-segmentation-in-flagsmith"><strong>How to Implement Granular Segmentation in Flagsmith</strong></h2>
<h3 id="heading-set-up-flagsmith-in-your-project"><strong>Set Up Flagsmith in Your Project</strong></h3>
<p>You can integrate Flagsmith into your application using the available SDKs for the language of your choice. For example, to integrate the SDK in Node.js, you’ll first need to install the npm package as follows:</p>
<pre><code class="lang-javascript">npm i flagsmith-nodejs --save
</code></pre>
<p>After installing the package, you will use the following code to initialize Flagsmith in your project:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Flagsmith = <span class="hljs-built_in">require</span>(<span class="hljs-string">'flagsmith-nodejs'</span>);
<span class="hljs-keyword">const</span> flagsmith = <span class="hljs-keyword">new</span> Flagsmith({ <span class="hljs-attr">environmentKey</span>: <span class="hljs-string">'FLAGSMITH_SERVER_SIDE_ENVIRONMENT_KEY'</span>,});
</code></pre>
<p>Once it’s integrated, configure your Flagsmith instance by creating a new project. We’ll go through this below.</p>
<h3 id="heading-how-to-create-identities-and-define-user-traits-and-segments"><strong>How to Create Identities and Define User Traits and Segments</strong></h3>
<p>Now you’ll need to create the identities and traits you want to use for segmentation. These could include user profile information, behavior metrics, or any other relevant data.</p>
<p>So, let’s create a user named John Doe.</p>
<ul>
<li><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcorqiG6dhrwu_3IARs_A3Rgn39I_g_9_cGNEyawmu6SWwqOFCXm_vXm8VGbgDHSzo4LMnnlSQ7DgvE_1_EH_MLBta2_eGhlMSPfabjGR7YwFvTCq3lnBWdoQDdu16x5elbFWp6zGHgmBbpiqdD9PnK4Hgb?key=CLsy_98J-hXFutqrVNKvTw" alt="create new ID on Flagsmith" width="1600" height="703" loading="lazy"></li>
</ul>
<p>Now click on the created user and define a trait country.</p>
<p>In Flagsmith, creating a <em>trait country</em> involves defining a user attribute that specifies their geographic location. Traits are key-value pairs assigned to user identities, allowing for precise segmentation. For example, you can define the "country" trait with values like "USA," "Canada," or "Germany."</p>
<p>This enables product teams to create segments based on location and target feature rollouts accordingly. For instance, a feature can be activated only for users with the "country" trait set to "USA," facilitating controlled and region-specific rollouts.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfsPeipIa35FrYuK7UuEz4g_5wrnwVvlk1YiJs1nNNmWiwszZcSVmb7zfD8CpN81Vh6rxNasuZHk5ze6nFPmkIF4JxFDWmb1gU68hd0CoDbuN5pjOMAZyJnZTCQWwxJPigYeooK7AlC0Mwjte74S9F_PbY?key=CLsy_98J-hXFutqrVNKvTw" alt="Defining trait and country on flagsmith" width="1600" height="640" loading="lazy"></p>
<p>Next, you’ll create some segments. You’ll use the Flagsmith dashboard to create custom segments based on these attributes. For example, you can create a segment for users who are from the USA. Define your segment, for example (western_users ), as below:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdPPGFxPEnBJXEIXNkDGyuC3IJQfE2G4wEtsSWtinIm3Yg_evRmo_ly1_ZPwCqwuWojv7XYI2DP_MMXBQqQy80FFIrccL-KXdmsS9cTrz5T5f9485vDcfiZlH-wkKTZBrk9-Lt9hvKZJgA-3ugQbeoiSfRS?key=CLsy_98J-hXFutqrVNKvTw" alt="Define segments on flagmith" width="1600" height="640" loading="lazy"></p>
<h3 id="heading-how-to-create-and-manage-feature-flags"><strong>How to Create and Manage Feature Flags</strong></h3>
<p>Create a feature flag called ai_recommendation_engine in Flagsmith for the features you want to roll out. Each flag represents a specific feature or configuration option that can be toggled on or off.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXciWtuMzy24Sl_n-i8_lGigMUfbCbV5KdmlAqEotHQiVp7CIw7myLIsVTqltTmZp1STUkAdwNPhGB11PI5tvdHB9dp84x3mjI9rR6ycu7Z-nHYFPUddjBu2adQceVkW8YLvUj6s_tOVpNdA78z3-tL6X06U?key=CLsy_98J-hXFutqrVNKvTw" alt="Specific feature or configuration option on Flagsmith" width="1600" height="653" loading="lazy"></p>
<p>Next, assign your feature flags to the segment you created. For instance, if you have a recommendation engine, you can target it specifically to users that match the segment created in the previous step. Use the Flagsmith dashboard to set these targeting rules and manage feature flag settings.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe4XKYMDUOMCrECTgvp9wK2I2j_HIvDBeDEr1EG0Nf3OdfxducIE-xiDn6GSPRi84veq2K2r0OnvPaCgyuO7xkRVWlpYLjXuJC5F7PS0rP-xzbUL52MO1fHl_E08wAXLsxI8JSLkZP4Q4_NMvrgPRl2OVUw?key=CLsy_98J-hXFutqrVNKvTw" alt="Setting up targetting rules on Flagsmith" width="1600" height="611" loading="lazy"></p>
<h3 id="heading-how-to-target-segments-for-rollouts"><strong>How to Target Segments for Rollouts</strong></h3>
<p>After configuring Flagsmith and setting up your segments and traits, you can start rolling out features to your defined segments.</p>
<p>First, you’ll want to do gradual rollouts. Using the percentage split operator, you can initially release the feature to a small percentage of users within the segment. Based on performance and feedback, you can gradually expand the rollout to a larger portion of the segment or additional segments, ensuring a controlled and data-driven approach.</p>
<p>Second, monitoring is a crucial part of feature rollouts and Flagsmith can help you with its analytics tools. You can track the performance of your feature flags and user segments, monitor how different segments interact with the new features, and make adjustments as needed.</p>
<p>For example, you might decide to increase the rollout percentage or adjust segment definitions based on user feedback.</p>
<h4 id="heading-some-best-practices">Some best practices:</h4>
<ul>
<li><p><strong>Start small:</strong> To test out segmentation, it’s a good idea to start small and create well-defined segments to test new features. This will help you gather valuable feedback and will prevent you from being overwhelmed in case of degraded performance or a rollback scenario.</p>
</li>
<li><p><strong>Use data:</strong> Analytical tools are a great help in gathering data on how different segments interact with your features. You can use this data to refine your targeting and improve the user experience.</p>
</li>
<li><p><strong>Iterate:</strong> You’ll likely make better decisions after several iterations. So remember that you should iterate your segmentation and rollouts based on metrics and user feedback.</p>
</li>
</ul>
<h4 id="heading-some-common-pitfalls">Some common pitfalls:</h4>
<ul>
<li><p><strong>Overlapping segments:</strong> Distinction between segmentations is the key to avoiding conflicts between feature targeting. Always be careful while defining segments for your user groups.</p>
</li>
<li><p><strong>Ignoring feedback:</strong> The greatest mistake a product team can make is to overlook early user feedback. Early feedback is crucial for identifying issues and making informed decisions about a feature rollout.</p>
</li>
</ul>
<p>By following these steps and best practices, you can effectively use this granular segmentation approach, ensuring that your feature rollouts are targeted, relevant, and successful.</p>
<h2 id="heading-benefits-of-granular-segmentation-for-user-engagement"><strong>Benefits of Granular Segmentation for User Engagement</strong></h2>
<h3 id="heading-improved-user-satisfaction"><strong>Improved User Satisfaction</strong></h3>
<p>Granular segmentation helps your users out, as it gives them specifically personalized features according to their needs and inclinations. You can build more personalized experiences by aiming certain features at particular users that match their behavior or interest.</p>
<p>For example, a fitness app might launch an update that contains a workout feature for those users who have shown interest in strength building, rather than for all users. This targeted approach ensures that users receive updates related to and suitable to them, which leads to a positive experience, increased satisfaction, and better recognition of your product.</p>
<h3 id="heading-increased-engagement"><strong>Increased Engagement</strong></h3>
<p>When users get features or updates that are targeted toward their specific needs, it’s more likely that they’ll engage with that feature. Granular segmentation helps maximize engagement by providing users with upgrades that are pertinent to their interests and usage patterns.</p>
<p>For example, an e-commerce platform could propose a new recommendation system and try it out on users who recurrently browse specific categories. This relevant targeting will likely increase the probability that those users will respond to those recommendations, leading to increased engagement and potentially higher conversions.</p>
<h3 id="heading-enhanced-feature-adoption"><strong>Enhanced Feature Adoption</strong></h3>
<p>Targeting specific segments of users with features that address their needs should lead to higher adoption rates. Presenting new features to users who are very likely to benefit from them, you increase the probability of these features being adopted and utilized.</p>
<p>For example, a software company introducing a new improved analytics tool would likely target power users who consistently use analytics features. After those users provide positive feedback and adopt the tool, it can be deployed on other segments. Then the team can be confident that the feature is approved and effective.</p>
<h3 id="heading-data-driven-insights"><strong>Data-Driven Insights</strong></h3>
<p>Granular segmentation offers valuable insights into how various user groups engage with new features. Analyzing this data can provide you insights into the behavior and inclinations your users as well as the overall impact of your features.</p>
<p>For example, you might realize that users are responsive to new features in a specific segment compared to other segments. Such information helps you refine your feature strategy, making rational decisions regarding future launches, and enhancing user engagement across different segments.</p>
<h3 id="heading-optimized-resource-allocation"><strong>Optimized Resource Allocation</strong></h3>
<p>Centering on targeted segments lets you allocate resources more effectively. instead of investing in a broad, one-size-fits-all approach, you can direct your initiative towards segments that are likely to benefit from and engage with new features. This optimized allocation assures that your resources are utilized efficiently, leading to positive outcomes and a higher return on investment.</p>
<p>By leveraging granular segmentation, you can enhance user engagement, improve feature adoption, and gain valuable insights, all of which contribute to a more successful and user-centric feature rollout strategy.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this article, we discussed the power of granular user segmentation in driving successful feature rollouts, highlighting how it can improve user satisfaction, engagement, and adoption rates. We also explored how Flagsmith enables this approach, offering tools to manage and target features with precision.</p>
<p>By leveraging these strategies, you can ensure that your product updates are more relevant and impactful. If you're interested in optimizing your feature rollouts, consider exploring Flagsmith’s capabilities to start making data-driven decisions.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Improve User Experience with Optimistic UI and SWR ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever noticed how some apps feel like they can read your mind? You click a button, and before you can even blink, it's done – no loading screens, no waiting around. It's like magic, right? Well, let me tell you a little secret: that's the pow... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/improve-user-experience-with-optimistic-ui-swr/</link>
                <guid isPermaLink="false">66bb8911b0d3ac3d7acde3db</guid>
                
                    <category>
                        <![CDATA[ UI Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Tue, 09 Jul 2024 22:33:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Article-cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever noticed how some apps feel like they can read your mind? You click a button, and before you can even blink, it's done – no loading screens, no waiting around. It's like magic, right? Well, let me tell you a little secret: that's the power of Optimistic UI.</p>
<p>In this article, we'll dive into Optimistic UI and explore how it works and keeps your web experience smooth as butter. We'll build a simple task app together that'll show how Optimistic UI can help turn mundane tasks into lightning-fast interactions that leave your users feeling happy.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li>Fundamentals of JavaScript and React</li>
<li>Fundamentals of Async programming and Axios</li>
<li>Knowledge of hook-oriented fetch libraries would also be beneficial</li>
</ul>
<h2 id="heading-what-well-cover"><strong>What We'll Cover:</strong></h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-optimistic-ui">What is Optimistic UI?</a></li>
<li><a class="post-section-overview" href="#heading-why-does-optimistic-ui-matter">Why Does Optimistic UI Matter?</a></li>
<li><a class="post-section-overview" href="#heading-other-benefits-of-optimistic-ui">Other Benefits of Optimistic UI</a></li>
<li><a class="post-section-overview" href="#heading-introducing-swr-stale-while-revalidate">Introducing SWR: Stale-While-Revalidate</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-environment">How to Set Up the Environment</a></li>
<li><a class="post-section-overview" href="#building-the-task-app-ui">How to Build the Task App UI</a><br>– <a class="post-section-overview" href="#heading-regular-crud-ui">Regular CRUD UI</a><br>– <a class="post-section-overview" href="#heading-optimistic-crud-ui">Optimistic CRUD UI</a></li>
<li><a class="post-section-overview" href="#heading-drawbacks-of-optimistic-ui">Drawbacks of Optimistic UI</a></li>
<li><a class="post-section-overview" href="#heading-ideal-use-cases-for-optimistic-ui">Ideal Use Cases for Optimistic UI</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-what-is-optimistic-ui">What is Optimistic UI?</h2>
<p>At its core, Optimistic UI is all about keeping your app feeling snappy and responsive, even when a lot is happening behind the scenes. It's like having a superpower that lets your app predict the future – well, sort of.</p>
<p>When you perform an action in your app – whether it's adding a new item to a list or updating a profile – Optimistic UI kicks in to make it happen right away, without waiting for confirmation from the server. It's the ultimate optimist, always assuming everything will work out just fine.</p>
<h2 id="heading-why-does-optimistic-ui-matter">Why Does Optimistic UI Matter?</h2>
<p>So why should you care about Optimistic UI? Simple: because it's the secret sauce that turns good apps into great ones. </p>
<p>Think about it: when you click a button, you expect something to happen – and you expect it to happen fast. That's where Optimistic UI shines. By giving your users instant feedback and keeping your app feeling snappy, Optimistic UI enhances the overall user experience. </p>
<p>No more staring at loading screens or wondering if your click <em>actually</em> did anything – with Optimistic UI, every action feels easy and effective.</p>
<h2 id="heading-other-benefits-of-optimistic-ui">Other Benefits of Optimistic UI</h2>
<ol>
<li><strong>Reduced Perceived Latency</strong>: Optimistic UI reduces the perceived latency of actions by displaying changes immediately without waiting for server confirmation. This creates a perception of faster response times, even if server communication takes longer.</li>
<li><strong>Improved Responsiveness</strong>: Optimistic UI allows users to interact with the app continuously without interruptions from loading spinners or waiting screens. This uninterrupted flow enhances the overall responsiveness of the application.</li>
<li><strong>Support for Complex Interactions</strong>: Optimistic UI helps complex interactions, such as drag-and-drop, multi-step processes, and real-time collaboration, feel fluid and intuitive. This flexibility opens up possibilities for innovative features and functionalities in the app.</li>
<li><strong>Increased User Engagement</strong>: The responsiveness and interactivity provided by Optimistic UI can lead to increased user engagement and retention. Users are more likely to return to an app that provides a smooth and enjoyable experience.</li>
</ol>
<h2 id="heading-introducing-swr-stale-while-revalidate">Introducing SWR: Stale-While-Revalidate</h2>
<p>Before we dive into the implementation, let's take a moment to talk about SWR. SWR is a lightweight React Hook library for data fetching. SWR stands for <a target="_blank" href="https://swr.vercel.app/examples/optimistic-ui">Stale-While-Revalidate</a>, and it strikes the perfect balance between performance and freshness when fetching data in your React applications.</p>
<p>SWR also automatically revalidates data in the background while still serving stale data from the cache. This means your app stays fast and responsive, even when fetching fresh data from the server.</p>
<p>But that's not all – SWR also supports key features like caching, pagination, and error handling, making it a powerful tool in your arsenal for building fast, reliable web applications as well as implementing Optimistic UI.</p>
<h2 id="heading-how-to-set-up-the-environment">How to Set Up the Environment</h2>
<p>I've prepared a GitHub repository with starter files to speed things up. Simply <a target="_blank" href="https://github.com/Daiveedjay/Optimistic-UI-with-SWR/tree/starter">clone this repo</a> and install the dependencies.</p>
<p>The starter code consists of the basic JSX components required, as well as some basic <a target="_blank" href="https://axios-http.com/docs/intro">Axios</a> functions for performing CRUD operations. After installing all the necessary packages with <code>npm i</code>, open your terminal and start up your local endpoint using <a target="_blank" href="https://www.npmjs.com/package/json-server">json-server</a>.</p>
<pre><code class="lang-bash">npx json-server data/db.json -p 3500
</code></pre>
<p>To see all your data present, head over to that route:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/1-Showing-initial-data.png" alt="Image" width="600" height="400" loading="lazy">
<em>Showing initial data</em></p>
<h2 id="heading-how-to-build-the-task-app-ui">How to Build the Task App UI</h2>
<p>In this section, we’ll first implement CRUD applications without Optimistic UI and then with Optimistic UI to show the differences between them.</p>
<h3 id="heading-regular-crud-ui">Regular CRUD UI</h3>
<p>Start by heading over to your <code>TaskContainer</code> component, then use the <code>useSWR</code> hook to call your fetch function.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> {
    isLoading,
    error,
    <span class="hljs-attr">data</span>: tasks,
    mutate,
  } = useSWR(cacheKey, fetchTasks, {
    <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span>
      data.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(b.createdAt) - <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(a.createdAt)),
  });
</code></pre>
<p>SWR uses a similar data fetching hook and pattern to other libraries such as <a target="_blank" href="https://tanstack.com/query/latest/docs/framework/react/overview">React Query (TanStack Query)</a> and <a target="_blank" href="https://redux-toolkit.js.org/rtk-query/overview">Redux Toolkit Query</a>. This hook fetching pattern often returns a loading state, an error state, your fetched data (if any) and a mutation function (but more about that later).</p>
<p><strong>Note</strong>: The <code>cacheKey</code> is a unique key used to notify SWR when and where to re-call your function. The <code>onSuccess</code> function is a method used to trigger another action when the fetch is successful – in this case, sorting the data in descending order.</p>
<p>With your data back, you can now create the JSX markup.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> (

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-8 p-4"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 shadow-lg "</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-4 "</span>&gt;</span>
            {tasks &amp;&amp;
              tasks.map((task, index) =&gt; {
                return (
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                    <span class="hljs-attr">key</span>=<span class="hljs-string">{task.id}</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-4 items-center py-2 px-6 rounded-md bg-[#74a0a6]"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                        <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                        <span class="hljs-attr">key</span>=<span class="hljs-string">{task.id}</span>
                        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">gap-4</span> <span class="hljs-attr">text-</span>[<span class="hljs-attr">14px</span>] <span class="hljs-attr">items-center</span> <span class="hljs-attr">font-bold</span> <span class="hljs-attr">list-none</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded</span> <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">88adb3</span>] <span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">609299</span>]`}&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex items-center"</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"relative flex items-center p-3 rounded-full cursor-pointer"</span>
                            <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"checkbox"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                              <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
                              <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                              <span class="hljs-attr">id</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                              <span class="hljs-attr">className</span>=<span class="hljs-string">"before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-[#edebd9] transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity checked:border-lines checked:bg-[#545240] checked:before:bg-[#edebd9] hover:before:opacity-10 before:checked:hover:before:opacity-10 "</span>
                              <span class="hljs-attr">checked</span>=<span class="hljs-string">{task.completed}</span>
                                                         /&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute transition-opacity opacity-0 pointer-events-none text-stone-100 top-2/4 left-2/4 -translate-y-2/4 -translate-x-2/4 peer-checked:opacity-100"</span>&gt;</span>
                              <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                                <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
                                <span class="hljs-attr">className</span>=<span class="hljs-string">"h-3.5 w-3.5"</span>
                                <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 20 20"</span>
                                <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
                                <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                                <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">"1"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                                  <span class="hljs-attr">fillRule</span>=<span class="hljs-string">"evenodd"</span>
                                  <span class="hljs-attr">d</span>=<span class="hljs-string">"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"</span>
                                  <span class="hljs-attr">clipRule</span>=<span class="hljs-string">"evenodd"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
                              <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-[#161515] "</span>&gt;</span>
                        {task.title}
                      <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm font-semibold text-[#42403f] "</span>&gt;</span>
                        {task.description}
                      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 mt-2 text-xs font-bold"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center "</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                            <span class="hljs-attr">src</span>=<span class="hljs-string">{userImages[index]}</span>
                            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 h-10 rounded-full "</span>
                          /&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {task.assignedTo}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                      <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 ml-auto rounded-full cursor-pointer hover:bg-red-300"</span>
                    &gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">FaTrash</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#545240"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                );
              })}
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  );
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/2-UI-after-fetcing-data.png" alt="Image" width="600" height="400" loading="lazy">
<em>UI after data fetching</em></p>
<p>After that, head into your <code>Taskform</code> component and create a form UI for creating new tasks.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { addSingleTask } <span class="hljs-keyword">from</span> <span class="hljs-string">"./services/api"</span>;
<span class="hljs-keyword">import</span> toast <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hot-toast"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Taskform</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [title, setTitle] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [description, setDescription] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [assignedTo, setAssignedTo] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-[#74a0a6] p-4 rounded-md"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col w-full gap-2 "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"title"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold "</span>&gt;</span>Title<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full font-medium  focus:outline-[#74a0a6] focus-within:outline-[#74a0a6] p-1 bg-transparent border rounded-md"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{title}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTitle(e.target.value)}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"description"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold "</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full font-medium  focus:outline-[#74a0a6] focus-within:outline-[#74a0a6] p-1 bg-transparent border rounded-md"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{description}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setDescription(e.target.value)}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"assignedTo"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold "</span>&gt;</span>Assigned To<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full font-medium  focus:outline-[#74a0a6] focus-within:outline-[#74a0a6] p-1 bg-transparent border rounded-md"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{assignedTo}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setAssignedTo(e.target.value)}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 mt-3 border text-white rounded-md w-max hover:bg-white hover:text-[#74a0a6]"</span>&gt;</span>
          Add
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>After that, import it into your <code>TaskContainer</code> component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> (

      &lt;div className="flex flex-col gap-8 p-4"&gt;
        &lt;Taskform /&gt;
        &lt;div className="p-4 shadow-lg "&gt;
          &lt;div className="flex flex-col gap-4 "&gt;
            {tasks &amp;&amp;
              tasks.map((task, index) =&gt; {
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/3-UI-with-Form-added.png" alt="Image" width="600" height="400" loading="lazy">
<em>UI with Form added</em></p>
<p>To add a new task, create a handler function in the <code>Taskform</code>, then import your <code>POST</code> function from your API file.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> addTaskMutation = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">const</span> createdAt = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString(); <span class="hljs-comment">// Get current timestamp as a string</span>

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> addSingleTask({
        title,
        description,
        assignedTo,
        <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span>,
        createdAt,
      });

      toast.success(<span class="hljs-string">"Task added succesfully."</span>);
      setTitle(<span class="hljs-string">""</span>);
      setDescription(<span class="hljs-string">""</span>);
      setAssignedTo(<span class="hljs-string">""</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to add the new task."</span>);
    }
  };
</code></pre>
<p>Finally, call the <code>mutate</code> function after your <code>POST</code> function call to enable SWR to invalidate your current data and make a new request. You can get this mutate function from the <code>useSWR</code> hook in the <code>TaskContainer</code>, then pass it through props to the form.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> {
    isLoading,
    error,
    <span class="hljs-attr">data</span>: tasks,
    mutate,
  } = useSWR(cacheKey, fetchTasks, {
    <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span>
      data.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(b.createdAt) - <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(a.createdAt)),
  });
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-8 p-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Taskform</span> <span class="hljs-attr">mutate</span>=<span class="hljs-string">{mutate}</span> /&gt;</span></span>
</code></pre>
<p>Then call it in the <code>TaskForm</code>.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { addSingleTask } <span class="hljs-keyword">from</span> <span class="hljs-string">"./services/api"</span>;
<span class="hljs-keyword">import</span> toast <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hot-toast"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Taskform</span>(<span class="hljs-params">{ mutate }</span>) </span>{
  <span class="hljs-keyword">const</span> [title, setTitle] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [description, setDescription] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [assignedTo, setAssignedTo] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> addTaskMutation = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">const</span> createdAt = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString();
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> addSingleTask({
        title,
        description,
        assignedTo,
        <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span>,
        createdAt,
      });
      mutate();

      toast.success(<span class="hljs-string">"Task added succesfully."</span>);
      setTitle(<span class="hljs-string">""</span>);
      setDescription(<span class="hljs-string">""</span>);
      setAssignedTo(<span class="hljs-string">""</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to add the new task."</span>);
    }
  };
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-[#74a0a6] p-4 rounded-md"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col w-full gap-2 "</span>
        <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> addTaskMutation(e)}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"title"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold "</span>&gt;</span>Title<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full font-medium  focus:outline-[#74a0a6] focus-within:outline-[#74a0a6] p-1 bg-transparent border rounded-md"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{title}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTitle(e.target.value)}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"description"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold "</span>&gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full font-medium  focus:outline-[#74a0a6] focus-within:outline-[#74a0a6] p-1 bg-transparent border rounded-md"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{description}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setDescription(e.target.value)}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"assignedTo"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold "</span>&gt;</span>Assigned To<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full font-medium  focus:outline-[#74a0a6] focus-within:outline-[#74a0a6] p-1 bg-transparent border rounded-md"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{assignedTo}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setAssignedTo(e.target.value)}
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 mt-3 border text-white rounded-md w-max hover:bg-white hover:text-[#74a0a6]"</span>&gt;</span>
          Add
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>Testing your component now gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/1-Regular-Create-Operation.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Regular Create Operation</em></p>
<p>As you can see, the list is updated after each form submission. But this still doesn’t highlight our need for optimistic UI. You’re probably thinking, if the operation happened that fast, why bother with Optimistic UI?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/2-What-s-the-point.gif" alt="Image" width="600" height="400" loading="lazy">
<em>What's the point gif</em></p>
<p>Well, for starters, no real-world application is ever going to beat the speed of your local JSON server, as the data is readily available to you and users often have unstable network connections.</p>
<p>Let's slow down the fetch to illustrate a real-world data request better. This better simulates a real-world scenario as users often come from different locations that have varying internet speeds.</p>
<p>Start by creating a delay function that runs before each of your function calls.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">const</span> tasksApi = axios.create({
  <span class="hljs-attr">baseURL</span>: <span class="hljs-string">"http://localhost:3500"</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tasksUrlEndpoint = <span class="hljs-string">"/tasks"</span>;

<span class="hljs-keyword">const</span> delay = <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> res(), <span class="hljs-number">1200</span>));

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> fetchTasks = <span class="hljs-keyword">async</span> () =&gt; {
   <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> tasksApi.get(tasksUrlEndpoint);
  <span class="hljs-keyword">return</span> response.data;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> addSingleTask = <span class="hljs-keyword">async</span> ({
  title,
  description,
  completed,
  assignedTo,
  createdAt,
}) =&gt; {
  <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> tasksApi.post(tasksUrlEndpoint, {
    title,
    description,
    completed,
    assignedTo,
    createdAt,
  });
  <span class="hljs-keyword">return</span> response.data;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateSingleTask = <span class="hljs-keyword">async</span> (task) =&gt; {
  <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> tasksApi.patch(<span class="hljs-string">`<span class="hljs-subst">${tasksUrlEndpoint}</span>/<span class="hljs-subst">${task.id}</span>`</span>, task);
  <span class="hljs-keyword">return</span> response.data;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteSingleTask = <span class="hljs-keyword">async</span> ({ id }) =&gt; {
  <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> tasksApi.delete(<span class="hljs-string">`<span class="hljs-subst">${tasksUrlEndpoint}</span>/<span class="hljs-subst">${id}</span>`</span>, id);
};
</code></pre>
<p>Then attempt your create operation again.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/3-Create-operation-after-delay.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Create operation after delay</em></p>
<p>As you may have noticed, the create operation was only fired after the delay function (spanning 1.2 seconds) finished running, which caused a brief spell of inactivity on the screen. </p>
<p>The usual way to handle those periods between loading is usually a loading spinner or indicator telling you that some background activity is running. But this often disrupts your flow when working in the application, and quite frankly is disappointing.</p>
<p>The same static effect can be seen in the update operation, where users have to wait for server confirmation to see fresh data.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> updateTaskMutation = <span class="hljs-keyword">async</span> (updatedTask) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> updateSingleTask(updatedTask);
      mutate();
      toast.success(<span class="hljs-string">"Successfully updated task"</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to update the task."</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-8 p-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Taskform</span> <span class="hljs-attr">mutate</span>=<span class="hljs-string">{mutate}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 shadow-lg "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-4 "</span>&gt;</span>
          {tasks &amp;&amp;
            tasks.map((task, index) =&gt; {
              return (
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                  <span class="hljs-attr">key</span>=<span class="hljs-string">{task.id}</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-4 items-center py-2 px-6 rounded-md bg-[#74a0a6]"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                      <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                      <span class="hljs-attr">key</span>=<span class="hljs-string">{task.id}</span>
                      <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">gap-4</span> <span class="hljs-attr">text-</span>[<span class="hljs-attr">14px</span>] <span class="hljs-attr">items-center</span> <span class="hljs-attr">font-bold</span> <span class="hljs-attr">list-none</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded</span> <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">88adb3</span>] <span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">609299</span>]`}&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex items-center"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                          <span class="hljs-attr">className</span>=<span class="hljs-string">"relative flex items-center p-3 rounded-full cursor-pointer"</span>
                          <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"checkbox"</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
                            <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                            <span class="hljs-attr">id</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-[#edebd9] transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity checked:border-lines checked:bg-[#545240] checked:before:bg-[#edebd9] hover:before:opacity-10 before:checked:hover:before:opacity-10 "</span>
                            <span class="hljs-attr">checked</span>=<span class="hljs-string">{task.completed</span> === <span class="hljs-string">true}</span>
                            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{()</span> =&gt;</span>
                              updateTaskMutation({
                                ...task,
                                completed: !task.completed,
                              })
                            }
                          /&gt;
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute transition-opacity opacity-0 pointer-events-none text-stone-100 top-2/4 left-2/4 -translate-y-2/4 -translate-x-2/4 peer-checked:opacity-100"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                              <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
                              <span class="hljs-attr">className</span>=<span class="hljs-string">"h-3.5 w-3.5"</span>
                              <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 20 20"</span>
                              <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
                              <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                              <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">"1"</span>&gt;</span>
                              <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                                <span class="hljs-attr">fillRule</span>=<span class="hljs-string">"evenodd"</span>
                                <span class="hljs-attr">d</span>=<span class="hljs-string">"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"</span>
                                <span class="hljs-attr">clipRule</span>=<span class="hljs-string">"evenodd"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-[#161515] "</span>&gt;</span>
                      {task.title}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm font-semibold text-[#42403f] "</span>&gt;</span>
                      {task.description}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 mt-2 text-xs font-bold"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center "</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                          <span class="hljs-attr">src</span>=<span class="hljs-string">{userImages[index]}</span>
                          <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
                          <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 h-10 rounded-full "</span>
                        /&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {task.assignedTo}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 ml-auto rounded-full cursor-pointer hover:bg-red-300"</span>
                   &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">FaTrash</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#545240"</span> /&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              );
            })}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/4-Update-operation-after-delay.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Update operation after delay</em></p>
<p>And in the delete operation, which also waits for server conformation to rehydrate the page.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> deleteTaskMutation = <span class="hljs-keyword">async</span> ({ id }) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> deleteSingleTask({ id });
      mutate();
      toast.success(<span class="hljs-string">"Successfully deleted task"</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to delete the task."</span>);
    }
  };
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-8 p-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Taskform</span> <span class="hljs-attr">mutate</span>=<span class="hljs-string">{mutate}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 shadow-lg "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-4 "</span>&gt;</span>
          {tasks &amp;&amp;
            tasks.map((task, index) =&gt; {
              return (
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                  <span class="hljs-attr">key</span>=<span class="hljs-string">{task.id}</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-4 items-center py-2 px-6 rounded-md bg-[#74a0a6]"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                      <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                      <span class="hljs-attr">key</span>=<span class="hljs-string">{task.id}</span>
                      <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">gap-4</span> <span class="hljs-attr">text-</span>[<span class="hljs-attr">14px</span>] <span class="hljs-attr">items-center</span> <span class="hljs-attr">font-bold</span> <span class="hljs-attr">list-none</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">rounded</span> <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">88adb3</span>] <span class="hljs-attr">cursor-pointer</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">609299</span>]`}&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex items-center"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                          <span class="hljs-attr">className</span>=<span class="hljs-string">"relative flex items-center p-3 rounded-full cursor-pointer"</span>
                          <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"checkbox"</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
                            <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                            <span class="hljs-attr">id</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">task-</span>${<span class="hljs-attr">task.id</span>}`}
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-[#edebd9] transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity checked:border-lines checked:bg-[#545240] checked:before:bg-[#edebd9] hover:before:opacity-10 before:checked:hover:before:opacity-10 "</span>
                            <span class="hljs-attr">checked</span>=<span class="hljs-string">{task.completed</span> === <span class="hljs-string">true}</span>
                            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{()</span> =&gt;</span>
                              updateTaskMutation({
                                ...task,
                                completed: !task.completed,
                              })
                            }
                          /&gt;
                          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute transition-opacity opacity-0 pointer-events-none text-stone-100 top-2/4 left-2/4 -translate-y-2/4 -translate-x-2/4 peer-checked:opacity-100"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                              <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
                              <span class="hljs-attr">className</span>=<span class="hljs-string">"h-3.5 w-3.5"</span>
                              <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 20 20"</span>
                              <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
                              <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                              <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">"1"</span>&gt;</span>
                              <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                                <span class="hljs-attr">fillRule</span>=<span class="hljs-string">"evenodd"</span>
                                <span class="hljs-attr">d</span>=<span class="hljs-string">"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"</span>
                                <span class="hljs-attr">clipRule</span>=<span class="hljs-string">"evenodd"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold text-[#161515] "</span>&gt;</span>
                      {task.title}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm font-semibold text-[#42403f] "</span>&gt;</span>
                      {task.description}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-2 mt-2 text-xs font-bold"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center "</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                          <span class="hljs-attr">src</span>=<span class="hljs-string">{userImages[index]}</span>
                          <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
                          <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 h-10 rounded-full "</span>
                        /&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {task.assignedTo}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 ml-auto rounded-full cursor-pointer hover:bg-red-300"</span>
                    <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> deleteTaskMutation({ id: task.id })}&gt;
                    <span class="hljs-tag">&lt;<span class="hljs-name">FaTrash</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#545240"</span> /&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              );
            })}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/5-Delete-operation-after-delay.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Delete operation after delay</em></p>
<p>These few seconds of inactivity or loading can impact the level of satisfaction users get from your application, which is why we’re going to use Optimistic UI to fix it.</p>
<h3 id="heading-optimistic-crud-ui">Optimistic CRUD UI</h3>
<p>The way this works in practical terms is that, when you perform an action, it immediately adds to your UI state (cache) while the async operation is running in the background.</p>
<p>If the operation is successful, nothing on the UI changes and everything acts like it worked on the first try. But if it fails, the UI state reverts to its previous state and an error is displayed via your toast.</p>
<p>An optimistic UI approach offers a much better user experience than traditional loading messages or spinners. When you see an immediate response after clicking a button, the app feels faster and more responsive, keeping you engaged and satisfied. You can continue interacting with the app seamlessly, without waiting for server confirmations, making the experience smoother and more intuitive.</p>
<p>This immediate feedback reduces your perceived wait time and keeps the interface visually stable, avoiding annoying flickers or sudden changes. Plus, when the app feels this responsive, you're more likely to keep using it and have a positive experience.</p>
<p>On the flip side, loading messages or spinners can interrupt your flow, making the app feel slower and potentially frustrating you.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/Group-369-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Optimistic UI diagram</em></p>
<p>It still sounds a little like gibberish, eh? Well, let’s learn as we go!</p>
<p>In your <code>swrAPI</code> file, create another mutation function. This function takes in two parameters: the new task you want to add and the list of already existing tasks.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> addTaskMutation = <span class="hljs-keyword">async</span> (newTask, tasks) =&gt; {
  };
</code></pre>
<p>Then it uses your already existing <code>create</code> function to attempt to create a new task. After this, you store the result and return that result in a new array, together with the already existing tasks.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> addTaskMutation = <span class="hljs-keyword">async</span> (newTask, tasks) =&gt; {
  <span class="hljs-keyword">const</span> addedTask = <span class="hljs-keyword">await</span> addSingleTask(newTask);
  <span class="hljs-keyword">return</span> [...tasks, addedTask].sort(
    <span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(b.createdAt) - <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(a.createdAt)
  );
};
</code></pre>
<p>As you would suspect, this function does the same as the previous <code>create</code> function we wrote, but it’s what comes next that we’re after.</p>
<p>Next, create an <code>options</code> function which is responsible for treating the async operation as a synchronous operation and immediately yields a response.</p>
<p>This function also takes some parameters such as:</p>
<ul>
<li><strong><code>optimisticData</code></strong>: which is the new data you want to display immediately.</li>
<li><strong><code>rollbackOnError</code></strong>: which sets the state to the previous one if the request fails.</li>
<li><strong><code>populateCache</code></strong>: which immediately sets this optimistic data in our UI state.</li>
<li><strong><code>revalidate</code></strong>: which lets us enable or disable another fetch after this function runs.</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> addTaskOptions = <span class="hljs-function">(<span class="hljs-params">newTask, tasks</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">optimisticData</span>: [...tasks, newTask].sort(
      <span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(b.createdAt) - <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(a.createdAt)
    ),
    <span class="hljs-attr">rollbackOnError</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">populateCache</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">revalidate</span>: <span class="hljs-literal">false</span>,
  };
};
</code></pre>
<p>To use this optimistic UI method with a <code>create</code> operation, import both functions into your <code>TaskForm</code>. Both functions need to be wrapped in the <code>mutate</code> function since they’re both attempting to mutate the data.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> {
  addTaskMutation <span class="hljs-keyword">as</span> addSingleTask,
  addTaskOptions,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"./services/swrAPI"</span>;

<span class="hljs-keyword">import</span> toast <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hot-toast"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Taskform</span>(<span class="hljs-params">{ mutate, tasks }</span>) </span>{
  <span class="hljs-keyword">const</span> [title, setTitle] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [description, setDescription] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [assignedTo, setAssignedTo] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> addTaskMutation = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">const</span> createdAt = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString();
    <span class="hljs-keyword">try</span> {

      <span class="hljs-keyword">await</span> mutate(
        addSingleTask(
          {
            title,
            description,
            assignedTo,
            <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span>,
            createdAt,
          },
          tasks
        ),
        addTaskOptions(
          {
            title,
            description,
            assignedTo,
            <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span>,
            createdAt,
          },
          tasks
        )
      ); 
     toast.success(<span class="hljs-string">"Task added succesfully."</span>);

    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to add the new task."</span>);
    }
  };
</code></pre>
<p><strong>Note</strong>: The tasks array is passed into the <code>TaskForm</code> via props for this functionality to work.</p>
<p>To see instances where there might be an error, give your functions a 50/50 chance of success or failure, by adding a random condition.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> addSingleTask = <span class="hljs-keyword">async</span> ({
  title,
  description,
  completed,
  assignedTo,
  createdAt,
}) =&gt; {
  <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() &lt; <span class="hljs-number">0.5</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to add new task"</span>);
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> tasksApi.post(tasksUrlEndpoint, {
    title,
    description,
    completed,
    assignedTo,
    createdAt,
  });
  <span class="hljs-keyword">return</span> response.data;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateSingleTask = <span class="hljs-keyword">async</span> (task) =&gt; {
  <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() &lt; <span class="hljs-number">0.5</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to update task"</span>);
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> tasksApi.patch(<span class="hljs-string">`<span class="hljs-subst">${tasksUrlEndpoint}</span>/<span class="hljs-subst">${task.id}</span>`</span>, task);
  <span class="hljs-keyword">return</span> response.data;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteSingleTask = <span class="hljs-keyword">async</span> ({ id }) =&gt; {
  <span class="hljs-keyword">await</span> delay();
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() &lt; <span class="hljs-number">0.5</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to update task"</span>);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> tasksApi.delete(<span class="hljs-string">`<span class="hljs-subst">${tasksUrlEndpoint}</span>/<span class="hljs-subst">${id}</span>`</span>, id);
};
</code></pre>
<p>Testing your <code>create</code> endpoint now gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/6-Optimistic-UI-with-Create-operation.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Optimistic UI with Create operation</em></p>
<p>And voilà! Your app is officially optimistic. It attempts to immediately add the new task to the list even if it fails and gracefully rolls back in the case of an error.</p>
<p>This works similarly for the update operation – starting with the updated <code>update</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateTaskMutation = <span class="hljs-keyword">async</span> (updatedTask, tasks) =&gt; {
  <span class="hljs-keyword">const</span> updatedTaskResponse = <span class="hljs-keyword">await</span> updateSingleTask(updatedTask);
  <span class="hljs-keyword">return</span> tasks.map(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span>
    task.id === updatedTask.id ? updatedTaskResponse : task
  );
};
</code></pre>
<p>Then its corresponding <code>options</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateTaskOptions = <span class="hljs-function">(<span class="hljs-params">updatedTask, tasks</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">optimisticData</span>: tasks.map(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span>
      task.id === updatedTask.id ? updatedTask : task
    ),
    <span class="hljs-attr">rollbackOnError</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">populateCache</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">revalidate</span>: <span class="hljs-literal">false</span>,
  };
};
</code></pre>
<p>To test this out, import the new <code>updateSingleTask</code> and <code>updateOptions</code> function in your <code>TaskConatiner</code>, and update the handler function.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> updateTaskMutation = <span class="hljs-keyword">async</span> (updatedTask) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> mutate(
        updateSingleTask(updatedTask, tasks),
        updateTaskOptions(updatedTask, tasks)
      );
      toast.success(<span class="hljs-string">"Successfully updated task"</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to update the task."</span>);
    }
  };
</code></pre>
<p>Which gives the following result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/7-Optimistic-UI-with-Update-operation---fix-gif.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Optimistic UI with Update operation</em></p>
<p>And finally for the delete action:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Function for deleting a task</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteTaskMutation = <span class="hljs-keyword">async</span> (taskToDelete, tasks) =&gt; {
  <span class="hljs-keyword">await</span> deleteSingleTask(taskToDelete);
  <span class="hljs-keyword">return</span> tasks.filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> task.id !== taskToDelete.id);
};

<span class="hljs-comment">// Options for deleting a task</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteTaskOptions = <span class="hljs-function">(<span class="hljs-params">taskToDelete, tasks</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">optimisticData</span>: tasks.filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> task.id !== taskToDelete.id),
    <span class="hljs-attr">rollbackOnError</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">populateCache</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">revalidate</span>: <span class="hljs-literal">false</span>,
  };
};
</code></pre>
<p>Which can be used in the <code>TaskContainer</code> delete handler like so:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> deleteTaskMutation = <span class="hljs-keyword">async</span> ({ id }) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> mutate(
        deleteSingleTask({ id }, tasks),
        deleteTaskOptions({ id }, tasks)
      );
      toast.success(<span class="hljs-string">"Successfully deleted task"</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      toast.error(<span class="hljs-string">"Failed to delete the task."</span>);
    }
  };
</code></pre>
<p>Which gives this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/8-Optimistic-UI-with-Delete-operation.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Optimistic UI with Delete operation</em></p>
<h2 id="heading-drawbacks-of-optimistic-ui">Drawbacks of Optimistic UI</h2>
<p>Now you must be thinking, if optimistic UI is so great, why not use it everywhere?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/lighter-hairspray.gif" alt="Image" width="600" height="400" loading="lazy">
<em>lighter hairspray gif</em></p>
<p>Well, like everything, that action turns chaotic without moderation. Here are some reasons why you should use optimistic UI in moderation.</p>
<ol>
<li><strong>Excessive Updates</strong>: Optimistic UI might get a bit carried away with updates, especially if your app's moving faster than your internet connection. Too many updates can slow things down, so it's essential to strike a balance.</li>
<li><strong>Exposing server-side logic</strong>: While offloading all the smarts to your app (like generating unique IDs or checking if that username is already taken) is tempting, remember that your server plays a crucial role too. Letting the front end of your app handle everything can lead to security risks and messy code, so be mindful of where you're putting your logic.</li>
<li><strong>Managing Mishaps:</strong> While Optimistic UI typically expects smooth sailing, life has a way of throwing curveballs. From a sudden internet hiccup to the server taking an unexpected coffee break, glitches can be quite a headache to manage gracefully.</li>
<li><strong>Avoid Rapid Changes</strong>: Imagine adding an item to your shopping cart, and then deciding to remove it before the "add" request even reaches the server. It's like changing your mind at the checkout counter – a bit confusing, right? Rapid changes like these can leave your app disoriented, so it's best to proceed cautiously.</li>
</ol>
<h2 id="heading-ideal-use-cases-for-optimistic-ui">Ideal Use Cases for Optimistic UI</h2>
<p>While optimistic UI may not be the holy grail of state management you were hoping to discover, it does have some good use cases such as:</p>
<ol>
<li><strong>Instant Messaging Apps</strong>: Almost all instant messaging platforms currently use this pattern. Your messages appear instantly in the chat window, even before they're confirmed by the server. This creates a seamless and responsive chatting experience, keeping the conversation flowing effortlessly.</li>
<li><strong>Collaborative Editing Tools</strong>: Whether you're working on a document with colleagues or collaborating on a project with teammates, Optimistic UI ensures that changes are reflected in real time. As you type, edit, or make updates, your changes are immediately visible to others, fostering collaboration and productivity.</li>
<li><strong>Social Media Feeds</strong>: Scroll through your social media feed, and you'll see posts, likes, and comments popping up like magic. Optimistic UI ensures that interactions, such as liking a post or leaving a comment, are reflected instantly, providing a more engaging browsing experience.</li>
<li><strong>E-commerce Websites</strong>: Adding items to your shopping cart, updating quantities, and checking out should feel like a breeze. Optimistic UI speeds up the shopping process by immediately updating your cart and displaying feedback, such as item availability or pricing changes, without delay.</li>
</ol>
<p>For convenience, here are some resources you may need:</p>
<ul>
<li><a target="_blank" href="https://github.com/Daiveedjay/Optimistic-UI-with-SWR/tree/starter">Starter Code</a></li>
<li><a target="_blank" href="https://github.com/Daiveedjay/Optimistic-UI-with-SWR/tree/final">Finished Code</a></li>
</ul>
<p>I'd like to acknowledge <a target="_blank" href="https://x.com/yesdavidgray?t=DlFXltzVgL_iokc_225Fgw&amp;s=08">Dave Gray</a>. It was <a target="_blank" href="https://www.youtube.com/watch?v=6gb6oyO1Tyg">his YouTube video</a> that inspired this article. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As we wrap up our dive into Optimistic UI, it's clear that this technique can be a user experience game-changer. It's the rush of your message popping up instantly or your shopping cart updating in real-time.</p>
<p>Optimistic UI is about speed as well as how it makes users feel – connected, empowered, and delighted. So, next time you click and see the magic unfold, remember: it's not just code...it's the pulse of user happiness (not a Coca-Cola ad 😂). Keep that magic alive in your apps!</p>
<p>Happy coding, and have an optimistic day!</p>
<p><strong>Like my articles?</strong></p>
<p>Feel free to <a target="_blank" href="https://www.buymeacoffee.com/JajaDavid">buy me a coffee here</a>, to keep my brain chugging and provide more articles like this.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/coffee-tom.gif" alt="coffee-tom" width="600" height="400" loading="lazy">
<em>Coffee Tom</em></p>
<h3 id="heading-contact-information"><strong>Contact Information</strong></h3>
<p>Want to connect or contact me? Feel free to hit me up on the following:</p>
<ul>
<li>Twitter / X: <a target="_blank" href="https://twitter.com/JajaDavid8">@jajadavid8</a></li>
<li>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/david-jaja-8084251b4/">David Jaja</a></li>
<li>Email: Jajadavidjid@gmail.com</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Tables Using the React-Data-Table-Component Library in React & TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, I'll teach you how to use the react-data-table-component library in your React projects. You'll learn how to render a table that has features such as pagination, searching/filtering, and sorting.  I'll walk you through each step, fr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-tables-using-the-react-datatable-component-library/</link>
                <guid isPermaLink="false">66ba5222256e9dbeab31aa92</guid>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ User Interface ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Losalini Rokocakau ]]>
                </dc:creator>
                <pubDate>Wed, 27 Mar 2024 15:40:38 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/cover-image-21.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, I'll teach you how to use the <em>react-data-table-component</em> library in your React projects. You'll learn how to render a table that has features such as pagination, searching/filtering, and sorting. </p>
<p>I'll walk you through each step, from setting up a React and TypeScript project with Vite to using the <em>react-data-table-component</em> library to render a table.</p>
<p>To follow along with this tutorial, there are a few prerequisites:</p>
<ol>
<li>A basic understanding of React and TypeScript</li>
<li>Basic knowledge of Bootstrap, which we'll use for styling</li>
<li>For Windows OS users, know how to use the PowerShell terminal (as you'll need an interactive terminal to create the project with Vite)</li>
<li>Node v20.11.1 installed</li>
<li>npm v10.2.4 installed</li>
<li>A code editor such as Visual Studio Code (VS Code) or Atom</li>
</ol>
<p>Let's start creating the project!</p>
<h3 id="heading-what-will-we-build">What will we build</h3>
<p>By the end of this tutorial, you will have built a table that displays a person's ID, name, height, and eye color.</p>
<p>The table will also have a search bar where users can search for a person based on the value of any of the four properties mentioned above.</p>
<p>Each row in the table will be selectable and each column will be sortable when the column header is clicked by a user.</p>
<h2 id="heading-1-create-a-react-and-typescript-project">1. Create a React and TypeScript Project</h2>
<p>In the command line, create the project with the command below:</p>
<pre><code>npm create vite@latest
</code></pre><p>Name the project <code>react-data-table-tutorial</code> .</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/fig-1-0-1.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 1.0</strong> Creating the React &amp; TypeScript project with Vite on PowerShell command line. Naming the project react-data-table-tutorial.</em></p>
<p>Navigate to React with the up and down keys to select React. Choose React as the library you're going to use.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/fig-1-1-1.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 1.1</strong> Selecting React as our framework by navigating to it on the list with up &amp; down arrow keys on the keyboard.</em></p>
<p>Similar to the step above, navigate to TypeScript and choose it as the language to use.</p>
<p>The next step is to change directories into the project's folder. Once you've done that, open the project in your code editor as seen in Figure 1.2 below. I'll be using the VS Code editor in this tutorial.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/fig-1-2-2.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 1.2</strong> Changing the folder into the react-data-table-tutorial directory and opening the contents of the folder into a code editor.</em></p>
<h2 id="heading-2-remove-the-default-code-set-by-react">2. Remove the Default Code Set by React</h2>
<p>This step is just to remove any default code and stylings in the files found in the <em>src</em> folder. </p>
<p>First, remove all default styling in the <em>App.css</em> file and replace it with the stylings below.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background-color</span>: white;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">160px</span> <span class="hljs-number">500px</span>;
}
</code></pre>
<p>Then remove the following from the <em>App.tsx</em> file:</p>
<ul>
<li>the imported statements of the <code>useState</code> hook, <code>reactLogo</code>, and the <code>viteLogo</code><em>.</em></li>
<li>The destructured array and <code>useState</code> hook for the count variable and <code>setCount</code> function.</li>
<li>The returned JSX wrapped within the fragment of the <code>App</code> component.</li>
</ul>
<p>The <code>App</code> component should look like the code-block below after all of those changes:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h2 id="heading-3-install-the-libraries-well-need">3. Install the Libraries We'll Need</h2>
<p>In the project, you will need to install a few libraries:</p>
<ol>
<li><em>styled-components</em> v3.23+</li>
<li><em>react-data-table-component</em> v16.8.0+</li>
<li><em>Bootstrap</em> v5.3.3</li>
</ol>
<p>You will need to install the <em>styled-components</em> library to use with the <em>react-data-table-component</em> library.</p>
<p>In the command line, install all of these by using the commands below:</p>
<pre><code>npm install styled-components
</code></pre><pre><code>npm install react-data-table-components
</code></pre><pre><code>npm install bootstrap@<span class="hljs-number">5.3</span><span class="hljs-number">.3</span>
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2024/03/fig-3-0.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 3.0</strong> Installing the libraries on Visual Studio Code's built-in terminal.</em></p>
<p>The installed libraries are listed under dependencies in the <em>package.json</em> file.</p>
<pre><code class="lang-json">{
   <span class="hljs-comment">// rest of the code in the file</span>
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"boostrap"</span>: <span class="hljs-string">"^5.3.3"</span>,
        <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
        <span class="hljs-attr">"react-data-table-component"</span>: <span class="hljs-string">"^7.6.2"</span>,
        <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>,
        <span class="hljs-attr">"styled-components"</span>: <span class="hljs-string">"^6.1.8"</span>,
    }
    <span class="hljs-comment">// rest of the code in the file</span>
}
</code></pre>
<h2 id="heading-4-import-bootstrap-in-the-app-component">4. Import Bootstrap in the App Component</h2>
<p>In the <em>App.tsx</em> file, import the Bootstrap library at the top of the file.</p>
<p>This will allow us to use Bootstrap stylings throughout the project.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/js/bootstrap.bundle.min.js"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>)</span>{
   <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h2 id="heading-5-create-a-table-component">5. Create a Table Component</h2>
<p>In the <em>src</em> folder, create a sub-directory and name it <em>components</em>. By convention, this is to hold all of the components of the project.</p>
<p>In the <em>components</em> folder, create a file called <em>Table.tsx</em>. This is for our <code>Table</code> component.</p>
<p>Create a function component called <code>Table</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/fig-4-0.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 5.0</strong> Create the Table component in the components sub-directory within the src folder.</em></p>
<h2 id="heading-6-import-the-react-data-table-component-library-to-start-using-it">6. Import the react-data-table-component Library to Start Using it</h2>
<p>Import the <em>react-data-table-component</em> library into the <code>Table</code> component.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> DataTable <span class="hljs-keyword">from</span> <span class="hljs-string">"react-data-table-component"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Table</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Table;
</code></pre>
<h2 id="heading-7-create-the-table-in-the-table-component">7. Create the Table in the Table Component</h2>
<p>Create a <code>&lt;div&gt;</code> container within the fragment and add Bootstrap styling of <code>container</code> and <code>my-5</code> to place the table in the center of the page.</p>
<p>Add the <code>DataTable</code> component as the child of the <code>&lt;div&gt;</code> container.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> DataTable <span class="hljs-keyword">from</span> <span class="hljs-string">'react-data-table-component'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Table</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container my-5"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">DataTable</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/&gt;</span></span>
    );
}
</code></pre>
<p>Pass the columns and rows of the table as props to the <code>DataTable</code> component.</p>
<p>The column headers of the table are objects that will be stored in an array. The rows of the table will also be stored in a similar way. The array of objects for both will have these structures:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> columns = [
    {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"ID"</span>,
        <span class="hljs-attr">selector</span>: <span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> row.id
    },
    {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Full Name"</span>,
        <span class="hljs-attr">selector</span>: <span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> row.fullName
    },
    {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Height"</span>,
        <span class="hljs-attr">selector</span>: <span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> row.height
    },
    {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Weight"</span>,
        <span class="hljs-attr">selector</span>: <span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> row.weight
    },
];
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> rows = [
    {
        <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">fullName</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">height</span>: <span class="hljs-string">"1.75m"</span>,
        <span class="hljs-attr">weight</span>: <span class="hljs-string">"89kg"</span>,
    },
    {
        <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
        <span class="hljs-attr">fullName</span>: <span class="hljs-string">"Jane Doe"</span>,
        <span class="hljs-attr">height</span>: <span class="hljs-string">"1.64m"</span>,
        <span class="hljs-attr">weight</span>: <span class="hljs-string">"55kg"</span>,
    },
    {
        <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>,
        <span class="hljs-attr">fullName</span>: <span class="hljs-string">"Sheera Maine"</span>,
        <span class="hljs-attr">height</span>: <span class="hljs-string">"1.69m"</span>,
        <span class="hljs-attr">weight</span>: <span class="hljs-string">"74kg"</span>,
    },
];
</code></pre>
<p>These constants are then passed into the <code>DataTable</code> component as such:</p>
<pre><code class="lang-jsx">&lt;DataTable columns={columns} data={rows} /&gt;
</code></pre>
<p>Create a <code>columns</code> constant that is an array of objects that has four columns headers for <code>personID</code>, <code>fullName</code>, <code>height</code>, and <code>eyeColor</code>.</p>
<p>Create a <code>rows</code> constant that is an array of objects that has 15 objects which is equivalent to data for 15 people.</p>
<p>Pass the two constants into their respective props in the <code>DataTable</code> component as seen in the next code-block.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Table</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> columns = [
        {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"ID"</span>,
            <span class="hljs-attr">selector</span>: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.personID,
        },
        {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"Full Name"</span>,
            <span class="hljs-attr">selector</span>: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.fullName,
        },
        {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"Height"</span>,
            <span class="hljs-attr">selector</span>: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.height,
        },
        {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"eyeColor"</span>,
            <span class="hljs-attr">selector</span>: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.eyeColor,
        },
    ];

    <span class="hljs-keyword">const</span> rows = [
        {
           <span class="hljs-attr">personID</span>: <span class="hljs-number">1</span>,
           <span class="hljs-attr">fullName</span>: <span class="hljs-string">"Kate Shein"</span>,
           <span class="hljs-attr">height</span>: <span class="hljs-string">"1.79m"</span>,
           <span class="hljs-attr">eyeColor</span>: <span class="hljs-string">"blue"</span>,
        },
        <span class="hljs-comment">//....remaining objects for the 2nd to 14th person</span>
        {
           <span class="hljs-attr">personID</span>: <span class="hljs-number">15</span>,
           <span class="hljs-attr">fullName</span>: <span class="hljs-string">"Isabella Thompson"</span>,
           <span class="hljs-attr">height</span>: <span class="hljs-string">"1.79m"</span>,
           <span class="hljs-attr">eyeColor</span>: <span class="hljs-string">"blue"</span>,
        },
    ];
}
</code></pre>
<p>You will also add the <code>fixedHeader</code> prop to the <code>DataTable</code> component to keep the column header fixed when a user scrolls down the table that has more than 10 records.</p>
<p>Give the table a title by passing the <code>title</code> prop to the component and its value is whatever you would like to call your table.</p>
<pre><code class="lang-javascript">&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"container"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DataTable</span> 
        <span class="hljs-attr">columns</span>=<span class="hljs-string">{columns}</span> 
        <span class="hljs-attr">data</span>=<span class="hljs-string">{rows}</span> 
        <span class="hljs-attr">fixedHeader</span>
        <span class="hljs-attr">title</span>=<span class="hljs-string">"React-Data-Table-Component Tutorial."</span>
     /&gt;</span></span>
&lt;/div&gt;
</code></pre>
<p>Back in the <code>App</code> component, import the <code>Table</code> component and place it within the fragment.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/js/bootstrap.bundle.min.js"</span>;
<span class="hljs-keyword">import</span> Table <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Table"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
           <span class="hljs-tag">&lt;<span class="hljs-name">Table</span> /&gt;</span>
        <span class="hljs-tag">&lt;/&gt;</span></span>
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h2 id="heading-8-add-pagination-and-sorting-and-make-each-row-selectable">8. Add Pagination and Sorting, and Make Each Row Selectable</h2>
<h3 id="heading-pagination-and-selectablerows">Pagination and <code>selectableRows</code></h3>
<p>Add the <code>pagination</code> and <code>selectableRows</code> props to the <code>DataTable</code> component.</p>
<p>By default, the first page has the first 10 records. If there needs to be API calls from the server side for custom pagination, then you can use the <code>paginationServer</code> property along with the <code>paginationTotalRows</code><em>,</em> <code>onChangeRowsPerPage</code> and <code>onChangePage</code> properties which work in conjunction with a few other things.</p>
<p>But for now, let's stick with the <code>pagination</code> property.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">//...rest of the code in the function component</span>
&lt;div className=<span class="hljs-string">"container d-flex justify-content-center my-5"</span>&gt;
   <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DataTable</span>
        <span class="hljs-attr">columns</span>=<span class="hljs-string">{columns}</span>
        <span class="hljs-attr">data</span>=<span class="hljs-string">{rows}</span>
        <span class="hljs-attr">fixedHeader</span>
        <span class="hljs-attr">title</span>=<span class="hljs-string">"React-Data-Table-Component Tutorial"</span>
        <span class="hljs-attr">pagination</span>
        <span class="hljs-attr">selectableRows</span>
    /&gt;</span></span>
&lt;/div&gt;
<span class="hljs-comment">//...rest of the code in the function component</span>
</code></pre>
<h3 id="heading-sorting">Sorting</h3>
<p>In the <code>DataTable</code> component, add the <code>sortable</code> property to each object in the <code>columns</code> constant. Give it a boolean value of <code>true</code> so that sorting is applied to each column when a user clicks on the column headers.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//...rest of the code in the function component</span>
<span class="hljs-keyword">const</span> columns = [
    {
        name: <span class="hljs-string">'ID'</span>,
        selector: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.personID,
        sortable: <span class="hljs-literal">true</span>,
    },
    {
        name: <span class="hljs-string">'Full Name'</span>,
        selector: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.fullName,
        sortable: <span class="hljs-literal">true</span>,
    },
    {
        name: <span class="hljs-string">'Height'</span>,
        selector: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.height,
        sortable: <span class="hljs-literal">true</span>,
    },
    {
        name: <span class="hljs-string">'Eye Color'</span>,
        selector: <span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> row.eyeColor,
        sortable: <span class="hljs-literal">true</span>,
    },
];
<span class="hljs-comment">//...rest of the code in the function component</span>
</code></pre>
<h2 id="heading-9-add-searching-and-filtering">9. Add Searching and Filtering</h2>
<p>Add a <code>&lt;div&gt;</code> container above the <code>DataTable</code> component in the returned JSX. Also add the bootstrap <code>input-group</code> class to the <code>&lt;div&gt;</code>.</p>
<p>The child element of this new container will be the <code>&lt;input /&gt;</code> of type <code>search</code> and we'll use Bootstraps' stylings for it. Use the code below:</p>
<pre><code class="lang-jsx">&lt;input
    type=<span class="hljs-string">"search"</span>
    className=<span class="hljs-string">"form-control-sm border ps-3"</span>
    placeholder=<span class="hljs-string">"Search"</span>
/&gt;
</code></pre>
<p>An optional step is to add a search icon or use <a target="_blank" href="https://getbootstrap.com/docs/5.3/forms/input-group/">Bootstraps' default styling</a> for a search bar. However, we'll leave it out for now so we can focus solely on the searching functionality.</p>
<p>Now, let's import the <code>useState</code> hook into the <em>Table.tsx</em> file.</p>
<p>Use the hook and pass in the <code>rows</code> constant as the default value of our state variable. Within the destructured array will be our state variable called <code>data</code> and the setter function called <code>setData</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [data, setData] = useState(rows);
</code></pre>
<p>Create a function called <code>handleSearch</code> that will be called when the <code>onChange</code> event listener is used on the search bar. </p>
<p>Pass in the event object, <code>e</code>, as its argument. Using type annotation, set the type of the event object to <code>React.ChangeEvent&lt;HTMLInputElement&gt;</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSearch = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
   <span class="hljs-comment">// the rest of the code will be defined in here</span>
};
</code></pre>
<p>Declare 5 variables of Boolean type as seen below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSearch = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
   <span class="hljs-keyword">let</span> searchValue: <span class="hljs-built_in">Boolean</span>;
   <span class="hljs-keyword">let</span> personIDValue: <span class="hljs-built_in">Boolean</span>;
   <span class="hljs-keyword">let</span> fullNameValue: <span class="hljs-built_in">Boolean</span>;
   <span class="hljs-keyword">let</span> heightValue: <span class="hljs-built_in">Boolean</span>;
   <span class="hljs-keyword">let</span> eyeColorValue: <span class="hljs-built_in">Boolean</span>;
};
</code></pre>
<p>Declare a new local constant called <code>newRows</code><em>.</em> In this constant, filter out and return the rows/data in the <em>rows</em> constant where the search input's value is the same as the value of the rows' <code>fullName</code> or <code>height</code> or <code>eyeColor</code> property.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> newRows = rows.filter(<span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> {
      personIDValue = row.personID
        .toString()
        .toLowerCase()
        .includes(e.target.value.toLowerCase());
      fullNameValue = row.fullName
        .toLowerCase()
        .includes(e.target.value.toLowerCase());
      heightValue = row.height
        .toLowerCase()
        .includes(e.target.value.toLowerCase());
      eyeColorValue = row.eyeColor
        .toLowerCase()
        .includes(e.target.value.toLowerCase());

      <span class="hljs-keyword">if</span> (personIDValue) {
        searchValue = personIDValue;
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (fullNameValue) {
        searchValue = fullNameValue;
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (heightValue) {
        searchValue = heightValue;
      } <span class="hljs-keyword">else</span> {
        searchValue = eyeColorValue;
      }

      <span class="hljs-keyword">return</span> searchValue;
});
</code></pre>
<p>Pass <code>newRows</code> into the setter function, <code>setData</code><em>.</em></p>
<pre><code class="lang-typescript">setData(newRows);
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// definition of the columns constant</span>

<span class="hljs-comment">// definition of the rows constant</span>

<span class="hljs-keyword">const</span> [data, setData] = useState(rows);

<span class="hljs-comment">// Handle Search</span>
<span class="hljs-keyword">const</span> handleSearch = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> searchValue: <span class="hljs-built_in">Boolean</span>;
    <span class="hljs-keyword">let</span> personIDValue: <span class="hljs-built_in">Boolean</span>;
    <span class="hljs-keyword">let</span> fullNameValue: <span class="hljs-built_in">Boolean</span>;
    <span class="hljs-keyword">let</span> heightValue: <span class="hljs-built_in">Boolean</span>;
    <span class="hljs-keyword">let</span> eyeColorValue: <span class="hljs-built_in">Boolean</span>;

    <span class="hljs-keyword">const</span> newRows = rows.filter(<span class="hljs-function">(<span class="hljs-params">row</span>) =&gt;</span> {
      personIDValue = row.personID
        .toString()
        .toLowerCase()
        .includes(e.target.value.toLowerCase());
      fullNameValue = row.fullName
        .toLowerCase()
        .includes(e.target.value.toLowerCase());
      heightValue = row.height
        .toLowerCase()
        .includes(e.target.value.toLowerCase());
      eyeColorValue = row.eyeColor
        .toLowerCase()
        .includes(e.target.value.toLowerCase());

      <span class="hljs-keyword">if</span> (personIDValue) {
        searchValue = personIDValue;
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (fullNameValue) {
        searchValue = fullNameValue;
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (heightValue) {
        searchValue = heightValue;
      } <span class="hljs-keyword">else</span> {
        searchValue = eyeColorValue;
      }

      <span class="hljs-keyword">return</span> searchValue;
    });

    setData(newRows);
};
</code></pre>
<p>Next step is to pass (not call the function) the <code>handleSearch</code> function into the <code>onChange</code> event listener. </p>
<pre><code class="lang-jsx">&lt;input
   type=<span class="hljs-string">"search"</span>
   className=<span class="hljs-string">"form-control-sm border ps-3"</span>
   placeholder=<span class="hljs-string">"Search"</span>
   onChange={handleSearch}
/&gt;
</code></pre>
<p>The value of the data property in the <code>DataTable</code> component will now be the state variable, <code>data</code> instead of <code>rows</code> that was initially passed in.</p>
<pre><code class="lang-jsx">&lt;DataTable
    columns={columns}
    data={data}
    fixedHeader
    title=<span class="hljs-string">"React-Data-Table-Component Tutorial."</span>
    pagination
    selectableRows
/&gt;
</code></pre>
<h2 id="heading-10-view-the-table">10. View the Table</h2>
<p>Run the project in the command line and view the table in a browser:</p>
<p><code>command line
npm run dev</code></p>
<p>The table should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/final-result-table.png" alt="Image" width="600" height="400" loading="lazy">
<em><strong>Figure 11.0</strong> Preview of the resulting table created.</em></p>
<p>You can read through the <a target="_blank" href="https://react-data-table-component.netlify.app/?path=/docs/getting-started-intro--docs">documentation</a> for <em>react-data-table-component</em> as it has more in-depth coverage of the library's usage that goes beyond the scope of this article. </p>
<p>Don't fret if there's a difference in your table from the expected result. The source code in <a target="_blank" href="https://github.com/chelmerrox/react-data-table-tutorial">this repository</a> will guide you.</p>
<p>Happy coding and may your code run smoothly!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Request Location Permissions in Jetpack Compose ]]>
                </title>
                <description>
                    <![CDATA[ Getting a user’s location can be a bit of hassle. Over the years, the permissions required to do this and the logic associated with it have changed quite drastically.  If your application depends on getting the user’s location, you want to ensure tha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/requesting-location-permissions-in-jetpack-compose/</link>
                <guid isPermaLink="false">66ba5034158e6c6a8cb8c7a3</guid>
                
                    <category>
                        <![CDATA[ Jetpack Compose ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tomer ]]>
                </dc:creator>
                <pubDate>Thu, 05 Oct 2023 19:28:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/andrew-stutesman-l68Z6eF2peA-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Getting a user’s location can be a bit of hassle. Over the years, the permissions required to do this and the logic associated with it have changed quite drastically. </p>
<p>If your application depends on getting the user’s location, you want to ensure that the user has a good experience when the application requests this info. So it's crucial to handle all the edge cases and allow the user to select the option the they're most comfortable with.</p>
<p>With Jetpack Compose, the logic associated with getting the user’s location has changed a bit, and it’s important to know the in’s and out’s of how it can be done.</p>
<p>As with most things, there is a library you can use to handle getting permissions. It’s from Accompanist (read Google) and you can find it <a target="_blank" href="https://google.github.io/accompanist/permissions/#:~:text=A%20library%20which%20provides%20Android%20runtime%20permissions%20support%20for%20Jetpack%20Compose.&amp;text=The%20permission%20APIs%20are%20currently,marked%20with%20the%20%40ExperimentalPermissionsApi%20annotation.">here</a>. <strong>But you are here to learn how to do things yourself,</strong> right? So read on. 🕵️‍♀️</p>
<p>Before we get into the code and the logic, it is important to understand that requesting a permission from a user is a path that can have many decision points. As such, it is best described by having states that represent the current status of the permission (approved/rejected/denied) and the current status of the operating system. </p>
<p>The diagram below illustrates this flow:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/location.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Later on in this article, you will see how we will represent these states in variables in our code.</p>
<h2 id="heading-save-our-souls-sos">Save Our Souls (S.O.S)</h2>
<p>Before we get to the logic of asking for a permission, we’ll take care of the boilerplate around it. As always, add the necessary permissions to your AndroidManifest.xml file:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.ACCESS_COARSE_LOCATION"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.ACCESS_FINE_LOCATION"</span> /&gt;</span>
</code></pre>
<p>Then, we’ll create a composable screen where we ask for the necessary location permissions. The first step in this screen is to check whether the user has previously granted the required permissions. You can do this with something that is not new to Jetpack Compose – using the checkSelfPermission method:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> locationPermissionsAlreadyGranted = ContextCompat.checkSelfPermission(
            <span class="hljs-keyword">this</span>,
            Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
</code></pre>
<p>If the permission is not granted, we have to request it. We will use the <a target="_blank" href="https://developer.android.com/reference/kotlin/androidx/activity/compose/package-summary#rememberlauncherforactivityresult">rememberLauncherForActivityResult</a> object to do this. This allows us in Jetpack Compose to get a result from an Activity.</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> locationPermissions = arrayOf(
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.ACCESS_COARSE_LOCATION)

<span class="hljs-keyword">val</span> locationPermissionLauncher = rememberLauncherForActivityResult(
                contract = ActivityResultContracts.RequestMultiplePermissions(),
                onResult = { permissions -&gt;
                    <span class="hljs-keyword">val</span> permissionsGranted = permissions.values.reduce { acc, isPermissionGranted -&gt;
                        acc &amp;&amp; isPermissionGranted
                    }

                    <span class="hljs-keyword">if</span> (!permissionsGranted) {
                       <span class="hljs-comment">//Logic when the permissions were not granted by the user</span>
                    }
                })
</code></pre>
<p>The arguments that we need to pass to <code>rememberLauncherForActivityResult</code> are:</p>
<ol>
<li>An <a target="_blank" href="https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContract">ActivityResultContract</a> – specifies the input of the activity and the output</li>
<li>onResult – a callback when the result is received</li>
</ol>
<p>In the code snippet above, we are using a multiple permissions contract, since we are requesting several location permissions. There is also a contract for requesting just one permission, <strong>ActivityResultContracts.RequestPermission().</strong></p>
<p>This piece of code doesn’t not run immediately as we need to request the permissions. To do that we use the launch method to start the activity:</p>
<pre><code class="lang-kotlin">locationPermissionLauncher.launch(locationPermissions)
</code></pre>
<p>In the case the user gave all or several of the required permissions, we can continue the logic of the application. </p>
<p>But, if the user did not approve any of the permissions, we need to find a way to make the user understand why these permissions are necessary.</p>
<h2 id="heading-explaining-the-rationale">Explaining the Rationale</h2>
<p>In case the user declined the permission, but did not select the “Deny And Don’t Ask Me Again” option, we have a way of giving the user a brief explanation on why they should grant the required permission(s). </p>
<p>To figure out if we should present this rationale, we use the <a target="_blank" href="https://developer.android.com/reference/androidx/core/app/ActivityCompat#shouldShowRequestPermissionRationale(android.app.Activity,java.lang.String)">shouldShowRequestPermissionRationale</a> from the Activity class:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> shouldShowPermissionRationale: <span class="hljs-built_in">Boolean</span> = shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)
</code></pre>
<p>Once we know that we can display this explanation, there are two ways to go about it:</p>
<ol>
<li>We can present it to the user with an AlertDialog</li>
<li>We can use the Snackbar</li>
</ol>
<p>Presenting an alert dialog is pretty straightforward. All we have to do is make sure we describe clearly to the user why it is required to approve this permission:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ShowLocationPermissionRationale</span><span class="hljs-params">()</span></span> {
        AlertDialog(
            onDismissRequest = {
               <span class="hljs-comment">//Logic when dismiss happens</span>
            },
        title = {
            Text(<span class="hljs-string">"Permission Required"</span>)
                },
        text = {
            Text(<span class="hljs-string">"You need to approve this permission in order to..."</span>)
        },
        confirmButton = {
            TextButton(onClick = {
              <span class="hljs-comment">//Logic when user confirms to accept permissions</span>
            }) {
                Text(<span class="hljs-string">"Confirm"</span>)
            }
        },
        dismissButton = {
            TextButton(onClick = {
              <span class="hljs-comment">//Logic when user denies to accept permissions</span>
            }) {
                Text(<span class="hljs-string">"Deny"</span>)
            }
        })
    }
</code></pre>
<p>If we want to present the Snackbar, we need to be aware that we have to use a Scaffold container since that is the only container that supports showing a Snackbar. If we don’t use one, a Snackbar won’t appear. </p>
<p>Below is a snippet that shows you how to do this:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> scope = rememberCoroutineScope()
<span class="hljs-keyword">val</span> snackbarHostState = remember { SnackbarHostState() }

Scaffold(snackbarHost = {
        SnackbarHost(hostState = snackbarHostState)
    }) { contentPadding -&gt;
        <span class="hljs-keyword">if</span> (shouldShowPermissionRationale) {
            LaunchedEffect(key1 = shouldShowPermissionRationale, block = {
                scope.launch {
                    <span class="hljs-keyword">val</span> userAction = snackbarHostState.showSnackbar(
                        message =<span class="hljs-string">"Please authorize location permissions"</span>,
                        actionLabel = <span class="hljs-string">"Approve"</span>,
                        duration = SnackbarDuration.Indefinite,
                        withDismissAction = <span class="hljs-literal">true</span>
                    )
                    <span class="hljs-keyword">when</span> (userAction) {
                        SnackbarResult.ActionPerformed -&gt; {
                            <span class="hljs-comment">//User approved to grant the permission</span>
                            <span class="hljs-comment">//Ask for permissions again</span>
                        }
                        SnackbarResult.Dismissed -&gt; {
                            <span class="hljs-comment">//User dismissed snackbar</span>
                        }
                    }
                }
            })
        }
}
</code></pre>
<p>We allowed the Snackbar itself to be dismissible using the withDismissAction attribute and listened in to the action performed by the user.</p>
<h2 id="heading-lifecycle-observer">Lifecycle Observer</h2>
<p>One thing we have glossed over is the fact that we need to make sure our permission request adheres to the composable lifecycle. This means that once a user chooses their preferences regarding the permission request, we need the UI to adapt accordingly. </p>
<p>If you try and put the code above inside the activity’s onCreate method, you will be surprised with the outcome, since the application will crash with the following exception:</p>
<blockquote>
<p><em>java.lang.IllegalStateException: Launcher has not been initialized</em></p>
</blockquote>
<p>This is because Composable functions are supposed to be side effect free. What is a side effect? According to <a target="_blank" href="https://developer.android.com/jetpack/compose/side-effects">Google’s documentation</a> it is:</p>
<blockquote>
<p>… a change to the state of the app that happens outside the scope of a composable function</p>
</blockquote>
<p>So, in our use case, launching an activity for the permissions is the side effect happening here. </p>
<p>To circumvent this scenario, we need to use one of the side effect options. Since we don’t want to ask the user for permissions without remembering what their choices were previously, we can’t use the general <strong>SideEffect</strong>. And <strong>LaunchedEffect</strong> is used for calling suspend methods inside of a Composable, which is not our use case here. </p>
<p>So we are left with <strong>DisposableEffect</strong>. Reading the <a target="_blank" href="https://developer.android.com/jetpack/compose/side-effects#disposableeffect">documentation</a>, we can see that <a target="_blank" href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#DisposableEffect(kotlin.Any,kotlin.Function1)">DisposableEffect</a> can be combined with Lifecycle events, which is what we are after.</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> lifecycleOwner = LocalLifecycleOwner.current
            DisposableEffect(key1 = lifecycleOwner, effect = {
                <span class="hljs-keyword">val</span> observer = LifecycleEventObserver { _, event -&gt;
                    <span class="hljs-keyword">if</span> (event == Lifecycle.Event.ON_START &amp;&amp; !locationPermissionsAlreadyGranted) {
                        locationPermissionLauncher.launch(locationPermissions)
                       }
                    }
                    lifecycleOwner.lifecycle.addObserver(observer)
                    onDispose {
                        lifecycleOwner.lifecycle.removeObserver(observer)
                    }
                }
            )
</code></pre>
<p>In the code snippet above, we are adding a lifecycle observer that runs only in the case of the onStart lifecycle event. We also combine it with the boolean we have declared at the start of this section, locationPermissionsAlreadyGranted. This is so we won’t show the dialog for asking permissions if they are already granted. </p>
<p>As with all lifecycle observers, we need to remove our observer once the composition ends. We have that logic inside DisposableEffect’s onDispose clause.</p>
<h2 id="heading-location-not-found">Location Not Found</h2>
<p>The last case we need to deal with is when the user chooses the “Deny And Don’t Ask Me Again” option. When this happens, we cannot ask the user to grant the required permissions. </p>
<p>The only way the user can revert their choice is to go to the settings screen of our application and change the permissions there. So we need to direct the user to go there. </p>
<p>To open the settings screen of our application, we need to use an intent with the action of <strong>ACTION_APPLICATION_DETAILS_SETTINGS.</strong></p>
<pre><code class="lang-kotlin">Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts(<span class="hljs-string">"package"</span>, packageName, <span class="hljs-literal">null</span>)).also {
            startActivity(it)
        }
</code></pre>
<p>Taking the logic above, we can add it to our code when we know that the user has chosen to deny the permissions and not be asked again. This happens inside our request for permissions when the user has not granted the permissions and the option to show the rationale is false.</p>
<h2 id="heading-location-confirmed">Location Confirmed</h2>
<p>If we take everything we discussed in this article and put it inside one file, we will get the following code:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> : <span class="hljs-type">ComponentActivity</span></span>() {

    <span class="hljs-meta">@OptIn(ExperimentalMaterial3Api::class)</span>
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(savedInstanceState: <span class="hljs-type">Bundle</span>?)</span></span> {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState)

        setContent {

            <span class="hljs-keyword">var</span> locationPermissionsGranted <span class="hljs-keyword">by</span> remember { mutableStateOf(areLocationPermissionsAlreadyGranted()) }
            <span class="hljs-keyword">var</span> shouldShowPermissionRationale <span class="hljs-keyword">by</span> remember {
                mutableStateOf(
                    shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)
                )
            }

            <span class="hljs-keyword">var</span> shouldDirectUserToApplicationSettings <span class="hljs-keyword">by</span> remember {
                mutableStateOf(<span class="hljs-literal">false</span>)
            }

            <span class="hljs-keyword">var</span> currentPermissionsStatus <span class="hljs-keyword">by</span> remember {
                mutableStateOf(decideCurrentPermissionStatus(locationPermissionsGranted, shouldShowPermissionRationale))
            }

            <span class="hljs-keyword">val</span> locationPermissions = arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            )

            <span class="hljs-keyword">val</span> locationPermissionLauncher = rememberLauncherForActivityResult(
                contract = ActivityResultContracts.RequestMultiplePermissions(),
                onResult = { permissions -&gt;
                    locationPermissionsGranted = permissions.values.reduce { acc, isPermissionGranted -&gt;
                        acc &amp;&amp; isPermissionGranted
                    }

                    <span class="hljs-keyword">if</span> (!locationPermissionsGranted) {
                        shouldShowPermissionRationale =
                            shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)
                    }
                    shouldDirectUserToApplicationSettings = !shouldShowPermissionRationale &amp;&amp; !locationPermissionsGranted
                    currentPermissionsStatus = decideCurrentPermissionStatus(locationPermissionsGranted, shouldShowPermissionRationale)
                })

            <span class="hljs-keyword">val</span> lifecycleOwner = LocalLifecycleOwner.current
            DisposableEffect(key1 = lifecycleOwner, effect = {
                <span class="hljs-keyword">val</span> observer = LifecycleEventObserver { _, event -&gt;
                    <span class="hljs-keyword">if</span> (event == Lifecycle.Event.ON_START &amp;&amp;
                        !locationPermissionsGranted &amp;&amp;
                        !shouldShowPermissionRationale) {
                        locationPermissionLauncher.launch(locationPermissions)
                    }
                }
                lifecycleOwner.lifecycle.addObserver(observer)
                onDispose {
                    lifecycleOwner.lifecycle.removeObserver(observer)
                    }
                }
            )

            <span class="hljs-keyword">val</span> scope = rememberCoroutineScope()
            <span class="hljs-keyword">val</span> snackbarHostState = remember { SnackbarHostState() }

            LocationPermissionsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Scaffold(snackbarHost = {
                        SnackbarHost(hostState = snackbarHostState)
                    }) { contentPadding -&gt;
                        Column(modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally){
                            Text(modifier = Modifier
                                .padding(contentPadding)
                                .fillMaxWidth(),
                                text = <span class="hljs-string">"Location Permissions"</span>,
                                textAlign = TextAlign.Center)
                            Spacer(modifier = Modifier.padding(<span class="hljs-number">20</span>.dp))
                            Text(modifier = Modifier
                                .padding(contentPadding)
                                .fillMaxWidth(),
                                text = <span class="hljs-string">"Current Permission Status: <span class="hljs-variable">$currentPermissionsStatus</span>"</span>,
                                textAlign = TextAlign.Center,
                                fontWeight = FontWeight.Bold
                            )
                        }
                        <span class="hljs-keyword">if</span> (shouldShowPermissionRationale) {
                            LaunchedEffect(<span class="hljs-built_in">Unit</span>) {
                                scope.launch {
                                    <span class="hljs-keyword">val</span> userAction = snackbarHostState.showSnackbar(
                                        message =<span class="hljs-string">"Please authorize location permissions"</span>,
                                        actionLabel = <span class="hljs-string">"Approve"</span>,
                                        duration = SnackbarDuration.Indefinite,
                                        withDismissAction = <span class="hljs-literal">true</span>
                                    )
                                    <span class="hljs-keyword">when</span> (userAction) {
                                        SnackbarResult.ActionPerformed -&gt; {
                                            shouldShowPermissionRationale = <span class="hljs-literal">false</span>
                                            locationPermissionLauncher.launch(locationPermissions)
                                        }
                                        SnackbarResult.Dismissed -&gt; {
                                            shouldShowPermissionRationale = <span class="hljs-literal">false</span>
                                        }
                                    }
                                }
                            }
                        }
                        <span class="hljs-keyword">if</span> (shouldDirectUserToApplicationSettings) {
                            openApplicationSettings()
                        }
                    }
                }
            }
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">areLocationPermissionsAlreadyGranted</span><span class="hljs-params">()</span></span>: <span class="hljs-built_in">Boolean</span> {
        <span class="hljs-keyword">return</span> ContextCompat.checkSelfPermission(
            <span class="hljs-keyword">this</span>,
            Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">openApplicationSettings</span><span class="hljs-params">()</span></span> {
        Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts(<span class="hljs-string">"package"</span>, packageName, <span class="hljs-literal">null</span>)).also {
            startActivity(it)
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">decideCurrentPermissionStatus</span><span class="hljs-params">(locationPermissionsGranted: <span class="hljs-type">Boolean</span>,
                                              shouldShowPermissionRationale: <span class="hljs-type">Boolean</span>)</span></span>: String {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> (locationPermissionsGranted) <span class="hljs-string">"Granted"</span>
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shouldShowPermissionRationale) <span class="hljs-string">"Rejected"</span>
        <span class="hljs-keyword">else</span> <span class="hljs-string">"Denied"</span>
    }
}
</code></pre>
<p>And this is how it looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/location2.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I have put all the logic in one file just for the purpose of this article. It is by no means the most esthetic and correct approach to handling the logic with requesting permissions. </p>
<p>You could easily refactor out the logic variables associated with holding the different state of the request for permissions to a view model class that is attached to this screen.</p>
<p>You can see all of the code described in this article by going to this project:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/TomerPacific/MediumArticles/tree/master/LocationPermissions">https://github.com/TomerPacific/MediumArticles/tree/master/LocationPermissions</a></div>
<p>And if you would like to read more of my articles, you can go view them below:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/TomerPacific/MediumArticles">https://github.com/TomerPacific/MediumArticles</a></div>
<p>I have also used this logic in an application that you can try out <a target="_blank" href="https://play.google.com/store/apps/details?id=com.tomerpacific.scheduler">here</a>.</p>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Tampermonkey to Improve a Website's UI – Example Using freeCodeCamp ]]>
                </title>
                <description>
                    <![CDATA[ What is Tampermonkey? Tampermonkey is a browser extension that lets you add custom scripts to websites, making them work or look the way you want. It's like giving websites a makeover or adding new features.  These scripts are called userscripts and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/customize-website-experience-with-tampermonkey/</link>
                <guid isPermaLink="false">66ba2e3eab41bfc0b9b131e2</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tasnim Ferdous ]]>
                </dc:creator>
                <pubDate>Tue, 22 Aug 2023 22:32:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/cover_image.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-tampermonkey">What is Tampermonkey?</h2>
<p>Tampermonkey is a browser extension that lets you add custom scripts to websites, making them work or look the way you want. It's like giving websites a makeover or adding new features. </p>
<p>These scripts are called userscripts and you can make tampermonkey run those scripts when you visit a particular site.</p>
<h2 id="heading-how-to-use-tampermonkey-to-customize-the-user-experience-of-a-website">How to Use Tampermonkey to Customize the User Experience of a Website</h2>
<p>There are a lot of use cases for Tampermonkey. The most obvious one is adding your own custom styling. You can add custom css for a specific site and change up the appearance as you want. But as you can run a script, you can also manipulate the DOM elements. </p>
<p>I will list out of the things I have done to give you some ideas to what is possible.</p>
<ol>
<li>Increase readability by changing font properties.</li>
<li>Remove ads on sites that don't allow an ad blocker.</li>
<li>Declutter a site so that you can focus on the portion that you're interested in.</li>
<li>Add keyboard shortcuts for repetitive tasks.</li>
<li>Add buttons for custom actions.</li>
<li>Automatically fill out form data.</li>
</ol>
<p>Today you'll get a glimpse of what you can do with Tampermonkey by writing scripts that'll work on the freeCodeCamp /news site. </p>
<p>First we will see how to declutter for a more focused reading experience. Then we will attach copy buttons on the code snippets. And lastly, we'll automatically generate table of contents that you can access with a toggle button. </p>
<p>The source code is available on <a href="https://github.com/renzhamin/freecodecamp-enhancer" target="_blank">GitHub</a>.</p>
<p>It's worth pointing out that any changes your script makes will only be available in your browser. So as long as you don't deal with any sensetive data on that site, you can go as wild as you want. </p>
<p>But be aware that some sites may have some policy in regards to using third party JavaScript and take disciplinary action if you violate that policy.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>As we will be modifying a website, basic knowledge of HTML, CSS, and JavaScript is required to go through this tutorial. Some experience with DOM manipulation would be great as well. </p>
<p>If you can effectively manipulate exisiting DOM elements, that will allow you to write Tampermonkey scripts to make yourself more productive on any website. </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#how-to-install-tampermonkey">How to Install Tampermonkey</a></li>
<li><a class="post-section-overview" href="#how-to-create-a-new-userscript">How to Create a New Userscript</a></li>
<li><a class="post-section-overview" href="#how-to-change-a-buttons-behaviour">How to Change a Button's Behaviour</a></li>
<li><a class="post-section-overview" href="#how-to-implement-reader-view-for-freecodecamp-news-articles">How to Implement Reader View for freeCodeCamp /news Articles</a></li>
<li><a class="post-section-overview" href="#how-to-add-copy-buttons-to-the-code-snippets">How to Add Copy Buttons to the Code Snippets</a></li>
<li><a class="post-section-overview" href="#how-to-auto-generate-a-table-of-contents">How to Auto-generate a Table of Contents</a></li>
<li><a class="post-section-overview" href="#key-takeways">Key Takeways</a></li>
</ul>
<p><a></a></p>
<h2 id="heading-how-to-install-tampermonkey">How to Install Tampermonkey</h2>
<p>For Chrome, the extension is available on the <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">chrome web store.</a></p>
<p>It's also available for Firefox, which you can install from <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Firefox Add-Ons.</a></p>
<p>For other browsers, you can visit the <a href="https://www.tampermonkey.net/index.php" target="_blank">Tampermonkey Home Page</a>. Currently, Chrome, Firefox, Edge, Safari and Opera are officially supported. But the one from Chrome web store works fine on Chromium-based browsers like Brave.</p>
<p><a></a></p>
<h2 id="heading-how-to-create-a-new-userscript">How to Create a New Userscript</h2>
<p>The easiest way to get started is using the <code>create new script</code> option from the toolbar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/create-new-script.jpeg" alt="create new script" width="600" height="400" loading="lazy"></p>
<p>You will be presented with something like this:</p>
<pre><code><span class="hljs-comment">// ==UserScript==</span>
<span class="hljs-comment">// @name         New Userscript</span>
<span class="hljs-comment">// @namespace    http://tampermonkey.net/</span>
<span class="hljs-comment">// @version      0.1</span>
<span class="hljs-comment">// @description  try to take over the world!</span>
<span class="hljs-comment">// @author       You</span>
<span class="hljs-comment">// @match        https://www.freecodecamp.org/news/*</span>
<span class="hljs-comment">// @icon         https://www.google.com/s2/favicons?sz=64&amp;domain=freecodecamp.org</span>
<span class="hljs-comment">// @grant        none</span>
<span class="hljs-comment">// ==/UserScript==</span>

(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-meta">    'use strict'</span>;

    <span class="hljs-comment">// Your code here...</span>
})();
</code></pre><p>The userscript header contains information about the script and other important parameters as well. </p>
<p>For now the relevant part is <code>@match</code>. This tells Tampermonkey for which sites to run the script. I have changed to match any article on the freeCodeCamp news site by using the "*" wildcard.</p>
<p>Test it out by putting something simple as <code>alert("HI")</code> on the function. Then navigate to any article on freecodecamp.org/news. </p>
<p>Lets do something interesting next. This will be a brief introduction to DOM manipulation. </p>
<p>Before writing code in the userscript, it's better to write out your code in the dev console first. Then, when you have working code, you can just paste it in the Tampermonkey script. This is how we will write all the scripts in this article as well.</p>
<p><a></a></p>
<h2 id="heading-how-to-change-a-buttons-behaviour">How to Change a Button's Behaviour</h2>
<p>At the end of each freeCodeCamp article there is a button that says "Tweet a thanks". You can see the onclick function of the button by using the element picker from the dev tools and clicking on that button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/tweet-btn.jpeg" width="600" height="400" alt="tweet-btn" loading="lazy"></p>
<p>Clicking on that button will open a new window in Twitter with some default text for a tweet like the following:</p>
<pre><code>Thank you @twitter-username-<span class="hljs-keyword">of</span>-author <span class="hljs-keyword">for</span> writing <span class="hljs-built_in">this</span> helpful article.

Title <span class="hljs-keyword">of</span> Article
<span class="hljs-attr">https</span>:<span class="hljs-comment">//www.freecodecamp.org/news/slug-of-article</span>
</code></pre><p>Let's say that you want to change up the default text to something like this:</p>
<pre><code>This article is quite fascinating.

Title <span class="hljs-keyword">of</span> Article by @twitter-username-<span class="hljs-keyword">of</span>-author
<span class="hljs-attr">https</span>:<span class="hljs-comment">//www.freecodecamp.org/news/slug-of-article</span>
</code></pre><p>The new text is static as you will chose it, but there are some variables here. You need to extract these 3 things:</p>
<ol>
<li>The link of the article.</li>
<li>The Twitter username of the author.</li>
<li>Title of the article.</li>
</ol>
<p>Extracting the link is as <code>location.href</code>.</p>
<p>Now how to extract the Twitter username?</p>
<p>At first glance, you may think the name after the author image is the answer. But that's actually the author's name (which is not necessarily unique) – it's not their Twitter username. So, where would you start looking for it?</p>
<p>Since you can just click the button or inspect it, you already know the values of all these variables. So a good way to find the Twitter username would be just to search for it in the entire HTML document. As it is unique, there won't be many occurrences. </p>
<p>Open the dev tools and search for the Twitter username. You will find this tag:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:creator"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"@username"</span> /&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/twitter-username-search.jpeg" width="600" height="400" alt="twitter-username-search" loading="lazy"></p>
<p>Which is not surprising, as most publishing sites put information such article title, tags, description, and author information in the meta tags for SEO purposes. But most of the time you can just find what you are looking for by using the inspection tool.</p>
<p>So how do we extract the "content" property of this meta tag? Considering that an article is written by only one author, there will always be only one meta tag with name "twitter:creator". So you can just use the querySelector.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> username = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'meta[name="twitter:creator"]'</span>).content
</code></pre>
<p>Now on to extracting the title. If you have wandered around looking for the Twitter username, you will find that there is also a meta tag with name "twitter:title". But this time, the title is something that you can visually see and inspect. In most cases that's the easier way to go about it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/article-title.png" width="600" height="400" alt="article-title" loading="lazy"></p>
<p>If you inspect on the article title above the cover image, you will see it's inside an h1 with the class "post-full-title". You can select this with the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> title = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"h1.post-full-title"</span>).textContent
</code></pre>
<p>Now we have all the pieces we need to make the change. Inspect the target button and you will see it comes with an id of "tweet-btn". Now let's define our text.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> text = <span class="hljs-string">`This article is quite fascinating.

<span class="hljs-subst">${title}</span> by <span class="hljs-subst">${username}</span>
<span class="hljs-subst">${location.href}</span>
`</span>
</code></pre>
<p>Putting it all together by changing the button's onclick, here's what you should have:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">change_tweet_text</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"tweet-btn"</span>).onclick = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> username = <span class="hljs-built_in">document</span>.querySelector(
            <span class="hljs-string">'meta[name="twitter:creator"]'</span>
        ).content
        <span class="hljs-keyword">const</span> title = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"h1.post-full-title"</span>).textContent

        <span class="hljs-keyword">const</span> text = <span class="hljs-string">`This article is quite fascinating.

<span class="hljs-subst">${title}</span> by <span class="hljs-subst">${username}</span>
<span class="hljs-subst">${location.href}</span>
`</span>

        <span class="hljs-keyword">const</span> share_link = <span class="hljs-built_in">encodeURI</span>(
            <span class="hljs-string">`https://twitter.com/intent/tweet?text=<span class="hljs-subst">${text}</span>`</span>
        )

        <span class="hljs-built_in">window</span>.open(share_link, <span class="hljs-string">"share-twitter"</span>, <span class="hljs-string">"width=550, height=235"</span>)

        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
}
</code></pre>
<p>Because the text contains newline characters, we have to convert it url-encoded format.</p>
<p>Paste the function inside the userscript and call it.</p>
<pre><code class="lang-js">;(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    change_tweet_text()
})()
</code></pre>
<p>Now reload the article page and click on the button to see if it's working as expected.</p>
<p>It is possible that it won't work as expected. Especially if the page takes a lot of time to load. </p>
<p>With this script, we are modifying a button which is a DOM element. But what if the element is not yet loaded? Then you will face unexpected results. </p>
<p>Tampermonkey provides the "// @run-at" header which you can specify to "document-end", but I've found that it still produces unexpected results sometimes. </p>
<p>The right way to mitigate it is to use the "load" event which is emitted when the whole page is loaded. So, we can refactor our driver function in the following way:</p>
<pre><code class="lang-js">addEventListener(<span class="hljs-string">"load"</span>, main)

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
}
</code></pre>
<p>You can be extra careful and throw a delay as well:</p>
<pre><code class="lang-js">addEventListener(<span class="hljs-string">"load"</span>, <span class="hljs-built_in">setTimeout</span>(main, <span class="hljs-number">2000</span>))
</code></pre>
<p>From now on, we will call all the functions in this main function which will run after the load event is fired.</p>
<p>Note that you could have achieved the same result using a regular expression. In that case, you could extract the variables from the button's onclick which contains the URL with the full text. </p>
<p>But extracting information from the tags is better because if one day the default tweet text changes, you would need the change the regex as well.</p>
<p><a></a></p>
<h2 id="heading-how-to-implement-reader-view-for-freecodecamp-news-articles">How to Implement Reader View for freeCodeCamp /news Articles</h2>
<p>If you make your browser fullscreen, the top navigation bar still stays at the top – which is not the best reading experience. So, we want to hide everything except the main article body. </p>
<p>Let's start inspecting. You will see that, close to the "main" tag, there is an "article" tag with class "post". It contains all the text of the article including the heading and cover image.</p>
<p>This time, we will live-edit the CSS of the page. In Firefox, you can use the "Style Editor" tab in the dev tools. Click on the "+" icon and start testing out your CSS.</p>
<p>As of now, there is no built-in way to hide all elements except one (in our case article.post) using CSS. You might be tempted to use <code>:not(article.post)</code> but it won't work because if any ancestor of the element is hidden, then all the descendents will be hidden as well. </p>
<p>We can accomplish our goal by using the ":has" selector. <code>:has(article.post)</code> will select all the ancestors of article.post so we can select the inverse of it with <code>:not(:has(article.post))</code>. </p>
<p>But there's still a problem: the descendent of article.post is also ignored by this selector. We can bring them back by chaining another not –<code>:not(article.post, article.post *)</code>. This will select everything that is not an article.post or a descendent of it.</p>
<p>This is the final CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:has(article.post))</span><span class="hljs-selector-pseudo">:not(article.post</span>, <span class="hljs-selector-tag">article</span><span class="hljs-selector-class">.post</span> *) {
    <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/style-editor.png" width="600" height="400" alt="style-editor" loading="lazy"></p>
<p>At the time of writing this, the ":has" selector is experimental in Firefox. But you can go to <code>about:config</code> and enable it by changing the value of <code>layout.css.has-selector.enabled</code> to true.</p>
<p>What if you want to use the navigation bar? It would be a shame to manually write the CSS and remove it when you don't want it, right? Especially now that you can do some scripting.</p>
<p>We will add a keyboard shortcut to the site that will make the browser fullscreen. We'll also apply that CSS and, upon exiting fullscreen, the CSS will be removed.</p>
<p>One of the exposed APIs of tampermonkey is <code>GM_addStyle(css)</code> for adding CSS. For this API to work, you need to include <code>// @grant           GM_addStyle</code> to the userscript header and also remove <code>// @grant none</code> if you have that.</p>
<p>To create the shortcut, you will use a "keydown" event listener.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_declutter_toggle</span>(<span class="hljs-params">key</span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .declutter :not(:has(article.post)):not(article.post, article.post *) {
            display: none;
        }`</span>

    GM_addStyle(css)

    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (event.key === key) {
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.fullscreen) {
                <span class="hljs-built_in">document</span>.exitFullscreen()
                <span class="hljs-built_in">document</span>.body.classList.remove(<span class="hljs-string">"declutter"</span>)
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">document</span>.body.classList.add(<span class="hljs-string">"declutter"</span>)
                <span class="hljs-built_in">document</span>.documentElement.requestFullscreen()
            }
        }
    })
}
</code></pre>
<p>We are applying the CSS to the elements with the class "declutter". By toggling the "declutter" on the body we are essentially toggling the CSS on the entire page.</p>
<p>Now, we just have to add the function call in our main function.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
}
</code></pre>
<p>Reload the page and you will see that pressing "F" will toggle fullscreen and also remove everything except article body when in the fullscreen state.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/declutter_demo.gif" alt="declutter demo" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<h2 id="heading-how-to-add-copy-buttons-to-the-code-snippets">How to Add Copy Buttons to the Code Snippets</h2>
<p>The first step is always identifying the correct selector. If you inspect any code snippet you will see that they're contained in <code>&lt;code&gt;</code> blocks and have a <code>&lt;pre&gt;</code> as a parent. So, to select all code snippets, the selector can be <code>pre &gt; code</code>. </p>
<p>Now the question is where to place the button? For easy access, we want the button to be on top of the code snippet and aligned to the right. You may think, placing it between the <code>&lt;pre&gt;</code> tag and <code>&lt;code&gt;</code> tag
could work – but that will interfere with the content of the code snippet which is not ideal.</p>
<p>The ideal solution is wrapping the <code>&lt;pre&gt;</code> tag with a div and creating the structure as <code>div &gt; button &gt; pre &gt; code</code>.</p>
<p>The copying is done by the clipboard API using the <code>navigator.clipboard.writeText</code> method. We will also apply some styling and let the reader know their copying was successful by changing the button text from "copy" to "copied" for a small duration of time.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">attach_code_copy_btn</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .copy-btn {
            float: right;
            margin-bottom: 5px;
            border-radius: 1rem;
            font-size: 0.8em;
            width: 7rem;
        }

        .pre-wrapper {
            width: 100%;
        }
    `</span>
    GM_addStyle(css)

    <span class="hljs-keyword">const</span> codes = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"pre &gt; code"</span>)
    codes.forEach(<span class="hljs-function">(<span class="hljs-params">code</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> pre = code.parentElement
        <span class="hljs-keyword">const</span> btn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"button"</span>)
        btn.textContent = <span class="hljs-string">"copy"</span>
        btn.classList.add(<span class="hljs-string">"copy-btn"</span>)
        btn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span>
            navigator.clipboard.writeText(code.textContent).then(<span class="hljs-function">() =&gt;</span> {
                btn.textContent = <span class="hljs-string">"copied"</span>
                <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> (btn.textContent = <span class="hljs-string">"copy"</span>), <span class="hljs-number">2500</span>)
            })
        )
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>)
        div.classList.add(<span class="hljs-string">"pre-wrapper"</span>)
        div.appendChild(btn)
        wrap(pre, div)
    })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">wrap</span>(<span class="hljs-params">elem, wrapper</span>) </span>{
    elem.parentNode.insertBefore(wrapper, elem)
    wrapper.appendChild(elem)
}
</code></pre>
<p>The <code>attach_code_copy_btn</code> function finds all code snippets and wraps them around a div, adds classes to them, and attaches a button which will copy the code content on click.</p>
<p>Call the function just as before:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
    attach_code_copy_btn()
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/copy_demo.gif" alt="copy demo" width="600" height="400" loading="lazy"></p>
<p><a></a></p>
<p>Heres some practice, try adding click event listeners to all <code>&lt;code&gt;</code> tags that are not inside a <code>&lt;pre&gt;</code> tag. On a mouse click on the <code>&lt;code&gt;</code> block the contents of the  block will be copied.</p>
<h2 id="heading-how-to-auto-generate-a-table-of-contents">How to Auto-generate a Table of Contents</h2>
<p>freeCodeCamp is a great place for reading in-depth articles. The articles are well organized as well. Most authors use proper headings and provide a table of contents. But the ToC is not accessible on every part of the page. </p>
<p>If you want to jump somewhere you will have to go back to the ToC then click on your section of interest. And then there are shorter articles that don't provide a ToC at all.</p>
<p>That's where we can introduce a userscript that will automatically generate a table of contents which is accessible anywhere on the page. This improves the user experience when reading longer articles. </p>
<p>The first thing you should know is how a ToC works. A section in the ToC is just an anchor tag with an href value of <code>#some-section</code>. Here "some-section" can be the name property of an anchor tag of the target location (for example, <code>&lt;a name="some-section"&gt;</code>) or if its used as an id on any element (for example, <code>&lt;h2 id="some-section"&gt;</code>).</p>
<p>If you inspect on any heading tag, you will see that the id attribute is present. This is automatically inserted when the article page is built. So we can use that for our ToC.</p>
<p>This is a ToC generator function modified from a <a href="https://stackoverflow.com/questions/187619/is-there-a-javascript-solution-to-generating-a-table-of-contents-for-a-page" target="_blank">StackOverflow answer</a>.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generate_toc</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> toc = <span class="hljs-string">""</span>
    <span class="hljs-keyword">let</span> level = <span class="hljs-number">1</span>

    <span class="hljs-keyword">let</span> container = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>)
    <span class="hljs-keyword">const</span> regex = <span class="hljs-regexp">/&lt;h([2-5])\s+id="([^"]+)"&gt;([^&lt;]+)&lt;\/h([2-5])&gt;/gi</span>
    <span class="hljs-keyword">const</span> matches = [...container.innerHTML.matchAll(regex)]
    matches.forEach(<span class="hljs-function">(<span class="hljs-params">match</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (match.length != <span class="hljs-number">5</span>) <span class="hljs-keyword">return</span>
        <span class="hljs-keyword">const</span> [_, openLevel, id, titleText, closeLevel] = match

        <span class="hljs-keyword">if</span> (openLevel !== closeLevel) {
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-keyword">if</span> (openLevel &gt; level) {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(openLevel - level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;ol&gt;"</span>)
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (openLevel &lt; level) {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level - openLevel + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/li&gt;&lt;/ol&gt;"</span>)
        } <span class="hljs-keyword">else</span> {
            toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/li&gt;"</span>)
        }

        level = <span class="hljs-built_in">parseInt</span>(openLevel)

        <span class="hljs-keyword">if</span> (!id) {
            id = titleText.replace(<span class="hljs-regexp">/ /g</span>, <span class="hljs-string">"_"</span>)
        }
        toc += <span class="hljs-string">'&lt;li&gt;&lt;a href="#'</span> + id + <span class="hljs-string">'"&gt;'</span> + titleText + <span class="hljs-string">"&lt;/a&gt;&lt;/li&gt;"</span>
    })

    <span class="hljs-keyword">if</span> (level) {
        toc += <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(level + <span class="hljs-number">1</span>).join(<span class="hljs-string">"&lt;/ol&gt;"</span>)
    }

    <span class="hljs-keyword">return</span> toc
}
</code></pre>
<p>We are using a regex to match h2, h3, h4, and h5 tags and creating an ordered list to display to ToC contents and returning the HTML of the whole ToC.</p>
<p>Now we will add this HTML to a div that will contain the ToC. That div will be wrapped inside a dialog component so that we can use a modal to easily show/hide the ToC. We will also add a button that will be used as the toggle.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show_toc</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"hidden"</span>
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"toc-dialog"</span>).showModal()
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_toc_toggle</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> dialog = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"dialog"</span>)
    dialog.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"toc-dialog"</span>)
    <span class="hljs-keyword">const</span> toc_div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>)
    toc_div.classList.add(<span class="hljs-string">"toc-content"</span>)
    toc_div.innerHTML = <span class="hljs-string">"&lt;h2&gt;Table of Contents&lt;/h2&gt;"</span>
    dialog.appendChild(toc_div)

    <span class="hljs-keyword">const</span> show_toc_btn = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"button"</span>)
    show_toc_btn.setAttribute(<span class="hljs-string">"id"</span>, <span class="hljs-string">"toc-toggle"</span>)
    show_toc_btn.innerHTML =
        <span class="hljs-string">'&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path d="M.361 256C.361 397 114 511 255 511C397 511 511 397 511 256C511 116 397 2.05 255 2.05C114 2.05 .361 116 .361 256zM192 150V363H149V150H192zM234 150H362V193H234V150zM362 235V278H234V235H362zM234 320H362V363H234V320z"&gt;&lt;/path&gt;&lt;/svg&gt;'</span>

    show_toc_btn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
        show_toc()
    })

    <span class="hljs-comment">// Click anywhere to close</span>
    dialog.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"auto"</span>
        event.currentTarget.close()
    })

    dialog.addEventListener(<span class="hljs-string">"close"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.overflow = <span class="hljs-string">"auto"</span>
        event.currentTarget.close()
    })

    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>).appendChild(dialog)
    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".post-content"</span>).appendChild(show_toc_btn)
}
</code></pre>
<p>Finally we will provide a driver function that will perform all the necessary tasks related to ToC generation. We will also add a keyboard shortcut to toggle the ToC.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_toc</span>(<span class="hljs-params">toc_toggle_key</span>) </span>{
    <span class="hljs-keyword">const</span> css = <span class="hljs-string">`
        .toc-content {
            margin: 0;
            padding: 0;
            max-width: 65vw;
        }

        @media (width &lt;= 800px) {
            .toc-content {
                max-width: 100vw;
            }
        }

        .toc-content h2 {
            padding: 20px 30px;
        }

        .toc-content ol {
            counter-reset: item;
        }

        .toc-content li {
            display: flex;
            white-space: nowrap;
            gap: 5px;
        }

        .toc-content li:before {
            content: counters(item, ".") " ";
            counter-increment: item;
        }

        .toc-content a {
            text-decoration: none;
            white-space: normal;
            flex-grow: 1;
        }

        .toc-content li:hover {
            background: rgba(82, 142, 227, 0.3);
            cursor: pointer;
        }

#toc-toggle {
            background: transparent;
            position: fixed;
            bottom: 5%;
            right: 5%;
            width: 5rem;
        }

#toc-toggle svg {
            fill: rgb(82, 142, 227);
            scale: 1;
        }

#toc-toggle svg:hover {
            scale: 1.15;
        }

#toc-dialog {
            padding: 0;
            border: 0;
        }
`</span>
    GM_addStyle(css)

    add_toc_toggle()
    <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".toc-content"</span>).innerHTML += generate_toc()

    <span class="hljs-keyword">if</span> (toc_toggle_key) {
        <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (event.key === toc_toggle_key) {
                show_toc()
            }
        })
    }
}
</code></pre>
<p>Now, just call the driver function in main.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    change_tweet_text()
    add_declutter_toggle(<span class="hljs-string">"F"</span>)
    attach_code_copy_btn()
    add_toc(<span class="hljs-string">";"</span>)
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/toc_demo-1.gif" alt="toc demo" width="600" height="400" loading="lazy"></p>
<p>Now whenever, you read an article on freeCodeCamp, you can use the toggle button or a shortcut to view the ToC.</p>
<p>For practice, try making the toc sticky in the side (left/right). If you are on a wider screen, having the toc on the side will make the reading experience even better. </p>
<p><a></a></p>
<h2 id="heading-key-takeways">Key Takeways</h2>
<p>You should never run arbitrary JavaScript on any occasion. That goes for Tampermonkey scripts as well. </p>
<p>I recommend going through every line of code before using a userscript. As you can already imagine, a userscript can drastically alter a website's appearance. If you are not careful enough, you might click a button that you expect to do one thing but it does something completely different, so always err on the side of caution.</p>
<p>When writing userscripts, keep these things in mind:</p>
<ol>
<li>Always test your code in the dev console first and take care of all edge cases.</li>
<li>Prefer creating/manipulating DOM elements using built-in functions instead of modifying the innerHTML.</li>
<li>If you find any unexpected behaviour, try throwing a long delay and checking if that resolves the issue.</li>
</ol>
<p>You can read my other articles on my <a href="https://blog.renzhamin.com" target="_blank">blog</a>. Find me on <a href="https://twitter.com/renzhamin" target="_blank">Twitter</a> <a href="https://www.linkedin.com/in/renzhamin/" target="_blank">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Information Architecture? How to Create Userflows and Sitemaps for UX Design ]]>
                </title>
                <description>
                    <![CDATA[ At some point in your design career, you may hear about Information Architecture (or IA). You might even have to create a userflow or sitemap. Now, this article aims to help you go back to the basics and remind you of the meaning and importance of IA... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/information-architecture-userflow-sitemap/</link>
                <guid isPermaLink="false">66d03a33dcbab93f8b58df8f</guid>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Faith Olohijere ]]>
                </dc:creator>
                <pubDate>Tue, 18 Jul 2023 20:40:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/pexels-sevenstorm-juhaszimrus-425160--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>At some point in your design career, you may hear about Information Architecture (or IA). You might even have to create a userflow or sitemap.</p>
<p>Now, this article aims to help you go back to the basics and remind you of the meaning and importance of IA. So if you don't really understand the concept of Information Architecture, this is the best place to be. </p>
<p>We'll also explore how to do a userflow and a sitemap later in the article. Let's get started!</p>
<h2 id="heading-what-is-information-architecture">What is Information Architecture?</h2>
<p>Information Architecture (IA) is the process of planning and designing how information is organized in websites and applications. It focuses on meeting user needs and business goals by creating a logical and user-friendly information structure.</p>
<p>Information Architecture is mostly used by designers to structure the layout of their designs, especially during the sketching/low-fidelity stage.</p>
<h3 id="heading-importance-of-information-architecture">Importance of Information Architecture</h3>
<p>I can't overstate the importance of Information Architecture. Below, I'll share some reasons why every UX Designer should learn what IA means and how to implement it effectively:</p>
<ul>
<li>Organization and Structure: Information Architecture provides a framework for organizing and structuring information. It helps you make sure your designs are logically grouped and categorized.</li>
<li>Content Prioritization and Hierarchy: Hierarchy is one of the basic principles of design, and IA helps you achieve that. It enables designers to highlight key information and arrange content based on its relevance and importance to users.</li>
<li>User-Centric Design: Information Architecture helps designers think of their users during the design process, by considering how users typically think, behave, and search for information. Everyone has mental models, including users, and IA helps to make sure that information is presented in a way that matches the mental models of your already existing/target users.</li>
<li>Navigation: Information Architecture provides a clear way to navigate a design. It allows users to understand intuitively where they are in a site and how to move between different sections of a design.</li>
<li>Business Goals: Information Architecture helps to make sure the information presentation of your design aligns with the goals and objectives of the business/firm/company. </li>
</ul>
<h2 id="heading-types-and-forms-of-information-architecture">Types and Forms of Information Architecture</h2>
<p>There are so many types and forms of Information Architecture, especially since it's used in a lot of fields. But here we'll be looking at Userflows and Sitemaps, which are the most common ones in design.</p>
<h3 id="heading-what-is-a-userflow">What is a Userflow?</h3>
<p>A userflow refers to a diagram that shows the step-by-step journey of a user through a website, application, or system. </p>
<p>Imagine trying to order food online. Some steps you might take include: opening the food ordering application on your phone, signing up/logging in, choosing from the list of available food, choosing how many portions you'd like, and finally making payment online. </p>
<p>That's a typical example of how a user flow might be structured.</p>
<p>Userflows illustrate the paths, interactions, and decision points users encounter as they navigate and complete specific tasks or goals. They help designers and stakeholders understand and optimize the user journey.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/media_127cc680607ff7d6703be50271d9f563fd17e071c.png" alt="Image" width="600" height="400" loading="lazy">
<em>An example of a Userflow</em></p>
<h3 id="heading-what-is-a-sitemap">What is a Sitemap?</h3>
<p>Sitemaps are diagrams that display the layout and organization of content within a website or application. </p>
<p>Imagine a 2 bedroom apartment. The apartment could contain a sitting room, a kitchen, 2 bedrooms, a bathroom, and a dining room. The sitemap simply tells us how we can get to the kitchen from the sitting room/the bedroom/any place in the apartment.</p>
<p>In design, sitemaps provide an overview of the pages, menus, and sub-pages, helping us plan and visualize the information architecture and user experience of the system. They aid in content planning, navigation design, and website development.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/337-sitemap-template.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>An example of a Sitemap</em></p>
<h2 id="heading-differences-between-userflows-and-sitemaps">Differences Between Userflows and Sitemaps</h2>
<ol>
<li>Focus and Perspective: Userflows primarily focus on the step-by-step actions and interactions a user takes within an app or website, to accomplish a task or goal. Sitemaps focus on the overall structure and organisation of a website or an application, depicting the hierarchical relationship between different pages or sections.</li>
<li>Level of Detail: Userflows provide more detailed information about each step of the user's journey, including specific actions, decisions, and possible outcomes at each stage. Sitemaps provide a high-level overview of the app's or website's structure, showing the main pages or sections, without diving into the specific interactions within each page.</li>
<li>User-Centric vs Architectural: Userflows are user-centric and primarily represents the user's perspective and actions within the app or website. Sitemaps are more architectural in nature, representing the overall structure and organisation of an app or a website from a high-level viewpoint.</li>
<li>Interactivity and Decision-points: Userflows highlight interactive elements within the app or website, illustrating the choices and options available to the user at each step. Sitemaps do not explicitly showcase interactivity or decision points. </li>
</ol>
<h2 id="heading-when-to-use-information-architecture">When to Use Information Architecture</h2>
<p>You might be wondering when knowledge about Information Architecture will come in handy. </p>
<p>You can use Information Architecture in any or situation at all, really – like designing a mobile app, a website, a pitch deck, and more. Information Architecture is always helpful because it helps you decide what content/information comes first, what follows and how, and so on. </p>
<p>Typically, you'll think about Information Architecture after conducting user research. This is because research can help you understand the goals and needs of your users, and what they would likely do when they're trying to accomplish a goal on your site. </p>
<p>Also, userflows usually come before sitemaps, because you have to understand how a typical user would journey through your site, before deciding on how the layout or organization of that site would look like.</p>
<h2 id="heading-how-to-create-userflows-and-sitemaps">How to Create Userflows and Sitemaps</h2>
<p>You can use the same methods and tools to create userflows and sitemaps. You just need to understand the differences between them and bear in mind that the purpose of each one is different.</p>
<h3 id="heading-how-to-create-a-userflow">How to Create a Userflow</h3>
<h4 id="heading-step-1-define-your-goals-and-identify-tasks">Step 1: Define your Goals and Identify Tasks</h4>
<p>The first step is to define your goals and objectives for the project, stating what kind of Information Architecture you're trying to create (a userflow or a sitemap). You can just write this down on paper before digitalizing it.</p>
<p>For example: I'm working on a food ordering app, and I'd like to make a userflow showing how a typical user would order food using the app. </p>
<p>I would write down the typical/possible steps the user would have to go through to achieve the task of ordering food. These steps could be:</p>
<ul>
<li>The user opens the application on their phone.</li>
<li>The user signs up or logs into the application.</li>
<li>The user goes to the homepage.</li>
<li>The user chooses from restaurants close to them.</li>
<li>The user selects from the menu and adds food to cart.</li>
<li>The user pays for the food and waits for delivery.</li>
<li>The user exits the application.</li>
</ul>
<h4 id="heading-step-2-choose-a-tool">Step 2: Choose a Tool</h4>
<p>The next step is to choose a tool you want to use to create your userflow. There are lots of tools available for this, like Figjam, Miro, Whimsical, and others. I use Figjam since it's so easy to understand. </p>
<p>Here's what it looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Figjam interface</em></p>
<h4 id="heading-step-3-sketch-your-design">Step 3: Sketch your design</h4>
<p>The next thing is for you to sketch your actual flow on Figjam. Before this, you should note that every shape for a userflow has a specified meaning. You can see some shapes, flows and associations, and their meanings below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Shapes, flows, and associations for a userflow.</em></p>
<p>The First Shape (Rounded edge rectangle): This shape represents the start/end of a userflow. It's typically used when you want to start or end a userflow. If you notice, there's also an end tag with a similar shape in the image above. Some people also use a circle to represent this.</p>
<p>The Second Shape (Rhombus): This shape represents a decision. It's used to show when a user has to make a decision like "login", "signup", and so on.</p>
<p>The Third Shape (Rectangle with dotted lines): This represents a group. A group in a userflow can contain process and data, showing that an action has been submitted to the database, and it's expecting feedback from the site.</p>
<p>The Fourth Shape (Parallelogram): This shape shows when the user has to make an input, like "choose how many portions of food they want to order, and so on.</p>
<p>The Fifth Shape (Rectangle with solid lines): This shape shows the process of the user going through the application or website. It might also include screens the user needs to pass through in order to achieve their goal.</p>
<p>The Sixth Shape (Cylinder): this shape represents the data running in the system, which feedback is required from.</p>
<p>Now that you know the different shapes and their uses, you'll need to first draw the shape for 'start':</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow4.png" alt="Image" width="600" height="400" loading="lazy">
<em>Choosing a Shape</em></p>
<p>Next, you'll want to resize the shape if necessary, and input your text.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow-5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Resizing shape and adding text</em></p>
<p>Up next, you'll need to add another shape and draw a connector. To add a connector, you can either click on the edge of the previous shape and drag, or draw the connector directly from the Figjam tools.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow6.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing a connector to the next shape.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow7.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing a Connector from Figjam tools</em></p>
<p>When you do that, you then input your text. (Remember to take note of the meanings/roles of each particular shape).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow8.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You continue this way until you're done adding all the steps in your userflow diagram.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Userflow9.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example userflow diagram with multiple steps</em></p>
<h4 id="heading-step-4-validate-and-finalize">Step 4: Validate and finalize</h4>
<p>Once you're done with your userflow, the next thing is to validate the diagram. This just means reviewing the userflow to make sure it's accurate, usable, and aligns with the users' needs. </p>
<p>Basically just go through the diagram and consider whether it represents the intended user journey, and whether all necessary steps and decision points are included.</p>
<h3 id="heading-how-to-create-a-sitemap">How to Create a Sitemap</h3>
<p>Earlier in the article, I mentioned that the processes for creating a sitemap and a userflow are similar. The difference lies in the purpose of the two diagrams. We'll be exploring how to create a sitemap using Miro, a diagramming tool.</p>
<h4 id="heading-step-1-open-the-tool">Step 1: Open the tool</h4>
<p>The first step obviously, is to open your tool. If you don't have an account already, sign up at <a target="_blank" href="https://www.freecodecamp.org/news/p/d9aa98df-32bc-4463-bf4e-557863ae187c/miro.com">Miro</a> and it should display your dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Miro Dashboard</em></p>
<p>Next, click on "New Board" to open a blank canvas.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-step-2-start-drawing-the-sitemap">Step 2: Start drawing the sitemap</h4>
<p>Next, you start drawing your sitemap. You should already know what screens you'll be designing, and how these screens connect to each other. Your userflow might help inform this.</p>
<p>Click on the Shapes tool from the toolbar on the lefthand side of your screen, to bring up all available shapes. </p>
<p>Note that, unlike Userflows where every shape has a meaning, you can use any shape for your sitemaps. Most designers make use of rectangles when doing sitemaps. It's very easy to build and stack, especially when you have lots of pages to represent.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Choosing a shape</em></p>
<p>A plus icon will appear, and you just have to drag till the shape is to your satisfaction.</p>
<h4 id="heading-step-3-edit-the-shapes">Step 3: edit the shapes</h4>
<p>Next, you edit the shape to add colour, and your text. This could be "Homepage", "Log-in", "Signup", and so on – whatever is the first screen of your design. </p>
<p>When you click on the button, a sub-menu comes up. Here, you can switch your shape type, change text font and size, add text colour, align it, add a border or fill colour for the shape, and more.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro4.png" alt="Image" width="600" height="400" loading="lazy">
<em>Typing inside a shape</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Editing a shape</em></p>
<h4 id="heading-step-4-add-shapes-for-the-next-screens">Step 4: add shapes for the next screens</h4>
<p>Next, you need to draw shapes for the next screens which the homepage branches into. This could be major items on the navigation bar, where you have to click on CTAs (Call To Actions) to open them. </p>
<p>To draw the next shape, you can simply click on the edges of the first one, and the outline of the shape will be displayed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro6.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sketching the next shape</em></p>
<p>Click on the outline to indicate that you want the shape to stay, and basically edit just like you did the first one.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro7.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro7.5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Editing the second shape</em></p>
<p>Next, you need to add other shapes that represent the subpages which the homepage branches out into. Just click on the first shape like you did when making the first sub-page shape.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro8.png" alt="Image" width="600" height="400" loading="lazy">
<em>Adding other sub-pages</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro9.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can also change their colours to differentiate from the homepage and show hierarchy.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro10.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-step-5-draw-more-pages">Step 5: draw more pages</h4>
<p>Next, you draw shapes for the pages under the first set of sub-pages.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro11.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You just continue that way until you're done with your sitemap, and all the screens you want to design have been represented in it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/miro12.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you're done making your userflow and sitemaps, you can move on to your low-fidelity wireframes or your main design proper. The IA you've done informs your next step and decides what screens you should be designing in particular.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Information Architecture is very important for you to be able to organize the layouts of your designs and arrange them. This is helpful because it can really help improve the quality of your designs. </p>
<p>There are even templates on some softwares that could help you do your Information Architecture diagrams. Remember to practice as often as you can.</p>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Improve User Experience  in React Apps – Animate Routes using Framer Motion ]]>
                </title>
                <description>
                    <![CDATA[ In the modern digital landscape, user experience (UX) has become a central focus of web development. Providing a smooth, captivating, and aesthetically pleasing interface can really influence user satisfaction and retention.  Route animation is a fre... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/improve-user-experience-in-react-by-animating-routes-using-framer-motion/</link>
                <guid isPermaLink="false">66bb890dadd24ba4273250e2</guid>
                
                    <category>
                        <![CDATA[ animation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Tue, 04 Apr 2023 22:27:19 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/Article-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the modern digital landscape, user experience (UX) has become a central focus of web development. Providing a smooth, captivating, and aesthetically pleasing interface can really influence user satisfaction and retention. </p>
<p>Route animation is a frequently underestimated aspect of UX that can substantially improve a website's interactivity. </p>
<p>In this tutorial, I'll walk you through the steps of incorporating route animations in React applications using Framer Motion, a powerful and user-friendly animation library.</p>
<h2 id="heading-importance-of-animating-routes-in-web-applications">Importance of Animating Routes in Web Applications</h2>
<p>Animating routes can make transitions between different pages or sections of a website more visually appealing and interactive. Smooth route animations enhance the overall user experience by providing a sense of continuity and fluidity. They can also minimise perceived loading times and keep users engaged while new content is fetched or rendered.</p>
<p>If you’re like I am, and you're a sucker for aesthetic animations, you’d agree that websites that have silky animations and transitions, especially between their parts (routes), tend to leave a stronger impression and keep you browsing longer than less animated ones.</p>
<h2 id="heading-a-brief-introduction-to-react-and-framer-motion">A Brief Introduction to React and Framer Motion</h2>
<p>React has become a widely-used JavaScript library for crafting user interfaces, especially in the context of single-page applications (SPAs). As an SPA-focused solution, React loads a single HTML page and dynamically alters the content based on user navigation within the app, through route changes.</p>
<p>Framer Motion, an open-source animation library designed for React, delivers a straightforward and expressive API for generating intricate animations. </p>
<p>The library boasts an extensive array of animation capabilities, including spring physics, gesture handling, and server-side rendering support. This makes Framer Motion an ideal choice for implementing route animations in React applications.</p>
<h2 id="heading-how-to-set-up-your-development-environment">How to Set Up Your Development Environment</h2>
<p>Before you can begin animating routes in a React application, you need to set up your development environment. This includes installing <a target="_blank" href="https://nodejs.org/en/download">Node.js</a> and <a target="_blank" href="https://www.npmjs.com/package/download">npm</a> (Node Package Manager) on your computer.</p>
<h3 id="heading-how-to-create-a-react-project">How to Create a React Project</h3>
<p>Once you have Node.js and npm installed, you can create a new React project using the Create React App command-line tool. Run the following command in your terminal:</p>
<pre><code>npx create-react-app react-framer-animation
</code></pre><p>After that, open that folder with your code editor. It should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/001-Setting-up-and-opening-your-react-app.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up and opening your React App</em></p>
<p><strong>Note</strong>: in this tutorial, I'll be using the <a target="_blank" href="https://code.visualstudio.com/download">VSCode Editor</a> for development but any modern text editor should suffice.</p>
<p>Next, you rid yourself of all the boilerplate styles and unnecessary files in your app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/002-Clearing-the-clutter-files.png" alt="Image" width="600" height="400" loading="lazy">
<em>Clearing the clutter files</em></p>
<p>Your next setup step is to install <em>framer motion</em> and <em>react-router</em> in your React app. Simply open your code editor’s terminal and run:</p>
<pre><code>npm install framer-motion react-router-dom
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2023/04/003-Installing-he-necessary-dependencies.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Installing the necessary dependencies</em></p>
<p>All that’s left is to run <code>npm start</code> which starts up a development server on your browser that opens a blank page.</p>
<h2 id="heading-how-react-router-works">How React Router Works</h2>
<p>React Router is a widely-used library for managing navigation and routing in React applications. It allows developers to create dynamic routes and handle route changes seamlessly (that is, navigating between pages or components).</p>
<p>To help you gain a better understanding, let’s set up routes for our project.</p>
<p>First, import all the necessary functionalities into your <code>App</code> component</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { BrowserRouter, NavLink, Route, Routes } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
</code></pre>
<p>Then create the rest of the components which you’ll navigate through. To prevent tedious back and forths between components, all components will be created in the <code>App.js</code> component.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"home component"</span>
    &gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>  Home Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Header</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"header"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Header Component<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"about component"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> About Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"contact component"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Contact Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>To render these components on the browser, you simply embed them in the <code>App</code> component.</p>
<pre><code class="lang-js">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">classname</span>=<span class="hljs-string">”App”</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>At an initial glance, your app should look something like this in the browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/004-Initial-rendering-of-your-components-without-any-styling.png" alt="Image" width="600" height="400" loading="lazy">
<em>Initial rendering of your components without any styling</em></p>
<p>To make your app look better, add these stylings:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Rajdhani:wght@600&amp;display=swap"</span>);

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Rajdhani"</span>, sans-serif;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}
<span class="hljs-selector-class">.header</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">3px</span> <span class="hljs-number">3px</span> <span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.2</span>);
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
}
<span class="hljs-selector-class">.header</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.header</span> <span class="hljs-selector-tag">ul</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.header</span> <span class="hljs-selector-tag">ul</span> <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1.5px</span> solid <span class="hljs-number">#555</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-selector-class">.component</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">87vh</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.home</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">214</span>, <span class="hljs-number">223</span>, <span class="hljs-number">135</span>);
}
<span class="hljs-selector-class">.about</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">115</span>, <span class="hljs-number">139</span>, <span class="hljs-number">243</span>);
}

<span class="hljs-selector-class">.contact</span> {
  <span class="hljs-attribute">background</span>: palevioletred;
}
</code></pre>
<p>Here's what it'll look like now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/After-applying-css.gif" alt="Image" width="600" height="400" loading="lazy">
<em>App after applying CSS</em></p>
<p>With your components styled, you can begin setting up the routes.</p>
<p>First, wrap the contents of your <code>App</code> component with the <code>BrowserRouter</code><em>,</em> then further wrap the contents with the <code>Routes</code> function. You do this to specify the components you can route (navigate) between.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>Keep in mind the <code>header</code> component is not placed inside the <code>Routes</code> component because it’s going to appear on the page irrespective of the route you navigate to.</p>
<p>Then, you assign a route path to each component.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">About</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Contact</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>At the moment, since you’re in the root route (/), only the home component is visible.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/005-Home-page-after-setting-up-the-routes-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Home page after setting up the routes</em></p>
<p>In order to navigate between pages, you use the <code>NavLink</code> element in your <code>header</code> ul and specify a different route per <code>NavLink</code><em>.</em> This would enable easy routing per <code>NavLink</code> you click on.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Header</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"header"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Header Component<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/contact"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>With that, you’ve successfully set up route buttons for your components!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/After-setting-up-routing-buttons.gif" alt="Image" width="600" height="400" loading="lazy">
<em>After setting up routing buttons</em></p>
<h2 id="heading-how-to-set-up-your-routes-for-animation">How to Set Up Your Routes for Animation</h2>
<p>Now, to my favourite part of this article – animating the routes. To animate routes in React with framer motion, you first import 2 properties.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { motion, AnimatePresence } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;
</code></pre>
<p>The <code>motion</code> property turns whatever element you prefix it to into a <code>motion</code> element that can be animated with Framer, and the <code>AnimatePresence</code> component enables smooth animations when adding, removing, or changing components from a <em>React component tree</em> (visual representation or hierarchy of all the components used in a React application).</p>
<p>To animate the routes, you start by wrapping the content of the <code>BrowserRouter</code> with the <code>AnimatePresence</code> component.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">AnimatePresence</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">About</span> /&gt;</span>} /&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Contact</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">AnimatePresence</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>On its own, the <code>AnimatePresence</code> component can’t tell when a component has mounted or unmounted so you’ll need to listen for that change. </p>
<p>To do this, you use the <code>useLocation</code> hook which listens for when there’s a change in the URL of your app (that is, when the route has changed). But you’re plighted with an error when you import and invoke the <code>useLocation</code> hook in your <code>App</code> component.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>
</code></pre>
<pre><code class="lang-js"> <span class="hljs-keyword">const</span> location = useLocation();
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/006-Error-using-useLocation.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error using useLocation</em></p>
<p>This error is caused because the useLocation hook can only be used within a Router component (a component that’s not a descendant of a Router component), which provides the routing context for the hook.</p>
<p>To solve this, you need to do a little refactoring. First, you create a <code>LocationProvider</code> component. This component is a wrapper component that returns the <code>AnimationPresence</code>.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">LocationProvider</span>(<span class="hljs-params"></span>) </span>{  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AnimatePresence</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">AnimatePresence</span>&gt;</span></span>; }
</code></pre>
<p>You then pass in the <code>children</code> prop to the <code>LocationProvider</code> which the <code>AnimatePresence</code> component uses to wrap any child element that would have a routing animation when mounted or unmounted.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">LocationProvider</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AnimatePresence</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">AnimatePresence</span>&gt;</span></span>;
}
</code></pre>
<p>Then you create a <code>RoutesWithAnimation</code> component where you specify each route and the element to be mounted in that route. In this component, you can now use the <code>useLocation</code> hook to check for when there’s a route change.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RoutesWithAnimation</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> location = useLocation();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Routes</span> <span class="hljs-attr">location</span>=<span class="hljs-string">{location}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{location.key}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">About</span> /&gt;</span>} /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Contact</span> /&gt;</span>} /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span></span>
  );
}
</code></pre>
<p><strong>Note</strong>: A <code>key</code> prop was passed in the <code>Routes</code> component which React uses to render the correct component when the location changes.</p>
<p>Finally, you clear the <code>App</code> component of all the routing logic you defined earlier and replace it with the <code>RoutesWithAnimation</code> nested in the <code>LocationProvider</code><em>.</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">LocationProvider</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">RoutesWithAnimation</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">LocationProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>To confirm that you’re keeping track of the route changes, log the <code>location</code> value to the console and toggle between routes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Confirming-that-react-is-tracking-that-route-changes.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As you can see, each time you change the route, the route path is logged to the console, along with a unique <code>key</code> property.</p>
<h2 id="heading-how-to-animate-routes-with-framer">How to Animate Routes with Framer</h2>
<p>To animate anything in Framer, you need to specify the following.</p>
<ul>
<li>Variants: Variants are a way to define and animate the properties of a component. A variant is an object that contains one or more named sets of properties, where each set represents a different animation state. To create a variant for your routes, you first define a variant object:</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> routeVariants = {}
</code></pre>
<ul>
<li>Initial animation state: In your variant, you specify the initial state of the animation by creating an initial animation object. For this project, the animation you’re creating involves each component sliding in from the bottom (y-axis) of the page. To do this, you initially translate the entire component out of the viewport:</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> routeVariants = {
    <span class="hljs-attr">initial</span>: {
        <span class="hljs-attr">y</span>: <span class="hljs-string">'100vh'</span>
    }
}
</code></pre>
<ul>
<li>Final animation state: Next, you specify the animation when the component is mounted by specifying a final animation state:</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> routeVariants = {
    <span class="hljs-attr">initial</span>: {
        <span class="hljs-attr">y</span>: <span class="hljs-string">'100vh'</span>
    }
    <span class="hljs-attr">final</span>: {
        <span class="hljs-attr">y</span>: <span class="hljs-string">'0vh'</span>
    }
}
</code></pre>
<p>In order to apply these new animation properties to your components, you first make each component a <em>motion</em> element by prefixing the <code>motion</code> keyword to it.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"home component"</span>&gt;</span>  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Home Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"about component"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> About Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"contact component"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Contact Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>;
}
</code></pre>
<p>Then, you pass in the <code>variants</code> object and each of the variants states to each component you wish to animate. The <code>initial</code> state is the unmounted state of the component and the <code>animate</code> state is the mounted state of the component.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">variants</span>=<span class="hljs-string">{routeVariants}</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span>
      <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"home component"</span>
    &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Home Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">variants</span>=<span class="hljs-string">{routeVariants}</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span>
      <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"about component"</span>
    &gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>  About Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">variants</span>=<span class="hljs-string">{routeVariants}</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span>
      <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"contact component"</span>
    &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Contact Component <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}
</code></pre>
<p>With this, your animation should already be working.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Initail-Routing-achieved-with-bouncy-effect.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Initial routing animation with extra bouncy effect achieved</em></p>
<p>And voilà! You’ve successfully animated the routes in your React app. Kudos! 🚀</p>
<p>One thing you’d notice is how bouncy our transition is. It slightly spills into the header component when entering the page. This is because the default animation type in Framer is spring which behaves as such. </p>
<p>To reduce the effect, you can simply specify a <code>mass</code> property on the final animation state.</p>
<pre><code class="lang-js">final: {
    <span class="hljs-attr">y</span>: <span class="hljs-string">"0vh"</span>,
    <span class="hljs-attr">transition</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-string">"spring"</span>,
      <span class="hljs-attr">mass</span>: <span class="hljs-number">0.4</span>,
    },
  },
</code></pre>
<p>This property specifies the mass of the animated component. An increase in value of the mass of the animated component results in a more bouncy effect and vice versa.</p>
<p><strong>Note</strong>: The mass value is usually kept between 0 and 1. (0 being no springiness and 1 being a lot of springiness). Setting your animated mass to 0.4 yields the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Initail-Routing-achieved.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Extra routing bouncy effect solved</em></p>
<h3 id="heading-children-animations">Children Animations</h3>
<p>We can take this even further by animating the route separately from the content of that route.</p>
<p>Start by creating a child variants object for the headings in each component.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> childVariants = {
  <span class="hljs-attr">initial</span>: {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">y</span>: <span class="hljs-string">"50px"</span>,
  },
  <span class="hljs-attr">final</span>: {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">y</span>: <span class="hljs-string">"0px"</span>,
    <span class="hljs-attr">transition</span>: {
      <span class="hljs-attr">duration</span>: <span class="hljs-number">0.5</span>,
      <span class="hljs-attr">delay</span>: <span class="hljs-number">0.5</span>,
    },
  },
};
</code></pre>
<p>The <code>childVariant</code> animates the children elements by moving them up 50px and making them visible by increasing the opacity. Finally, the delay makes that animation fire slightly after the parent component animation fires.</p>
<p>To make this animation effective, you make each h1 a motion element. After that, you define your variants and animation states in all the child elements you want to animate. Each h1 element should look something like this:</p>
<pre><code class="lang-js"> &lt;motion.h1 variants={childVariants} initial=<span class="hljs-string">"initial"</span> animate=<span class="hljs-string">"final"</span>&gt;
        <span class="hljs-comment">// Whatever component name was in here.</span>
&lt;/motion.h1&gt;
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Final-Routing-animation.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Final result with both routing and children animation achieved</em></p>
<p>And with that, you’ve implemented a pretty sleek routing animation with Framer, congratulations! 🎉</p>
<p>For reference, here’s the full code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> {
  BrowserRouter,
  NavLink,
  Route,
  Routes,
  useLocation,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">import</span> { motion, AnimatePresence } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;

<span class="hljs-keyword">const</span> routeVariants = {
  <span class="hljs-attr">initial</span>: {
    <span class="hljs-attr">y</span>: <span class="hljs-string">"100vh"</span>,
  },
  <span class="hljs-attr">final</span>: {
    <span class="hljs-attr">y</span>: <span class="hljs-string">"0vh"</span>,
    <span class="hljs-attr">transition</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-string">"spring"</span>,
      <span class="hljs-attr">mass</span>: <span class="hljs-number">0.4</span>,
    },
  },
};

<span class="hljs-keyword">const</span> childVariants = {
  <span class="hljs-attr">initial</span>: {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">y</span>: <span class="hljs-string">"50px"</span>,
  },
  <span class="hljs-attr">final</span>: {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">y</span>: <span class="hljs-string">"0px"</span>,
    <span class="hljs-attr">transition</span>: {
      <span class="hljs-attr">duration</span>: <span class="hljs-number">0.5</span>,
      <span class="hljs-attr">delay</span>: <span class="hljs-number">0.5</span>,
    },
  },
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">LocationProvider</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">RoutesWithAnimation</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">LocationProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">LocationProvider</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AnimatePresence</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">AnimatePresence</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RoutesWithAnimation</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> location = useLocation();
  <span class="hljs-built_in">console</span>.log(location);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Routes</span> <span class="hljs-attr">location</span>=<span class="hljs-string">{location}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{location.key}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">About</span> /&gt;</span>} /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Contact</span> /&gt;</span>} /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Header</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"header"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Header Component<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/contact"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">variants</span>=<span class="hljs-string">{routeVariants}</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span>
      <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"home component"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">motion.h1</span> <span class="hljs-attr">variants</span>=<span class="hljs-string">{childVariants}</span> <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span> <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>&gt;</span>
        Home Component
      <span class="hljs-tag">&lt;/<span class="hljs-name">motion.h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">variants</span>=<span class="hljs-string">{routeVariants}</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span>
      <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>

      <span class="hljs-attr">className</span>=<span class="hljs-string">"about component"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">motion.h1</span> <span class="hljs-attr">variants</span>=<span class="hljs-string">{childVariants}</span> <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span> <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>&gt;</span>
        About Component
      <span class="hljs-tag">&lt;/<span class="hljs-name">motion.h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">variants</span>=<span class="hljs-string">{routeVariants}</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span>
      <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>

      <span class="hljs-attr">className</span>=<span class="hljs-string">"contact component"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">motion.h1</span> <span class="hljs-attr">variants</span>=<span class="hljs-string">{childVariants}</span> <span class="hljs-attr">initial</span>=<span class="hljs-string">"initial"</span> <span class="hljs-attr">animate</span>=<span class="hljs-string">"final"</span>&gt;</span>
        Contact Component
      <span class="hljs-tag">&lt;/<span class="hljs-name">motion.h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here’s a link to the repository: <a target="_blank" href="https://github.com/Daiveedjay/Framer-Articl">GitHub</a></p>
<p>And the live version: <a target="_blank" href="https://react-framer-article.netlify.app/">Netlify</a></p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>I’ve got to say, I had lots of fun writing this article and building this animation, and I hope you did too. (I also refreshed one too many times because I liked the animation effect 😉). </p>
<p>I was really motivated to put this article out there because when I learnt Framer motion a few weeks ago, I struggled to find up-to-date resources to teach me how to use it, especially ones that implemented it with the latest version of both tools (react-router v6 and Framer motion 10). So I hope this article provides a reference to a much more recent approach to routing animations with Framer.</p>
<h3 id="heading-resources">Resources</h3>
<p>To learn more about framer motion and the react-router, here are a couple of resources I’d recommend</p>
<ul>
<li><a target="_blank" href="https://www.youtube.com/watch?v=2V1WK-3HQNk&amp;list=PL4cUxeGkcC9iHDnQfTHEVVceOEBsOf07i">Framer Motion (For React)</a></li>
<li><a target="_blank" href="https://www.framer.com/motion/">Framer motion Docs</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=OMQ2QARHPo0&amp;list=PL4cUxeGkcC9iVKmtNuCeIswnQ97in2GGf">React Router In-depth</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=k2Zk5cbiZhg">React Router V6</a></li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, animating routes in a React application using Framer Motion can enhance the user experience by creating smooth and seamless transitions between different pages. </p>
<p>By incorporating components like <code>AnimatePresence</code>, <code>motion</code>, and <code>variants</code>, you can customize your app's animations, making it more engaging and visually appealing. </p>
<p>Implementing animations can improve the overall flow and navigation of your app, creating a more enjoyable and responsive experience for your users.</p>
<h3 id="heading-contact-information">Contact Information</h3>
<p>Want to connect or contact me? Feel free to hit me up on the following:</p>
<ul>
<li>Twitter: <a target="_blank" href="https://twitter.com/JajaDavid8">@jajadavid8</a></li>
<li>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/david-jaja-8084251b4/">David Jaja</a></li>
<li>Email: Jajadavidjid@gmail.com</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ UX vs UI – What's the Difference? Definition and Meaning ]]>
                </title>
                <description>
                    <![CDATA[ When designing a website or an app, the finished product needs to be functional, intuitive, and aesthetically pleasing. This is where UX and UI design come in. UX and UI design are two technical terms that can often be confusing. Many people may use ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/ux-vs-ui-whats-the-difference-definition-and-meaning/</link>
                <guid isPermaLink="false">66b1e4c841fdb67461b85280</guid>
                
                    <category>
                        <![CDATA[ UI Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI UX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ User Interface ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Dionysia Lemonaki ]]>
                </dc:creator>
                <pubDate>Fri, 14 Oct 2022 16:15:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/10/pexels-picjumbocom-196644.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When designing a website or an app, the finished product needs to be functional, intuitive, and aesthetically pleasing. This is where UX and UI design come in.</p>
<p>UX and UI design are two technical terms that can often be confusing.</p>
<p>Many people may use these two terms interchangeably, incorrectly, or assume they are the same thing. But that is not the case, as they are two different fields.</p>
<p>In a nutshell, UX design focuses on how well the product <em>works</em> and how the user <em>feels</em> when using it, whereas UI design focuses on how that software product <em>looks</em>. </p>
<p>Both are equally important and play a vital role when creating and developing software. </p>
<p>Think of it like this: a pretty UI may attract visitors, but if the product is not functional, the visitors won't stick around. And if the product works properly but doesn't look good, users will feel frustrated and look elsewhere to satisfy their needs.</p>
<p>There needs to be a balance.</p>
<p>With that said, the definition above only scratches the surface.</p>
<p>In this article, you will learn about the differences between UX and UI design and understand what UX and UI designers do in their day-to-day work.</p>
<p>Here is what we will cover:</p>
<ol>
<li><a class="post-section-overview" href="#ux-definition">What is UX design and why is it important?</a></li>
<li><a class="post-section-overview" href="#ux-job">What does a UX designer actually do? The UX design process explained</a><ol>
<li><a class="post-section-overview" href="#phase-1">Understanding phase</a></li>
<li><a class="post-section-overview" href="#phase-2">User research phase</a></li>
<li><a class="post-section-overview" href="#phase-3">Analyze and define phase</a></li>
<li><a class="post-section-overview" href="#phase-4">Ideate and design phase</a></li>
<li><a class="post-section-overview" href="#phase-5">Reporting phase</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#ui-intro">What is UI design?</a><ol>
<li><a class="post-section-overview" href="#ui-skills">What does a UI designer do?</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#differences">UX VS UI design - what's the difference?</a></li>
</ol>
<h2 id="heading-what-is-ux-design-and-why-is-it-important">What Is UX Design and Why Is It Important? <a></a></h2>
<p>UX design is short for user experience design.</p>
<p>The term was coined and popularized in the early 1990s by Donald Norman, an American researcher, professor, and author, during his time at Apple.</p>
<p>According to Norman:</p>
<blockquote>
<p>User experience encompasses all aspects of the end-user’s interaction with the company, its services, and its products. </p>
</blockquote>
<p>Note that the original definition of UX referred to physical and digital products. Nowadays, UX design mainly relates to digital products. </p>
<p>So, user experience design is a process that involves cultivating a good relationship between a company, the company's software products /services, and the company's clients/customers.</p>
<p>The relationship is based on how a user feels when using the company's products.</p>
<p>UX has to do with feelings, emotions, thoughts, behaviors, and human psychology.</p>
<p>Specifically, good UX depends on the following points:</p>
<ul>
<li>How does a person feel when using the product?</li>
<li>How accessible is it for the person to navigate the product? Can everyone use it?</li>
<li>How usable is the product? Does it work as intended?</li>
<li>How easy is it for the person to navigate and find the necessary information?</li>
<li>How well-planned and intuitive are the steps a user needs to take to accomplish a task?</li>
<li>How logical is the sequence of actions a user needs to perform to accomplish their goal with ease?</li>
<li>Does it make it easy for them to achieve their initial goal?</li>
<li>How well structured is the content available? Does it follow a logical hierarchy?</li>
<li>How useful is the product?</li>
<li>How valuable is the product?</li>
<li>Does it serve a purpose?</li>
</ul>
<p>All of the above are part of what makes good UX design and contribute to the value the software offers.</p>
<p>UX is all about solving problems and always keeping the user in mind.</p>
<p>The end goal is to provide a pleasant and positive experience for the user, create digital products that a user feels comfortable using, and for the user to accomplish their task and have their needs met.</p>
<p>All in all, it focuses on anything and everything that affects the user's journey when trying to solve a problem they have.</p>
<p>Good user experience is vital for every business. If the user/customer is happy and not frustrated, they are more likely to remain loyal.</p>
<p>If users can't use the product with ease, this will result in them giving up on it and looking for an alternative product/service that meets their needs, resulting in a loss for the business. They are unlikely to return to a product after a bad experience.</p>
<h2 id="heading-what-does-a-ux-designer-actually-do-the-ux-design-process-explained">What Does A UX Designer Actually Do? The UX Design Process Explained <a></a></h2>
<p>The UX design process is comprised of many parts. It is a sequential process with the end user always in mind throughout all stages.</p>
<p>The role of a UX designer will vary from business to business and depends on the size of the team they are on. </p>
<p>On a small team, a UX designer will probably be responsible for the whole UX process from start to finish. On a larger team, they might be involved in only one part of the process, such as UX research.</p>
<p>That means a UX designer may not be part of all the stages outlined in the following sections.</p>
<p>Now, let's see the different phases in the UX design process in more detail.</p>
<h3 id="heading-understanding-phase">Understanding Phase <a></a></h3>
<p>The first step in the UX design process is understanding the brand and its users.</p>
<p>Specifically, it is about understanding how the project the UX designer is working on aligns with the brand's mission, vision, and goals. </p>
<p>And it is about understanding the brand's users/customers. </p>
<p>A UX designer can't make assumptions about what the users really want or what problems they are facing when using the product/service. They will only know once they ask the users directly.</p>
<p>The UX designer needs to understand, be in tune with, and empathize with the problems users are facing and their pain points. They need to understand their users' wants and needs.</p>
<p>The key word here is <a target="_blank" href="https://www.freecodecamp.org/news/how-empathy-makes-you-a-better-software-engineer/">empathy</a>! Empathy is the ability to understand the user's hopes, goals, frustrations, and needs. </p>
<p>Once the UX designer has gathered those insights and identified the goals of the brand and the problems the users are facing, they can figure out how to solve problems and provide solutions through their designs and how they can create products/services that solve those problems.</p>
<h3 id="heading-user-research-phase">User Research Phase <a></a></h3>
<p>As mentioned in the section above, a UX designer can't assume, and if they do, they need to question those assumptions by conducting user research.</p>
<p>There are two main methods for conducting user research:</p>
<ul>
<li>Qualitative methods</li>
<li>Quantitative methods</li>
</ul>
<p>Qualitative research methods focus on observational and non-numerical findings, human behaviors, opinions, and emotions. </p>
<p>They include creating customer feedback surveys and asking open-ended questions to understand the key frustrations and pain points and what needs improving.</p>
<p>They also include reaching out to customers directly and conducting 1:1 face-to-face or online user interviews or focus groups of 3-5 target users for usability testing.</p>
<p>Quantitative methods focus on numbers, metrics, and measurable data points. </p>
<p>They include using web analytics tools and researching how the current design is performing. For example, they might find out how many visitors of an e-commerce site are clicking on a particular item and adding it to their cart, or what is the average time spent on the checkout page.</p>
<p>To learn more about user research, check out the following resources:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/why-you-need-a-ux-researcher-on-your-product-team/">Why you need a UX researcher on your product team</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/use-user-reseach-to-create-the-perfect-ui-design/">Use User Research to Create the Perfect UI Design</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/qualitative-vs-quantitative-definition-research-methods-and-data/">Qualitative VS Quantitative Definition – Research Methods and Data</a></li>
</ul>
<h3 id="heading-analyze-and-define-phase">Analyze and Define Phase <a></a></h3>
<p>After gathering user insights, the next phase in the UX design process is for the UX designer to analyze and make sense of all the information and define the problem they need to solve for the user. </p>
<p>They define what their focus should be and what would have the most impact on the users.</p>
<p>At this stage, it is crucial not to lose sight of the user's needs.</p>
<p>For this reason, the UX designer will create user personas based on the research they've gathered.</p>
<p>A user persona is a fictional character that will use the product/service. </p>
<p>It represents a typical target user and identifies their needs, frustrations, behaviors, and goals.</p>
<p>User personas allow UX designers to understand their users better and make wise design decisions.</p>
<p>At this stage, a UX designer might also create use cases. </p>
<p>A use case is a description of how different users might use and interact with the product or service.</p>
<p>Finally, a UX designer might also create journey maps.</p>
<p>A journey map is a visual and graphic representation of how the user will interact with the product/service from start to finish, and a map depicting the whole user journey until the user accomplishes their goal. </p>
<p>Journey maps are useful for understanding the different pain points a user faces when using the product and where those pain points occur, which leads to restructuring the product/service.</p>
<p>To learn more about user personas and use cases, check out the following resources:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-user-stories-epics-pesonas/">How To Write User Stories, Epics, &amp; Personas</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/use-cases-and-organizational-structure/">Use cases and organizational structure</a></li>
</ul>
<h3 id="heading-ideate-and-design-phase">Ideate and Design Phase <a></a></h3>
<p>The ideate phase is all about brainstorming and exploring out-of-the-box design ideas.</p>
<p>At this stage, the UX designer will come up with different creative options for how to solve the problem at hand.</p>
<p>They will brainstorm ideas and sketch them on paper before using any digital tools.</p>
<p>They will also create wireframes. Wireframes are rough prototypes and visual representations of what the UI and its various components will look like and which act as a guide and direction for the final design.</p>
<p>They will outline the steps and the complete path a user will take while using the product/service.</p>
<p>The end goal of this phase of the UX design process is to design low or high-fidelity versions of the structure and layout of the product (which may or may not be interactive) and of what the final design will look like. The first design is never the final design.</p>
<p>After continuously iterating the designs, adding new ideas and features,  rigorous testing, and feedback, the UX designer will have a detailed and carefully crafted digital mockup.</p>
<p>The mockup will look almost identical to the final product, which they hand off to the engineering team. The engineering team will build and ship the real-world working product.</p>
<p>To learn more about creating wireframes, check out the following resources:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/what-is-a-wireframe-ux-design-tutorial-website/">What is a Wireframe? This UX Design Tutorial Will Show You.</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/designing-a-website-ui-with-prototyping/">How to Design a Website Prototype from a Wireframe</a></li>
</ul>
<h3 id="heading-reporting-phase">Reporting Phase <a></a></h3>
<p>After the product launches, the next phase of the UX design process is the reporting phase. This includes conducting usability testing, observing how users are using the end product, collecting feedback, and gaining additional insights.</p>
<p>This phase helps the UX designer and their teams understand if the end product solves the initial problem and if it meets the needs of their end users.</p>
<p>At this phase, here are some of the questions that a UX designer may ask:</p>
<ul>
<li>"What did we do right?"</li>
<li>"What <em>didn't</em> we do right, and why?" </li>
<li>"What did we learn?"</li>
<li>"How can we improve in the future?"</li>
<li>"How are users responding to the product/service? Did we solve their problems and pain points?"</li>
</ul>
<p>With that said, the UX design process doesn't end here, since there are continuous iterations and improvements to be made.</p>
<h2 id="heading-what-is-ui-design-and-why-its-important-an-overview-of-ui-design">What Is UI Design and Why It's Important? An Overview of UI Design   <a></a></h2>
<p>So far, you have seen that UX design involves every aspect of a user’s experience when using a product. So what exactly is UI design, then?</p>
<p>Back in the 1970s, to use a computer, you had to use a command line interface (or CLI for short), and users needed to know a programming language to communicate with the machine. </p>
<p>At that time, you needed a lot of technical knowledge and skills to complete a simple task with a computer.</p>
<p>That changed in the 1980s with the introduction of Graphical User Interfaces (or GUIs for short) and personal computers. </p>
<p>A mouse, application icons, buttons, menus, checkboxes, and folders replaced the cryptic command line. Anyone could use a computer. Users could easily create, update, move, and delete files and send emails – no coding required.</p>
<p>The people that created these early graphical user interfaces were called User Interface (or UI for short) designers.</p>
<p>Nowadays, the term user interface involves every visual part of a digital product/service that a user interacts with. This includes mobile apps, websites, screens, touchscreens, keyboards, and wearable technology such as smartwatches, to name a few.</p>
<p>You can think of it as the bridge between the user and technology.</p>
<p>A UI designer is responsible for designing every step that allows a user to interact with the digital product/service. This includes layouts, structure, buttons, colors, and animations.</p>
<p>UI design is all about the <em>look,</em> <em>feel</em>, and <em>aesthetics</em> of a digital product. It involves every visual aspect and appearance of the product the user interacts with.</p>
<p>The end goal of a UI designer is to make something visually appealing to the user that's easy to interact with.</p>
<p>Good UI is vital for drawing attention to potential customers and attracting new users. Everyone appreciates a well-designed, clean, intuitive, and modern design.</p>
<p>But how does all this relate to UX design? You can think of UI design as a subset of UX design and a small part of the UX design process. But it is a separate discipline in its own right. </p>
<h3 id="heading-what-does-a-ui-designer-do-ui-design-skills-definition-and-examples">What Does A UI Designer Do? UI Design Skills Definition and Examples <a></a></h3>
<p>UI designers need to have visual design skills.</p>
<p>Specifically, they need to know the following concepts and disciplines:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-increase-user-engagement-with-your-app-using-color-theory-7c6f5c632570/">Color theory</a>, creating color palettes and creating accessible contrast that ensures both usability and readability.</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-design-good-typography/">Typography</a>.</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-layout-and-design-a-website-without-any-design-skills-86d94e40b55a/">Layout</a>, spacing, structure, and effective placement of elements on a page.</li>
<li>Graphics, icons, and illustration design.</li>
<li>Interaction design principles, animation effects, and motion design.</li>
<li>Branding – creating a consistent visual identity for a brand/product and communicating the brand's mission and message.</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/master-responsive-website-design/">Responsive design</a>, and ensuring the UI looks good on all screen sizes and devices.</li>
<li>Creating <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-style-guide-in-figma/">style guides</a>.</li>
<li>Creating <a target="_blank" href="https://www.freecodecamp.org/news/learn-how-to-create-a-design-system-in-figma/">design systems</a>.</li>
<li>Knowledge of industry tools such as <a target="_blank" href="https://www.freecodecamp.org/news/ui-design-with-figma-tutorial/">Figma</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/ui-ux-design-tutorial-from-zero-to-hero-with-wireframe-prototype-figma/">Wireframing and prototyping</a>.</li>
</ul>
<h2 id="heading-ux-vs-ui-design-whats-the-difference-the-difference-between-ux-and-ui-design-explained-in-plain-english">UX VS UI Design – What's the Difference? The Difference Between UX and UI Design Explained in Plain English <a></a></h2>
<p>By this point, you hopefully understand what UX and UI design involves.</p>
<p>To summarize their differences:</p>
<p>UX design is how things <em>work</em> and how <em>useful</em> they are. UX design focuses on the whole experience and the journey a user takes when using a product from start to finish. It involves the overall experience a user has when trying to solve a problem. </p>
<p>UX requires knowledge of psychology, cognitive science, human behavior, identifying the user's pain points, and solving their problems.</p>
<p>UI design is how things <em>look</em>. UI design is a subset of UX design and is a more specialized field. It focuses only on the visual aspects that a user interacts with and creates inclusive, accessible, pleasant, and aesthetically pleasing digital interfaces. </p>
<p>UI design also focuses on creating a brand aesthetic that leads to an emotional connection with users/customers.</p>
<p>Both UX and UI design are equally important in creating useful, usable, and modern digital products and services and meeting the needs of their users.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This marks the end of the article – thank you so much for making it to the end!</p>
<p>Hopefully, this guide was helpful, and it gave you some insight into what UX and UI design are, what UX and UI designers do in their day-to-day work, and what the differences between those two terms are.</p>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Modal with JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ By Victor Eke It's probably happened to you before: you unintentionally attempted to perform an action on a webpage, but luckily got a pop-up window asking you to confirm your decision.  This pop-up window is called a modal. It's a web page element t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-modal-with-javascript/</link>
                <guid isPermaLink="false">66d46174b3016bf139028d9c</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 03 Oct 2022 23:49:06 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/10/How-to-build-a-modal-with-JavaScript.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Victor Eke</p>
<p>It's probably happened to you before: you unintentionally attempted to perform an action on a webpage, but luckily got a pop-up window asking you to confirm your decision. </p>
<p>This pop-up window is called a modal. It's a web page element that pops up and displays in front of other page content. </p>
<p>You can use modals for doing things like storing information you don't want to immediately see on a webpage, creating navigation menus, adding call-to-action elements, and more.</p>
<p>An excellent example is the modal that appears on Twitter when you attempt to close the compose tweet menu.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/twitter_warning_message_modal.png" alt="Twitter warning modal window" width="600" height="400" loading="lazy"></p>
<p>You can also use modals for other things like creating call-to-action elements, navigation menus, newsletter widgets, and more. </p>
<p>As a web developer, knowing how to build a modal can be an handy skill to have. In this tutorial, I'll walk you through the process of how you can create a simple modal using HTML, CSS, and JavaScript.</p>
<p>Here's a screenshot of what we’ll be building:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/modal.png" alt="A modal built with html, css and javascript" width="600" height="400" loading="lazy"></p>
<p>The steps are very easy to follow so you can customize the modal or build your own from scratch – it’s entirely up to you. At the end of this article, I'll provide the codepen file so you can play around with it.</p>
<h2 id="heading-step-1-add-the-markup">Step 1 – Add the Markup</h2>
<p>Alright, let's get started with the HTML.</p>
<p>First, you'll add a <code>section</code> element and give it two classes, <code>modal</code> and <code>hidden.</code> Under this element, you’ll also have a <code>&lt;div&gt;</code> element with a class of <code>overlay</code> and <code>hidden</code>. Then finally, you’ll add a <code>&lt;button&gt;</code> element with a class of <code>btn</code> and <code>btn-open.</code> </p>
<p>Here’s what that looks like:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"modal hidden"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"overlay hidden"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-open"</span>&gt;</span>Open Modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<ul>
<li>The section element with a class of modal will serve as your modal container.</li>
<li>The div with the class of <code>overlay</code> will serve as your overlay element. This is the dark blurred background you see when the modal is open.</li>
<li>The button with the class of <code>btn</code> and <code>btn-open</code> will serve as your modal button so it fires up our modal when you click this button.</li>
</ul>
<p>Now inside of your modal, add the markup, and also add the <code>X</code> button for closing the modal. This button will be assigned a <code>btn-close</code> class.</p>
<p>So here’s what your complete markup will look like at the end:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"modal hidden"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"user.png"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"user"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn-close"</span>&gt;</span>⨉<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Stay in touch<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      This is a dummy newsletter form so don't bother trying to test it. Not
      that I expect you to, anyways. :)
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"brendaneich@js.com"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"overlay hidden"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-open"</span>&gt;</span>Open Modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p><strong>Important</strong> ⚠️ Take note of the hidden class added to the modal and the overlay element. This is very important because you'll target these classes to hide your modal and overlay using CSS.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/complete-markup.png" alt="complete-markup" width="600" height="400" loading="lazy"></p>
<h2 id="heading-step-2-style-the-modal">Step 2 – Style the Modal</h2>
<p>Let's start by resetting the default margin and padding of every element on the page, and then center both the modal and open-modal button.</p>
<p>Now jump over to your CSS and add the following styles:</p>
<pre><code class="lang-css">* {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Inter"</span>, sans-serif;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#222</span>;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
}
</code></pre>
<p>The next step is styling the modal container itself and the elements inside the container. This process is a bit lenghty so I’ll just copy and paste the styling here and then explain it a bit after:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.modal</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">0.4rem</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">450px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.3rem</span>;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">250px</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">20%</span>;
  <span class="hljs-attribute">background-color</span>: white;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-class">.modal</span> <span class="hljs-selector-class">.flex</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
}

<span class="hljs-selector-class">.modal</span> <span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.7rem</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9em</span>;
}

<span class="hljs-selector-class">.modal</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#777</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.4rem</span> <span class="hljs-number">0</span> <span class="hljs-number">0.2rem</span>;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>;
}

<span class="hljs-selector-class">.btn</span> {
  <span class="hljs-attribute">display</span>: inline-block;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.8rem</span> <span class="hljs-number">1.4rem</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">background-color</span>: black;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1em</span>;
}

<span class="hljs-selector-class">.btn-open</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">150px</span>;
}

<span class="hljs-selector-class">.btn-close</span> {
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate</span>(<span class="hljs-number">10px</span>, -<span class="hljs-number">20px</span>);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">0.7rem</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#eee</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
}
</code></pre>
<p>And here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/complete-modal-style.png" alt="complete-modal-style" width="600" height="400" loading="lazy"></p>
<p>What you did was style the modal element and then position it using the absolute property. This works because you added a position relative property to the body element earlier. </p>
<p>You also styled the elements inside of the modal, but I won’t go deep into the details of that because that is not completely important to us here.</p>
<h2 id="heading-step-3-add-the-overlay">Step 3 – Add the Overlay</h2>
<p>For the overlay, you want to position it over the entire page with a subtle dark background and blur. </p>
<p>Since you have the position relative to the body element, you can use the position fixed property to add the overlay over the body element. You'll overlay it 100% of the viewport width and height.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.overlay</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>);
  <span class="hljs-attribute">backdrop-filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">3px</span>);
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">1</span>;
}
</code></pre>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/overlay.png" alt="overlay" width="600" height="400" loading="lazy"></p>
<p>The overlay works, but you only want it to affect the body element and not the modal. To fix this, add a higher <code>z-index</code> property to the modal container.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.modal</span> {
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">2</span>;
}
</code></pre>
<p>Now the modal should be on the overlay and not behind it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/modal-1.png" alt="modal" width="600" height="400" loading="lazy"></p>
<p>You've successfully created the modal and added an overlay behind it! But you don't want to show the modal, at least not until the <code>open-modal</code> button is clicked. </p>
<p>To hide it, you need to target the <code>.hidden</code> class you added earlier to the modal and overlay element in your CSS. You'll also give it a display of none.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hidden</span> {
  <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<p>Now only the button is showing on the page. You can now work on the modal functionality using JavaScript.</p>
<h2 id="heading-step-4-add-modal-functionality">Step 4 – Add Modal Functionality</h2>
<p>Before we proceed, I believe it is best to explain how the modal works. Remember how you used the <code>hidden</code> class to hide the modal and overlay? To add or remove this class from the elements, you'll use the DOM's classList element. </p>
<p>But first, you need to select your classes using the DOM's <code>querySelector</code> method and store them in variables so they are reusable.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> modal = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".modal"</span>);
<span class="hljs-keyword">const</span> overlay = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".overlay"</span>);
<span class="hljs-keyword">const</span> openModalBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".btn-open"</span>);
<span class="hljs-keyword">const</span> closeModalBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".btn-close"</span>);
</code></pre>
<h2 id="heading-how-to-open-the-modal">How to Open the Modal</h2>
<p>In other to show the modal, create a function called <code>openModal</code>. Inside this function, you'll use the DOM <code>classList</code> property which takes in different methods like <code>.remove()</code> and <code>.add()</code> to remove the <code>hidden</code> class from the <code>modal</code> and <code>overlay</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> openModal = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  modal.classList.remove(<span class="hljs-string">"hidden"</span>);
  overlay.classList.remove(<span class="hljs-string">"hidden"</span>);
};
</code></pre>
<p>And then you can use an <code>eventListener</code> to tie this function to the open modal button <code>openModalBtn</code>. That way, anytime this button is clicked, the function is executed, which shows the modal.</p>
<pre><code class="lang-js">openModalBtn.addEventListener(<span class="hljs-string">"click"</span>, openModal);
</code></pre>
<p>Now when you click on the <code>open modal</code> button, this will remove the <code>hidden</code> class from the modal element and you can see your modal.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/open-modal.gif" alt="Open modal" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-close-the-modal">How to Close the Modal</h2>
<p>For closing the modal, you’ll also create a function called <code>closeModal</code>. Inside the function, use the <code>.add()</code> method to add back the <code>hidden</code> class you removed.</p>
<p>The <code>classList</code> property also has an <code>add()</code> method which you'll use to add the hidden class back when you click the <code>closeModal</code> button. Just like you added an <code>eventListener</code> to the button to close the modal, you'll do the same to the <code>x</code> button – but this time, you'll add the <code>hidden</code> class back.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> closeModal = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  modal.classList.add(<span class="hljs-string">"hidden"</span>);
  overlay.classList.add(<span class="hljs-string">"hidden"</span>);
};
</code></pre>
<p>To close the modal, add an <code>eventListener</code> to the close modal button to execute the function you just wrote now.</p>
<pre><code class="lang-js">closeModalBtn.addEventListener(<span class="hljs-string">"click"</span>, closeModal);
</code></pre>
<p>Now when you click the close button, the function will add back the hidden class to the modal and overlay components, thus closing the modal.</p>
<p>Here's the output</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/close_modal.gif" alt="close modal" width="600" height="400" loading="lazy"></p>
<p>Usually, modals are also closed when you click outside of the modal container or on the body of the webpage. To do that, add an <code>eventListener</code> to close the modal when you click on the overlay.</p>
<pre><code class="lang-js">overlay.addEventListener(<span class="hljs-string">"click"</span>, closeModal);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/close_modal_when_overlay_is_clicked.gif" alt="close_modal_when_overlay_is_clicked" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-close-the-modal-on-key-press">How to Close the Modal on Key Press</h2>
<p>In addition to closing the modal when you click the close button or the overlay, you can also attach an event listener to watch for keyboard events. </p>
<p>In this instance, you want the modal to close when you press the <code>Escape</code> key, much like in the Twitter compose modal example. </p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>);
</code></pre>
<p>But this time the type of event you want is not the <code>“click”</code> event – you want to use the <code>“keydown”</code> event to execute your function.</p>
<p>Next up, you'll write a condition that checks if the current key pressed is the <code>Escape</code> key and the modal does not contain the <code>hidden</code> class. So it’s open, and you want to execute the <code>closeModal</code> function (in essence, close the modal).</p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">"Escape"</span> &amp;&amp; !modal.classList.contains(<span class="hljs-string">"hidden"</span>)) {
    modalClose();
  }
});
</code></pre>
<p>Now when the modal is open and you hit the <code>&lt;kbd&gt;Esc&lt;/kbd&gt;</code> key, it will close the modal.</p>
<p>And with this, you’ve successfully created a modal component with HTML, CSS, and JavaScript and it works just as intended. 🥳</p>
<p>Here’s the codepen file to test this modal in action:</p>
<div class="embed-wrapper"><iframe height="400" style="width:100%" src="https://codepen.io/evavic44/embed/zYjjzoV?default-tab=html%2Cresult&amp;theme-id=light" title="Embedded content" loading="lazy">
  See the Pen <a href="https://codepen.io/evavic44/pen/zYjjzoV">
  Modal with overlay and blur</a> by Eke (<a href="https://codepen.io/evavic44">@evavic44</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe></div>

<h2 id="heading-conclusion">Conclusion</h2>
<p>I sincerely hope you found this post interesting or useful. If you did, kindly share with your friends or subscribe to my blog so you won't miss any future postings. Thanks for reading.</p>
<p><a target="_blank" href="https://github.com/evavic44">GitHub</a> | <a target="_blank" href="https://twitter.com/victorekea">Twitter</a> | <a target="_blank" href="https://eke.hashnode.dev">Blog</a> | <a target="_blank" href="https://victoreke.com">Portfolio</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Override Root Font Size to Create a Better User Experience ]]>
                </title>
                <description>
                    <![CDATA[ By Damla Erkiner Back in the day, web developers, front-end engineers, and UI designers had a bit of an easier job. This is because they weren't expected to rearrange their code and designs to fit so many different screen sizes.  But in today's world... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/override-root-font-size-for-a-better-user-experience/</link>
                <guid isPermaLink="false">66d45e03052ad259f07e4a83</guid>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 23 Aug 2022 15:14:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/root-font-size-article-image.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Damla Erkiner</p>
<p>Back in the day, web developers, front-end engineers, and UI designers had a bit of an easier job. This is because they weren't expected to rearrange their code and designs to fit so many different screen sizes. </p>
<p>But in today's world, if you make a decision that doesn't take responsive design techniques into consideration it can be doomed to failure. </p>
<p>After all, no one wants to visit a web site to stare at weird shapes, distorted images, and illegible text. </p>
<p>People's time is precious and limited, particularly in today's fast-paced world. A poor user experience in the form of non-responsive web design can really harm a business or brand in the blink of an eye. </p>
<p>This is why every developer should treat their products and websites like gemstones in their portfolio. These are literally a part of your personal brand and you don't want to ruin it with bad designs.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/responsive-webdesign-vs-non-responsive-webdesign.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.fortyone.io/en/responsive-webdesign.html">Illustration by Forty One</a></em></p>
<p>If you want to guarantee a hassle-free user experience, as a web developer or designer you're supposed to adopt certain practices that require you to play around with the layout. In this way, every single page you compose looks sophisticated and is user-friendly regardless of the screen size. </p>
<p>This process of creating responsive designs is deeply connected to <a target="_blank" href="https://www.madcapsoftware.com/blog/content-architecture-what-it-is-and-why-its-important/#:~:text=What%20is%20Content%20Architecture%3F,can%20affect%20the%20content%20architecture.">content architecture</a>. </p>
<p>There are various techniques you can use to make your website's potential users smile – or at least not feel frustrated – when they look at their screens. </p>
<p>Those may involve using media queries, CSS Flexbox, or CSS Grid. That being said, this article will primarily consider a method that is popular among experienced web developers. </p>
<h2 id="heading-why-should-you-adjust-the-root-font-size">Why Should You Adjust the Root Font Size?</h2>
<p>In a nutshell, this technique targets the root font-size and it also has to do with why you choose 'rem' units with this set-up. If you're already curious about it, let's dive right into it.</p>
<p>First and foremost, you should know that the standard root font-size of browsers is 16px. This fixed value is in 'px' units by default. But 'rem' units compared to 'px' are capable of creating more responsive websites. You can read more about that in <a target="_blank" href="https://www.aleksandrhovhannisyan.com/blog/use-rems-for-font-size/">this article</a>. </p>
<p>So if we try to convert px values into rem, we'll need to do some math. Suppose we want to set the font-size of an element to 4px, but we also want it to be as responsive as possible. To turn it into a 'rem' value, we need to divide it into 16px and the result will be 0.25rem. <a target="_blank" href="https://nekocalc.com/px-to-rem-converter">An online converter</a> exists for this purpose as well.</p>
<p>Everything would be fine and dandy if we had a way to turn every 'px' value into a 'rem' figure more easily, now that rem is considered to be more responsive. Here is how our dreams can come true.</p>
<p>In lieu of dealing with such cumbersome calculations, we can set the root (HTML) font-size to 62.5%. In this case, you can make all the other calculations automatically through the system. </p>
<h2 id="heading-how-to-change-root-font-size">How to Change Root Font Size</h2>
<p>Let's delve into the details to better grasp the situation. When the root font/HTML size is 100%, the font-size is 16px by default. But, if you set it to 62.5%, the new root value will be 10px. </p>
<p>Now, 10px (1rem) is way too tiny and is a recipe for disaster in terms of good user experience. So we should set it to 1.6rem (16x) again in the body. Everything looks the same on the surface. But still, this time, the new rem set-up will be more responsive than that of the 'px' version.</p>
<p>Now we're able to translate all the values into rem units. The tricky bit here is the root size calculation in the HTML section. Now, whatever size we pick for the body will be re-shaped in accordance with the pre-defined value in the root/HTML. To put it simply, the new percentage value '62.5%' in the root/html section ensures the smooth transformation of 'px' into 'rem'.</p>
<h2 id="heading-why-we-prefer-the-percentage-expression">Why We Prefer the 'Percentage' Expression</h2>
<p>At this point, you might be asking yourself why we opt for the percentage expression (62.5%) along with 'rem' units. Why bother? Isn't the 'px' version supposed to be the same? Well, not so much. </p>
<p>As <a target="_blank" href="https://www.quora.com/Why-is-the-CSS-technique-html-font-size-62-5-used-to-set-the-base-font-size-to-10px-Why-not-just-simply-say-html-font-size-10px">some developers on Quora</a> suggest, percentage-based values can scale more smoothly compared to fixed numbers. This means that not only are rem units important, but also the percentage preference in the root/HTML part matters a lot in this particular set-up.</p>
<p>The most effective part regarding the figure '62.5%' in the root is that even if the visitor alters the font size of the browser, they'll be able to view the page properly thanks to the related CSS set-up. </p>
<p>Also, it poses no threats in terms of accessibility because we also set the font-size of the body as 1.6rem in addition to the 62.5% arrangement in the root/HTML section. This way, we ensure that those two numbers go hand in hand no matter what the screen size is.</p>
<h2 id="heading-why-accessibility-is-important">Why Accessibility is Important</h2>
<p>It's important to elaborate on the accessibility issue. I believe it should be of the utmost importance to web developers and designers from all walks of life.</p>
<p><a target="_blank" href="https://websitesetup.org/websites-for-visually-impaired/">Research</a> shows that the number of visually impaired individuals is gradually on the rise, and there are many reasons for this. But one thing is for sure: in an era of cutting-edge technological advancements, everyone should be able to enjoy the internet and access information through certain accessibility measures. And applying proper responsive design techniques is definitely one of those. </p>
<h2 id="heading-what-is-bruce-lawsons-significant-message">What is Bruce Lawson's Significant Message?</h2>
<p>I remember watching <a target="_blank" href="https://www.youtube.com/watch?v=tgXbbOirY8o">an excellent talk by Bruce Lawson</a>, an expert when it comes to web standards. During a virtual free bootcamp organised by <a target="_blank" href="https://www.classcentral.com/">Class Central</a> and freeCodeCamp last year, he was a guest speaker. He explained very clearly why we as web developers are responsible for keeping everyone included and making sure that no one else is left out when coding and/or designing a website.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/tgXbbOirY8o" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-shall-we-test-our-new-set-up">Shall We Test Our New Set-up?</h2>
<p>Let's be more specific and come up with an imaginary scenario. Suppose a visually-impaired user wants to visit the webpage you've designed. To be able to see everything more clearly, they decide to make some adjustments t0 the browser's font-size in advance. For example they might set it to 18px, a bit bigger than the standard size (16px). </p>
<p>It is now time to examine the following code snippet closely to witness the functionality of the HTML and body-oriented set-up from the perspective of this individual.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">62.5%</span>;
}
<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.6rem</span>;
}
</code></pre>
<p>As the developer of this webpage, you chose '62.5%' for the HTML and '1.6rem' for the body as the initial font size. But remember that the browser's font-size determined by the above user is now 18px, not 16px anymore. How is it going to work out? Will this person be upset and leave the page feeling frustrated or will they keep surfing the webpage without trouble?</p>
<p>Here's the answer. Once the browser's font-size is chosen as 18px by the user, the font-size will instantly be re-calculated as 11.25px (18px <em> 62.5%) by the system. As a result, the value for the body will be 18px (1.6rem </em> 11.25px) just the way it is requested by that specific user. So this person will not be negatively affected by the situation just because they wish to see the font-size bigger than the standard version. </p>
<p>The good news is that all of these will be re-calculated automatically. And thanks to the percentage and rem-oriented set-up, the text on the webpage will be more responsive and user-friendly. </p>
<h2 id="heading-more-experiments">More Experiments</h2>
<p>To further see the possible effects of this setup, let's now take a look at how the font-sizes of the following elements/main containers will work out for our user.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">62.5%</span>;
}
<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.6rem</span>;
}
<span class="hljs-selector-tag">header</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3rem</span>;
}
<span class="hljs-selector-tag">section</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5rem</span>;
}
<span class="hljs-selector-tag">footer</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.8rem</span>;
}
</code></pre>
<p>In line with the new HTML size (62.5% <em> 18px = 11.25px), the re-calculated value of the header element's font-size will be 33.75px (3rem </em> 11.25px ). The one for the section element will roughly be 28px (2.5rem <em> 11.25px). And finally, the font-size of the footer will be 31.5px (11.25px </em> 2.8rem) from the perspective of our imaginary user. </p>
<p>In other words, with the new arrangement involving the font-size of the HTML and body, everything else will be handled smoothly under the hood without you as the developer having to perform separate calculations for every single element.</p>
<h2 id="heading-is-this-method-bullet-proof">Is This Method Bullet-Proof?</h2>
<p>Despite the fact that the method that uses the root size '62.5%', since it's specifically a percentage value and the rem-based preference in the body section gives us a chance to play around with the general font-size dynamically, it is also not risk-free and you should use it cautiously (see <a target="_blank" href="https://www.joshwcomeau.com/css/surprising-truth-about-pixels-and-accessibility/">this article</a> for more info). </p>
<p>For instance, it may lead to <a target="_blank" href="https://css-tricks.com/forums/topic/62-5-font-size/">some problems</a> when 'em' values are preferred instead of 'rem' values. Plus, this technique only resizes text. So you'll need to use some other tricks (for example, when dealing with the size of the images). </p>
<p>That said, overriding the root font-size is still a widespread practice preferred by <a target="_blank" href="https://www.aleksandrhovhannisyan.com/blog/62-5-percent-font-size-trick/">many developers</a> around the world and it can be handy if used carefully. </p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>All in all, the concepts of accessibility, responsive web design, maintainable and scalable code, and web performance are fundamental in creating a solid user experience. </p>
<p>Perhaps in the future, someone will come up with a better way to handle this. I just wanted to share with you the pros and cons of adjusting the root font size. </p>
<p>Even if you are not planning to use it at all, one of your teammates might go for it. So it is always a good idea to be aware of the arguments for and against it.</p>
<p>Thank you for reading. If you've liked this article, one of the best ways to support me is to share it. Should you have any questions or comments, you can always contact me via <a target="_blank" href="https://www.linkedin.com/in/damla-erkiner-000b76227/">LinkedIn</a>. I'll be more than happy to help you out with your queries.</p>
<p>Happy coding!</p>
<p><strong>“Knowledge is power.” – Francis Bacon</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Skeleton Loader Example – How to Build a Skeleton Screen with CSS for Better UX ]]>
                </title>
                <description>
                    <![CDATA[ Content loaders, skeleton screens, ghost elements, and content placeholders. These are the names given to the effect we'll be exploring today. Many companies, such as Linkedin, Facebook, Youtube and Slack, use this effect in their apps and websites, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-skeleton-screens-using-css-for-better-user-experience/</link>
                <guid isPermaLink="false">66d45f313dce891ac3a96804</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ user experience ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Israel Oyetunji ]]>
                </dc:creator>
                <pubDate>Mon, 25 Apr 2022 13:49:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/Build-Skeleton-Screens-for-Better-UX.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Content loaders, skeleton screens, ghost elements, and content placeholders. These are the names given to the effect we'll be exploring today.</p>
<p>Many companies, such as Linkedin, Facebook, Youtube and Slack, use this effect in their apps and websites, as you may have noticed.</p>
<p>As much as we developers want our websites to load as quickly as possible, there are times when a lot of data needs to be rendered on the page, so Skeleton screens are a great option.</p>
<p>In this article, we'll cover:</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-skeleton-screen">What Skeleton Screens are</a></p>
</li>
<li><p><a class="post-section-overview" href="#differenttypesofskeletonscreens">Different types of Skeleton screens</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-skeleton-screens">Why use Skeleton Screens</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-them">When to use them</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-things-to-keep-in-mind">Things to keep in mind</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-building-a-daily-dev-skeleton-loading-ui">Building A Daily Dev Skeleton Loading UI</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-set-up-the-project">Set up the project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-design-the-skeleton-elements">Design the Skeleton elements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-clone-the-card-template">Clone the card template</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-create-json-file">Create JSON file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-populate-html-elements-with-appropriate-content">Populate HTML elements</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes that you have:</p>
<ul>
<li><p>Knowledge of HTML and CSS (SASS)</p>
</li>
<li><p>Knowledge of JavaScript (ES6)</p>
</li>
</ul>
<p>We will use HTML and SASS for this project. If you would like to get started with SASS, check out this <a target="_blank" href="https://freecodecamp.org/news/beginners-guide-to-sass">Beginner's Guide.</a></p>
<h2 id="heading-what-is-a-skeleton-screen">What is a Skeleton Screen?</h2>
<p>A skeleton screen is an animated placeholder that simulates the layout of a website while data is being loaded.</p>
<p>They let the user know that some content is loading and, more importantly, provide an indication of what is loading, whether it's an image, text, card, and so on.</p>
<p>This gives the user the impression that the website is faster because they already know what type of content is loading before it appears. This is referred to as <strong>perceived performance</strong>.</p>
<p>Here are some examples of skeleton screens from Facebook and LinkedIn:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/1-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>LinkedIn home feed loading state</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/2-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Facebook home feed loading state</em></p>
<h2 id="heading-different-types-of-skeleton-screens">Different types of Skeleton Screens</h2>
<p>There are 2 main types of skeleton screens:</p>
<ul>
<li><p>Content Placeholders</p>
</li>
<li><p>Color Placeholders</p>
</li>
</ul>
<p>Content Placeholders are typically light grey boxes and circles that simulate the appearance of the page, as shown in the images above for Facebook and LinkedIn.</p>
<p>Color Placeholders are more difficult to create because they simulate not only the UI layout but also the dominant color. It is most commonly found on image-focused websites such as Pinterest and Unsplash.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/9.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Color Placeholder example from Pinterest</em></p>
<h2 id="heading-why-use-skeleton-screens">Why Use Skeleton Screens?</h2>
<ul>
<li><p>They appear to be faster and are more user friendly. Improved perceived performance provides both good UX and helps in increasing conversion rate.</p>
</li>
<li><p>The issue with spinners/loaders is that we have no idea what is loading or how long it will take.</p>
</li>
<li><p>The use of spinners/loaders creates a period of uncertainty for the user since the load time is unknown.</p>
</li>
<li><p>Skeleton screens draw the user's attention to progress rather than waiting time.</p>
</li>
<li><p>It creates an illusion of speed and short load time</p>
</li>
</ul>
<h2 id="heading-when-to-use-them">When to use them</h2>
<ul>
<li><p>Use to notify the user that something is loading when more than one element is loading at the same time.</p>
</li>
<li><p>Use when loading data takes more than 3 seconds.</p>
</li>
<li><p>Use on websites with a lot of traffic.</p>
</li>
<li><p>Use for a background or long-running process.</p>
</li>
</ul>
<h2 id="heading-things-to-keep-in-mind">Things to keep in mind</h2>
<p>While implementing skeleton screens, we should keep in mind the goal we are trying achieve with the website or app, and prioritize loading the content.</p>
<p>Use of skeleton loading screens is no excuse to skip actual performance optimization, and if you can cache meaningful content and display that, that'll be good.</p>
<h2 id="heading-building-a-daily-dev-skeleton-loading-ui">Building A Daily Dev Skeleton Loading UI</h2>
<p>In this section, we will dive into the implementation of the skeleton loading screen following a step-by-step process so it's easier to understand.</p>
<p>We'll build one like daily.dev's feed section.</p>
<h3 id="heading-step-1-set-up-the-project">Step 1: Set up the project</h3>
<p>First, to code along with me, clone or download the starter code for the layout <a target="_blank" href="https://github.com/israelmitolu/Skeleton-Loading-using-CSS/tree/master/starter">here</a>. You can download the files by using <a target="_blank" href="https://minhaskamal.github.io/DownGit/#/home">DownGit</a>.</p>
<p>The code contains the card layout, so we will continue from here in the next steps.</p>
<p>To begin, start the development server in the IDE and open up your browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/screencapture-codepen-io-israelmitolu-full-wvpOaQd-2022-04-21-17_16_47.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Starter Card Layout</em></p>
<h3 id="heading-step-2-design-the-skeleton-elements">Step 2: Design the Skeleton elements</h3>
<p>There are 5 elements that we want to build for the skeleton loading: the logo image, title, details, cover image and footer section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/4-3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Daily Dev's Skeleton Elements</em></p>
<p>Now, we will add <code>skeleton</code> classes to the locations of the above elements.</p>
<p>For the logo,</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__header header__img skeleton"</span> /&gt;</span>
</code></pre>
<p>For the title, there will be 2 divs to represent the two lines that we have in the picture above.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>For the details, add the following code inside the div of class <code>body__text</code>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text skeleton-text__body"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside the <code>body__img</code> div, add the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"cover-img"</span> /&gt;</span>
</code></pre>
<p>For the footer, add this code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text skeleton-footer"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now, the complete HTML code for the card:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"card-link"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__header"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__header header__img skeleton"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"logo-img"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__header header__title"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"card-title"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__body"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__body body__text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"card-details"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text skeleton-text__body"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__body body__img"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"cover-img"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__footer"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"card-footer"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skeleton skeleton-text skeleton-footer"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Now, let's add some styling to make the skeleton components:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.skeleton</span> {
  <span class="hljs-attribute">animation</span>: skeleton-loading <span class="hljs-number">1s</span> linear infinite alternate;
}

<span class="hljs-keyword">@keyframes</span> skeleton-loading {
  0% {
    <span class="hljs-attribute">background-color</span>: hsl(<span class="hljs-number">200</span>, <span class="hljs-number">20%</span>, <span class="hljs-number">80%</span>);
  }
  100% {
    <span class="hljs-attribute">background-color</span>: hsl(<span class="hljs-number">200</span>, <span class="hljs-number">20%</span>, <span class="hljs-number">95%</span>);
  }
}

<span class="hljs-selector-class">.skeleton-text</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">0.7rem</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.25rem</span>;
}

<span class="hljs-selector-class">.skeleton-text__body</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">75%</span>;
}

<span class="hljs-selector-class">.skeleton-footer</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">30%</span>;
}
</code></pre>
<p>This is the resulting layout:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/5.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Card component loading</em></p>
<h3 id="heading-step-3-clone-the-card-template">Step 3: Clone the card template</h3>
<p>Insert a <code>template</code> tag between the <code>container</code> and the <code>card</code> element in the <code>index.html</code> file.</p>
<p>In the image above there's a <code>template</code> tag that I commented out, and yes, its a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template">valid HTML element</a> ;). It is used to declare fragments of HTML that can be cloned and inserted in the document by script.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"card-template"</span>&gt;</span>
</code></pre>
<p>As a result, make sure to add the closing tag <code>&lt;/template&gt;</code> after the closing tag of the <code>card</code> div.</p>
<p>Now let's look at the JavasScript code that we'll use to clone the card template.</p>
<p>Create a <code>script</code> tag just before the end of the <code>body</code> tag, and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> container = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".container"</span>);
<span class="hljs-keyword">const</span> cardTemplate = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"card-template"</span>);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) {
  container.append(cardTemplate.content.cloneNode(<span class="hljs-literal">true</span>));
}
</code></pre>
<p>The code above grabs the page container and the card template, and then creates 9 clones/copies of the card (making 10 in total). Then it appends/inserts the cards into the container.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/6.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cloned card skeleton UI</em></p>
<h3 id="heading-step-4-create-json-file">Step 4: Create JSON file</h3>
<p>We need some data before we can add content to the page. Normally, you would need to get data with an external website, but we'll be using one that I've set up specifically for this project.</p>
<p>To begin, create a file called <code>data.json</code> in the project folder.</p>
<p>Add the following code to the JSON file.</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Writing Cleaner CSS Using BEM "</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Mar 12, 2022 · 4m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/dd19e7a56475f39ab1c38167c02c7b58"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://israelmitolu.hashnode.dev/writing-cleaner-css-using-bem-methodology"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://daily-now-res.cloudinary.com/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"The Beginner's Guide to Sass"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Apr 05, 2022 · 8m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/bec6719be210973098293a32dc732d1e"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://www.freecodecamp.org/news/the-beginners-guide-to-sass/"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/devto"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"I made Squid Game with Javascript"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Oct 25, 2021 · 3m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/1f947033365381cbe322ddf294ad7169"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://dev.to/0shuvo0/i-made-squid-game-with-javascript-10j9"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">4</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Using Custom Cursors with Javascript for a Better User Experience"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Feb 12, 2022 · 9m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/3d056b99c95b37cd35ae5cfc6a8b38be"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://israelmitolu.hashnode.dev/using-custom-cursors-with-javascript-for-a-better-user-experience"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">5</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://daily-now-res.cloudinary.com/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"React Best Practices - Tips for Writing Better React Code in 2022"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Feb 03, 2022 · 31m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/5a629fff5583f9ab5f0931d14736b299"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://www.freecodecamp.org/news/best-practices-for-react/"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">6</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/tnw"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"You suck at Googling: 5 tips to improve your search skills"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Mar 31, 2022 · 4m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/e318150ae67c2083ff3585a96f366f7b"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://thenextweb.com/news/5-tips-to-improve-your-google-search-skills"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">7</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/logrocket"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"A better way of solving prop drilling in React apps"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Jan 14, 2022 · 13m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/6fe4c4060bca638b419d8b2c63d8eaf7"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://blog.logrocket.com/solving-prop-drilling-react-apps/"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">8</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/dz"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Golang and Event-Driven Architecture"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Apr 18, 2022 · 6m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/d06eddd82c62288df6e2600bcda61579"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://dzone.com/articles/golang-and-event-driven-architecture"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">9</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Introduction to Git In 16 Minutes"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Mar 18, 2021 · 8m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/3c02111a8f242f607551500432e17a78"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://vickyikechukwu.hashnode.dev/introduction-to-git-in-16-minutes"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">10</span>,
    <span class="hljs-attr">"logoImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"How to Create a Sleek Preloader Animation Using GSAP Timeline"</span>,
    <span class="hljs-attr">"details"</span>: <span class="hljs-string">"Jan 25, 2022 · 7m read time"</span>,
    <span class="hljs-attr">"coverImage"</span>: <span class="hljs-string">"https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/e238c35cb9d41dd9a5475602aef00119"</span>,
    <span class="hljs-attr">"link"</span>: <span class="hljs-string">"https://israelmitolu.hashnode.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline"</span>
  }
]
</code></pre>
<p>To replicate Daily Dev's feed section, we have created some data that has an array of objects with properties such as id, logo image, title, details and cover image.</p>
<h3 id="heading-step-5-populate-html-elements-with-appropriate-content">Step 5: Populate HTML Elements with appropriate content</h3>
<p>Add the following code to the script tag that houses your JavaScript:</p>
<pre><code class="lang-javascript">fetch(<span class="hljs-string">"data.json"</span>)
  .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> response.json())
  .then(<span class="hljs-function">(<span class="hljs-params">posts</span>) =&gt;</span> {
    container.innerHTML = <span class="hljs-string">""</span>;
    posts.forEach(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> div = cardTemplate.content.cloneNode(<span class="hljs-literal">true</span>);
      div.getElementById(<span class="hljs-string">"card-link"</span>).href = post.link;
      div.getElementById(<span class="hljs-string">"logo-img"</span>).src = post.logoImage;
      div.getElementById(<span class="hljs-string">"card-title"</span>).textContent = post.title;
      div.getElementById(<span class="hljs-string">"card-details"</span>).textContent = post.details;
      div.getElementById(<span class="hljs-string">"cover-img"</span>).src = post.coverImage;
      div.getElementById(
        <span class="hljs-string">"card-footer"</span>
      ).innerHTML = <span class="hljs-string">` &lt;ion-icon name="arrow-up"&gt;&lt;/ion-icon&gt;
          &lt;ion-icon name="chatbox-ellipses"&gt;&lt;/ion-icon&gt;
          &lt;ion-icon name="bookmark"&gt;&lt;/ion-icon&gt;`</span>;
      container.append(div);
    });
  });
</code></pre>
<p>The code above is what we will use to add content to the cards once they are done loading.</p>
<p>Now, let me explain the code bit by bit:</p>
<pre><code class="lang-json">fetch(<span class="hljs-string">"data.json"</span>)
  .then((response) =&gt; response.json())
</code></pre>
<p>Here, we have a basic fetch request, where we set the path to the resource. In this case, the <code>data.json</code> file. If it were an external API, you would use the endpoint URL as the argument:</p>
<p>The <code>fetch()</code> method does not directly return the JSON response body but instead returns a promise that resolves with a Response object.</p>
<p>To learn more, check out the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">MDN docs</a>.</p>
<pre><code class="lang-json">.then((posts) =&gt; {
    container.innerHTML = <span class="hljs-attr">""</span>;
    posts.forEach((post) =&gt; {
      const div = cardTemplate.content.cloneNode(true);
      div.getElementById(<span class="hljs-attr">"logo-img"</span>).src = post.logoImage;
      div.getElementById(<span class="hljs-attr">"card-title"</span>).textContent = post.title;
      div.getElementById(<span class="hljs-attr">"card-details"</span>).textContent = post.details;
      div.getElementById(<span class="hljs-attr">"cover-img"</span>).src = post.coverImage;
      div.getElementById(
        <span class="hljs-attr">"card-footer"</span>
      ).innerHTML = `&lt;ion-icon name=<span class="hljs-attr">"arrow-up"</span>&gt;&lt;/ion-icon&gt;
          &lt;ion-icon name=<span class="hljs-attr">"chatbox-ellipses"</span>&gt;&lt;/ion-icon&gt;
          &lt;ion-icon name=<span class="hljs-attr">"bookmark"</span>&gt;&lt;/ion-icon&gt;`;
      container.append(div);
    });
  });
</code></pre>
<p>Here, we define what should happen after fetching the data.</p>
<p>The code first clears the page, and then runs a <code>forEach()</code> method which extracts the properties from the JSON file, and then inputs it into the card elements (logo image, card title,...) using <code>.textContent</code> property.</p>
<p>Finally, for the footer, we used <code>.innerHTML</code> to insert the icons as HTML content.</p>
<p>If you added everything correctly, there shouldn't be any errors, and this is our fully functional skeleton loading UI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/7-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Our finished Daily Dev skeleton UI</em></p>
<p>Check out the <a target="_blank" href="https://daily-dev-ui.netlify.app/">live demo</a> and the <a target="_blank" href="https://github.com/israelmitolu/Skeleton-Loading-using-CSS">source code repository</a> on Github.</p>
<h3 id="heading-network-throttling-in-chrome-devtools">Network Throttling in Chrome DevTools</h3>
<p>It's important to note that we didn't set a timeout because this skeleton screen is dependent on the user's network speed.</p>
<p>If you want to simulate it at different network speeds, go into the network tab in your browser Devtools.</p>
<p>Here's how to do it in Chrome v100:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/8.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Throttle Network in Chrome DevTools</em></p>
<ol>
<li><p>Open DevTools (Ctrl+Shift+i).</p>
</li>
<li><p>Navigate to the "Network" tab.</p>
</li>
<li><p>Select the type of connection you want</p>
</li>
<li><p>Reload the page to see assets downloading at the specified connection speed.</p>
</li>
</ol>
<p>If the default options don't suit you, you can create a custom Network Throttling Profile by selecting the option at the very top of the dropdown menu.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You made it all the way to the end! You've learned about skeleton loading, and how it contributes to user experience by creating the illusion of speed when loading data, and you've implemented your own.</p>
<p>I hope you found this tutorial useful and that it serves as a good starting point for creating various skeleton loading screens.</p>
<p>If you found this article insightful, do share it with your friends and network. Also, feel free to connect with me on <a target="_blank" href="https://twitter.com/israelmitolu">Twitter</a> and my <a target="_blank" href="https://israelmitolu.hashnode.dev">blog</a> where I share resources and articles to make you a better dev.</p>
<p>Thanks for reading, and happy coding!</p>
<p>Before you go, here are some skeleton loading packages for <a target="_blank" href="https://blog.openreplay.com/3-ways-to-implement-skeleton-components-in-react#heading-what-is-a-skeleton-component">React</a>, <a target="_blank" href="https://openbase.com/categories/js/best-angular-loading-skeleton-libraries">Angular</a> and <a target="_blank" href="https://openbase.com/categories/js/best-vue-loading-skeleton-libraries">Vue</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
