<?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[ projects - 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[ projects - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 16:11:19 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/projects/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Become an Analytical Programmer – Solve the "Rock, Paper, Scissors" Game 5 Ways Using JavaScript & Mermaid.js ]]>
                </title>
                <description>
                    <![CDATA[ Over the past year, I’ve explored tools and practices that help developers build an analytical mindset. One recurring theme is how experienced programmers often describe understanding code as forming a mental picture – a conceptual map of the program... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-become-an-analytical-programmer-compare-five-projects/</link>
                <guid isPermaLink="false">6822006162f6347293451e07</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ evaristo.c ]]>
                </dc:creator>
                <pubDate>Mon, 12 May 2025 14:06:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746812725602/cd4a5bc4-71f2-4678-8f5d-5571d9cc38e8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Over the past year, I’ve explored tools and practices that help developers build an analytical mindset. One recurring theme is how experienced programmers often describe understanding code as forming a mental picture – a conceptual map of the program’s flow.</p>
<p>When it comes to software development, finding ways to visualize these mental models is a common thread. Many developers describe sketching workflows or imagining them mentally to reason through code. And in my own experience working with senior developers and software architects, this visualization habit is extremely common.</p>
<p>A 2013 Quora post captures this well:</p>
<blockquote>
<p>“A top coder sees a program graphically (…) A data structure as a hierarchy of relationships, a program as a network of data pipes… The first and only time he reduces pictures to words is when he writes code.” —Rober Wagner</p>
</blockquote>
<p>So it appears that developing our ability to recognize patterns in code could help us to develop our expertise. But programming is rarely that straightforward – there are often multiple ways to solve the same problem. It also reflects a developer’s personal habits, experiences, and styles. So which patterns matter, then?</p>
<p>Imagine you assign the same task to five developers: build a small game using HTML, CSS, and JavaScript. Each solution should follow the same functional rules. What are the chances their code will look the same?</p>
<p>You’d expect that their implementations might differ, but the underlying <strong>mental workflow</strong> – the internal logic they follow – might be remarkably similar. If you can reconstruct this mental flow from each codebase, you can create a shared referential point for comparison. Once this conceptual diagram is in place, you can “overlay” it on each codebase to identify where they diverge in structure, logic, or abstraction.</p>
<p>Ever since I started coding, spotting “patterns” while comparing other people’s code has fascinated me. I’ve found the process of comparing code not only educational but also enjoyable.</p>
<p>Up until now, I’ve approached it mostly by intuition – but I’ve always felt the need for a more structured method. This article explores a more systematic approach to code comparison, reflecting on what works well and what can be improved.</p>
<h3 id="heading-how-to-read-this-article"><strong>How to Read this Article</strong></h3>
<p>This article is intentionally extensive. Instead of just showing the end result, I’ll walk you through the methodology so you can recreate or adapt it for your own code comparisons.</p>
<p>That said, there are different ways you might approach this article, depending on your interests:</p>
<ul>
<li><p><strong>Just curious about the idea?</strong> The core argument is already laid out in the introduction. Maybe read the conclusion too, and that might be enough for your needs.</p>
</li>
<li><p><strong>Looking to improve your portfolio?</strong> Skim through the analyses. If you're working on a game project, you’ll find comparisons of five JavaScript implementations of Rock, Paper, Scissors. These may give you practical ideas for your own code.</p>
</li>
<li><p><strong>Interested in code analysis?</strong> Read the whole thing. This article goes deeper into why analyzing code is valuable and presents a practical methodology – including tools – you can apply.</p>
</li>
</ul>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-visual-tools-for-code-comparison">Visual Tools for Code Comparison</a>: In this part, we’ll talk about diagrams and focus on the one we’ll use for this project – the use case-driven system block diagram.</p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-we-are-going-to-do">What we are going to do</a>: here, we reveal the focus of our analysis, what we expect to do, what we will consider when making the diagrams, and a short intro to the tool we will use for this, Mermaid.js.</p>
</li>
<li><p><a class="post-section-overview" href="#heading-five-code-examples-of-rock-paper-scissors">Five Code Examples of Rock, Paper, Scissors</a>: Next, we’ll assess the JavaScript code of five CodePen projects, starting from the simplest one and finishing with the most complex one. At each assessment, we’ll make a comparison against the preceding projects.</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-cpc-rock-paper-scissors-by-amit-a-project-with-fireworks-animations">“CPC Rock Paper Scissors” by Amit: A project with fireworks animations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-rock-paper-scissors-game-using-native-drag-amp-drop-by-hmz-c-a-project-using-the-html-drag-element">“Rock paper scissors game using native drag &amp; drop” by Hmz C: A project using the HTML Drag element</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-rock-paper-scissors-by-brad-traversy-a-project-with-a-modal-and-a-reset">“Rock Paper Scissors” by Brad Traversy: A project with a modal and a reset</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-rock-paper-scissors-oop-by-damian-a-project-written-in-oop">“Rock Paper Scissors OOP” by Damian: A project written in OOP</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-recurrent-neural-network-rock-paper-scissors-by-andrew-worcerster-a-project-implementing-ai">“Recurrent Neural Network - Rock, Paper, Scissors” by Andrew Worcerster: A project implementing AI</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ol>
<p>Now, let’s dive in!</p>
<h2 id="heading-visual-tools-for-code-comparison"><strong>Visual Tools for Code Comparison</strong></h2>
<p>There are many tools and approaches for code analysis, and although there are more and more “automatic” ways of doing it, <strong>flow diagrams</strong> are still incredibly useful. Unfortunately, while they’re widely used in programming education and analysis, their use for comparing different implementations is rarely discussed or illustrated in tutorials.</p>
<p>Still, the potential is clear. Flow diagrams capture structure and logic in a compact, readable form. They're heavily used in compiler design for analyzing control flow, optimizing logic, and identifying bugs.</p>
<p>When applied to comparative analysis, they can highlight differences in:</p>
<ul>
<li><p>Modularity and abstraction</p>
</li>
<li><p>Execution flow</p>
</li>
<li><p>Separation of concerns or unnecessary complexity</p>
</li>
</ul>
<p>But not all diagram types are equally effective for this purpose. Traditional flowcharts often dive too deep into the nitty-gritty, repeating low-level logic like if-else branches already visible in the source. While helpful for understanding a single script, they may obscure broader design patterns when comparing multiple implementations.</p>
<p>When comparing codebases, a better option is to focus on code structure: how functions, modules, and scopes relate and interact. For that, <strong>system block diagrams</strong> are a better fit.</p>
<h3 id="heading-why-use-system-block-diagrams"><strong>Why Use System Block Diagrams?</strong></h3>
<p>System block diagrams visualize a program at a higher level. They use labeled blocks to represent major components (functions, classes, modules) and arrows to show how data or control flows between them. This lets you focus on architecture: which part does what, and how do they work together?</p>
<p>Used for code comparison, placing two system block diagrams side by side immediately highlights structural and design differences. You can see at a glance how one solution favors modularity, another embeds logic in fewer blocks, or which one better separates concerns.</p>
<p>Still, system block diagrams can be drawn in different ways – some emphasizing structure, others workflow. Is there a format that combines both?</p>
<h3 id="heading-the-use-case-driven-system-block-diagram"><strong>The Use Case-Driven System Block Diagram</strong></h3>
<p>A strong candidate is the <strong>use case-driven system block diagram</strong>. <a target="_blank" href="https://www.clear.rice.edu/comp310/JavaResources/systemblockdiagrams.html">An example</a> of this variant is suggested as part of the resources for a computer science curriculum (“Advanced Object-Oriented Programming and Design” COMP310) of Rice University.</p>
<p>Unlike use case diagrams (which focus only on actors and goals), use case-driven system block diagrams are more implementation-focused. They still show modules and data flow, but through the lens of system functionality and user-driven logic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746025297569/da5599e5-838a-4c90-bbf8-ddcaba58e36f.jpeg" alt="An example of Use Case-Driven System Block Diagram, by computer science students at Rice University" class="image--center mx-auto" width="1736" height="1327" loading="lazy"></p>
<p>Interestingly, the example above effectively exemplifies its usage for <strong>evaluating existing architectures</strong>. This confirms the suitability of this type of diagram for comparisons between codebases. Simply draw one diagram per implementation, striving to find their same functional goal. Then, compare their workflows and structures side by side. You’ll quickly spot which solution is more modular, more efficient, or easier to maintain.</p>
<h2 id="heading-what-we-are-going-to-do"><strong>What We Are Going to Do</strong></h2>
<p>To elaborate upon the suitability of this methodology for code analysis, we’ll apply the fundamentals of use case-driven system block diagram methodology to analyze five JavaScript projects’ code – each created by a different developer – implementing the Rock, Paper, Scissors game on <a target="_blank" href="https://codepen.io/">CodePen</a>.</p>
<p>The Rock, Paper, Scissors (RPS) game is a simple two-player challenge where each participant simultaneously shows either a “rock”, a “paper”, or “scissors” depicted by one of their hands, usually after a countdown. Then they apply the following rule to decide the “winner”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746025625482/59c24cc3-d1e8-455b-8387-67733487e58d.jpeg" alt="Rules of the Rock Paper Scissors game, Wikipedia" class="image--center mx-auto" width="1438" height="1375" loading="lazy"></p>
<ul>
<li><p>Rock beats scissors</p>
</li>
<li><p>Scissors beats paper</p>
</li>
<li><p>Paper beats rock</p>
</li>
</ul>
<p>If both players choose the same option, it’s a tie.</p>
<p>In most programming versions, the player faces off against the computer, which makes random (or pseudo-random) choices. It’s a favorite beginner’s project because it’s easy to understand and fun to build.</p>
<h3 id="heading-the-code-examples">The Code Examples</h3>
<p>We are going to compare the (vanilla) JavaScript code for the following 5 projects, ordered by complexity:</p>
<ol>
<li><p><strong>“CPC Rock Paper Scissors”</strong> by Amit (@ghaste in CodePen)</p>
</li>
<li><p><strong>“Rock Paper Scissor game using native drag &amp; drop”</strong> by Hmz C (@HmZ2 in CodePen)</p>
</li>
<li><p><strong>“Rock Paper Scissors“</strong> by Brad Traversy (@bradtraversy in CodePen)</p>
</li>
<li><p><strong>“Rock Paper Scissors OOP“</strong> by Damian (@CvtS in CodePen)</p>
</li>
<li><p><strong>“Recurrent Neural Network - Rock, Paper, Scissors“</strong> by Andrew Worscerter (@amwmedia in CodePen)</p>
</li>
</ol>
<p>In order to keep the article shorter, the full code won’t be provided here, except for some exceptional cases. You are encouraged to visit the corresponding projects in CodePen to have a look at the code alongside the analysis. Because the authors can update or even delete the code in CodePen at any time, I’ll also add links to Gists with the code I analyzed at the time I wrote the article.  </p>
<p>Follow this link: <a target="_blank" href="https://gist.github.com/evaristoc/81dc9f508aa54c355f3f89b08a2450d5">https://gist.github.com/evaristoc/81dc9f508aa54c355f3f89b08a2450d5</a> if you want to find the copy of the JavaScript codes used for this article.</p>
<h4 id="heading-how-to-choose-your-projects">How to Choose Your Projects</h4>
<p>To find the projects, I searched CodePen for any pen about “Rock Paper Scissors”. Then I checked out the edits of some of them, and chose the ones I found the most interesting. I made a point to choose ones with varying complexities.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746808534102/acd2dba9-05d4-454d-8bb8-f4ee51dff304.jpeg" alt="Querying CodePen in search for the projects" class="image--center mx-auto" width="1875" height="875" loading="lazy"></p>
<h3 id="heading-the-scope"><strong>The Scope</strong></h3>
<p>First of all, our goal here isn’t to judge the quality of the codebases. Instead, it’s to demonstrate how we can use this diagram-based methodology to compare them.</p>
<p>CodePen projects often include quick demos, experiments, or even AI-generated snippets. Some developers might be more interested in the HTML/CSS of the project. But while they don’t always follow best practices, that’s what makes this exercise interesting – the diversity of approaches reveals valuable contrasts worth analyzing.</p>
<h3 id="heading-the-methodology"><strong>The Methodology</strong></h3>
<p>For the comparison, we’re using a methodology inspired by Rice University’s COMP310 course, which suggests the following high-level steps for constructing use case-driven system block diagrams:</p>
<ol>
<li><p><strong>Add all system use cases</strong> to a single diagram. These use cases will be similar to “steps in a workflow”.</p>
</li>
<li><p><strong>Group related cases</strong> within conceptually – or functionally – related operations.</p>
</li>
<li><p><strong>Encapsulate groups</strong> into modules (or blocks) based on roles and responsibilities. Start drawing the relationships (note: what we should understand for encapsulation here shouldn’t be confused with other definitions like the one given in OOP).</p>
</li>
<li><p><strong>Decouple operations</strong> as much as possible – refine scope and minimize interconnections.</p>
</li>
</ol>
<p>In order to allow the comparison between code examples, we will try to find cases (or steps), modules, and relations that are consistently similar between each code example, independently of the “architecture”.</p>
<p>Although UML compliance is encouraged in the original course, we’ll take a lighter approach – preserving the intent and structure without strictly following every UML rule.</p>
<h3 id="heading-how-well-build-the-diagrams">How We’ll Build the Diagrams</h3>
<p>The Rice course focuses more on <strong>what</strong> the diagram should show, but not so much on <strong>how</strong> to actually build it. For that, I refer to a <a target="_blank" href="https://www.freecodecamp.org/news/how-to-make-flowcharts-with-mermaid/">previous article</a> where I broke down the diagram creation process. Here’s how the two approaches align:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Step</strong> (from earlier article)</td><td><strong>Corresponds to</strong> (Rice methodology)</td></tr>
</thead>
<tbody>
<tr>
<td>Define a scope</td><td>Identify the goal and context for your diagram</td></tr>
<tr>
<td>Find the start and end of the workflow</td><td>Identify use cases (actually, a general scanning of the workflow)</td></tr>
<tr>
<td>Identify deep-dive vs. generalized sections</td><td>Identify groups of cases. Prepare for modular encapsulation</td></tr>
<tr>
<td>Iterative refining and generalization</td><td>Applied throughout to improve module clarity and decoupling</td></tr>
<tr>
<td>Enhance and extend</td><td>Equivalent to fine-tuning and breaking dependencies</td></tr>
</tbody>
</table>
</div><p>Feel free to revisit that article if you want a more detailed walkthrough of these steps.</p>
<h3 id="heading-our-tool-mermaidjs">Our Tool: Mermaid.js</h3>
<p>To create the diagrams, we’ll use <a target="_blank" href="https://mermaid.js.org/"><strong>Mermaid.js</strong></a>, a JavaScript library that converts text into diagrams. It’s Markdown-compatible, which makes it especially handy for online projects, blog posts, and documentation.</p>
<p>The main reason I chose Mermaid.js is that diagrams are easily editable, so we can:</p>
<ul>
<li><p>Focus on defining relationships instead of worrying about layout</p>
</li>
<li><p>Easily iterate and update as we discover more structure in the code</p>
</li>
<li><p>Allows code reusability</p>
</li>
</ul>
<p>Mermaid.js is also highly configurable, allowing a certain level of customization. It was also made for the web, so it’s compatible with JavaScript, CSS and Markdown. But it’s not a perfect tool, and its use poses challenges both because of the limitations of the tool itself and even because of the limitations of using visual diagrams for code analysis.</p>
<p>If you aren’t familiar with using Mermaid.js, I wrote <a target="_blank" href="https://www.freecodecamp.org/news/use-mermaid-javascript-library-to-create-flowcharts/">an extensive tutorial</a> about the tool, along with its pros and cons. Although I will refrain from talking in-depth about Mermaid.js here, I will make use of some other tricks that were not included in previous articles.</p>
<p>Be aware that to keep consistency in sizing and titling all resulting diagrams were subjected to post-treatment, which also included color filtering to improve contrast.</p>
<h2 id="heading-five-code-examples-of-rock-paper-scissors">Five Code Examples of Rock, Paper, Scissors</h2>
<h3 id="heading-1-cpc-rock-paper-scissors-by-amit-a-project-with-fireworks-animations">1. “CPC Rock Paper Scissors” by Amit: A project with fireworks animations</h3>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/ghaste/embed/XWLxQEw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p>Amit is a prolific CodePen user who has been uploading about 2-3 projects per week since as early as 2021. Their most recent projects (year 2025) have increased in complexity and quality while maintaining the high productivity rate.</p>
<p>But in Sept 2024, Amit made a simple (JavaScript) version of the Rock, Paper, Scissors game. This code is a lightweight, user-friendly implementation with fun visual elements like result display, animations, and visual feedback for wins and losses.</p>
<p>Key features of this project are:</p>
<ul>
<li><p>It provides a complete Rock, Paper, Scissors game with a visual spinning effect when the computer is selecting its choice.</p>
</li>
<li><p>The game has animated fireworks to celebrate wins and changes the background color to red for losses.</p>
</li>
<li><p>The game keeps the interface interactive and fun through the use of emoji, quick animations, and visual feedback for the player’s performance.</p>
</li>
</ul>
<h3 id="heading-analysis-of-the-project">Analysis of the Project</h3>
<h4 id="heading-quickly-discovering-of-the-start-and-end-of-the-workflow">Quickly discovering of the start and end of the workflow</h4>
<p>The code of this pen runs immediately once it’s called. Several variables are initialized (the <code>animationContainer</code>, the <code>choices</code>, the <code>emojiMap</code>, and so on).  </p>
<p>Amit also made three separate functions for each item (that is, “rock”, “paper” and “scissors”). Each of them will be added as a <strong>click event</strong> to the interface (the HTML) and they all run the same function after clicking (<code>playGame</code>) each with a different argument based on what the user clicked.</p>
<p>After the interface is cleared, some variables are initialized, and the click event is added to the different emoji. The interface will wait for click interactions from the user, which will trigger the <code>playGame</code> function.</p>
<p>Let’s make a quick diagram showing this high level of generalization:</p>
<pre><code class="lang-mermaid">---
title: “CPC Rock Paper Scissors” by Amit - Simple SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
subgraph clickEventListener[Click Event]
    eventHandler@{shape: fr-rect, label: "event handler\n(*&lt;u&gt;playGame&lt;/u&gt;*)"}
end
end
init --&gt; |"(1) initialize values&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handler:**playGame**)"|interface
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface
end
style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746177298317/0b9f4609-9810-4ba5-8d8f-6e0869080fdb.jpeg" alt="Simple SB-Diagram of the RPS game by Amit" class="image--center mx-auto" width="501" height="725" loading="lazy"></p>
<h4 id="heading-the-playgame-event-handler">The <code>playGame</code> Event Handler</h4>
<p>Let’s take a closer look at the <code>playGame</code> function:</p>
<pre><code class="lang-javascript">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">playGame</span>(<span class="hljs-params">playerChoice</span>) </span>{
    <span class="hljs-keyword">const</span> computerChoice = choices[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * choices.length)];
    <span class="hljs-keyword">const</span> animationDiv = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"animation"</span>);
    <span class="hljs-keyword">const</span> resultDiv = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"result"</span>);

    <span class="hljs-built_in">clearInterval</span>(animationInterval);
    <span class="hljs-keyword">let</span> currentIndex = <span class="hljs-number">0</span>;

    animationInterval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
      animationDiv.textContent = emojiMap[choices[currentIndex]];
      currentIndex = (currentIndex + <span class="hljs-number">1</span>) % choices.length;
    }, <span class="hljs-number">100</span>);

    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">clearInterval</span>(animationInterval);
      animationDiv.textContent = emojiMap[computerChoice];
      displayResult(playerChoice, computerChoice);
    }, <span class="hljs-number">1500</span>);
  }
</code></pre>
<p>It seems that there is a lot going on only in this function that could be assumed as separated functionalities. Although not explicit, there are different sections in it:</p>
<ul>
<li><p>one dedicated to calculating the computer’s choice</p>
</li>
<li><p>then a <code>setInterval</code> to handle an animation</p>
</li>
<li><p>then a <code>setTimeout</code> to clear the previous animation and run the <code>displayResult</code> function</p>
</li>
</ul>
<h4 id="heading-refinement">Refinement</h4>
<p>The first functionality operation that’s easy to identify within the whole code, and that could be set apart, is the one in charge of displaying the results: the <code>displayResult</code> func.</p>
<p>But we need to clarify the relationships in our diagram. The <code>displayResult</code> function is not coming after the event handler but is called from the event handler itself. We could argue that the event handler <em>uses</em> the <code>displayResult</code> function. Let’s clarify these definitions on the diagram, and also add a legend:</p>
<pre><code class="lang-mermaid">---
title: “CPC Rock Paper Scissors” by Amit - SB Diagram Refinement 01
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
subgraph clickEventListener[Click Event]
    eventHandler@{shape: fr-rect, label: "event handler\n(*&lt;u&gt;playGame&lt;/u&gt;*)"}
end
displayFunc@{shape: fr-rect, label: "display func\n(*&lt;u&gt;displayResult&lt;/u&gt;*)"} -.-o eventHandler
end
init -.-o |"(1) initialize values&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handler:**playGame**)"|interface@{shape: curv-trap, label: "interface display"}
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface

subgraph legend
      direction LR
      start1[ ] --&gt;|"next step"| stop1[ ]
      style start1 height:0px;
      style stop1 height:0px;
      start2[ ] -.-o|"assignation"| stop2[ ]
      style start2 height:0px;
      style stop2 height:0px; 
end

end

interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746177264415/e826d8e8-bddb-4f9e-9f86-8454f4671ac1.jpeg" alt="First Refinement of SB-Diagram of the RPS game by Amit" class="image--center mx-auto" width="534" height="1004" loading="lazy"></p>
<p>Similarly, the <code>displayResult</code> function uses two functions dedicated to the animations: <code>triggerFireworks</code> and <code>triggerLossBackground</code>. All the functions are declared on the <em>main scope</em>.</p>
<pre><code class="lang-mermaid">---
title: “CPC Rock Paper Scissors” by Amit - SB Diagram Refinement 02
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
data@{shape: lean-r, label: "&lt;span style="color:#f1ce32"&gt;constants&lt;/span&gt;"} --&gt; eventHandler
init
subgraph clickEventListener[Click Event]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;playGame&lt;/code&gt;&lt;br/&gt;event handler"}
end
    displayFunc@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;displayResult&lt;/code&gt;&lt;br/&gt;display func"} -.-o |"(3) the event handler calls the display function"| eventHandler
    display01@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;triggerFireworks&lt;/code&gt;&lt;br/&gt;animation"} -.-o displayFunc
    display02@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;triggerLossBackground&lt;/code&gt;&lt;br/&gt;animation"} -.-o displayFunc
end
init -.-o |"(1) initialize values&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handler:&lt;code style="color:orange;"&gt;playGame&lt;/code&gt;)"|interface@{shape: curv-trap, label: "interface display"}
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
eventHandler --&gt; |"(4) animations, results"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746177244326/42474333-e6c5-499e-ba95-86541ebd525b.jpeg" alt="Second Refinement of SB-Diagram of the RPS game by Amit" class="image--center mx-auto" width="537" height="925" loading="lazy"></p>
<h4 id="heading-imperative-programming">Imperative Programming</h4>
<p>This code is very much imperative. Here’s a definition of imperative programming from Wikipedia:</p>
<blockquote>
<p>Imperative programming focuses on describing <em>how</em> a program operates step by step (generally order of the steps being determined in source code by the placement of statements one below the other), rather than on high-level descriptions of its expected results</p>
</blockquote>
<p>Finding that the project follows an imperative paradigm is helpful as it provides some general idea of what we can expect in terms of how the code is organized and the type of logic we might use. We can expect that the computation logic will follow a certain workflow.</p>
<h4 id="heading-finalizing-the-diagram">Finalizing the diagram</h4>
<p>Now that we have uncovered the most explicit modules and their relationships, we can work on identifying sections of the code that might be considered separated functional/operational units. These separate units can be defined as “use cases” and will become the steps of the workflow.</p>
<p>As a way of enhancing the diagram, I have differentiated between steps and process blocks to prevent confusion between “modules” and “cases”.</p>
<pre><code class="lang-mermaid">---
title: “CPC Rock Paper Scissors” by Amit - Final SB Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
choices ~~~ stop
subgraph main
init
data@{shape: lean-r, label: "&lt;span style="color:#f1ce32"&gt;constants&lt;/span&gt;"} --&gt; eventHandler
subgraph clickEventListener[Click Event]
    subgraph eventHandler["&lt;code style="color:orange"&gt;playGame&lt;/code&gt;&lt;br/&gt;event handler"]
       subgraph step03[step03 - result handling and presentation]
        delay01
        delay02
        delay01 ~~~ nonode01[ ]
        style nonode01 fill:none,stroke:none;
       end
       step01@{label: "step01 - compute computer choice"} --&gt; step02@{label: "step02 - prepare display"}
       step02 --&gt; delay01@{shape: delay, label: setInterval}
       delay01 --&gt; delay02@{shape: delay, label: setTimeout}
    end
end
    subgraph displayFunc["&lt;code style="color:orange"&gt;displayResult&lt;/code&gt;&lt;br/&gt;display func"]
       step04@{label: "step01 - compute winner"} --&gt; step05@{label: "step02 - show results"}
    end
    displayFunc -.-o |"(3) the event handler calls the display function"| delay02
    display01@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;triggerFireworks&lt;/code&gt;&lt;br/&gt;animation"} -.-o step05
    display02@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;triggerLossBackground&lt;/code&gt;&lt;br/&gt;animation"} -.-o step05
end
init -.-o |"(1) initialize values&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handler:&lt;code style="color:orange;"&gt;playGame&lt;/code&gt;)"|interface@{shape: curv-trap, label: "interface display"}
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
step05 --&gt; |"(4) animations, results"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      stepLegend["----step----"]
      style stepLegend fill:#474949, stroke: #CCC, color:#CCC
      end
end
end

interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,displayFunc subProc
classDef step fill:#474949, stroke: #CCC, color:#CCC
class step01,step02,step03,step04,step05,step06 step
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746178100298/bf5f7240-b125-4780-aaf8-fdbbdd911390.jpeg" alt="Final SB Diagram of the RPS game by Amit" class="image--center mx-auto" width="477" height="990" loading="lazy"></p>
<p>Let’s summarize what’s happening here:</p>
<ul>
<li><p>In this project, a function acting as an event handler contains most of the project logic and it assumes full execution responsibility over some of the steps of the workflow.</p>
</li>
<li><p>Once the event handler is triggered, the choice of the user is passed to it and the same function calculates the choice of the computer.</p>
</li>
<li><p>That calculation is followed by part of the preparation of the interface, then an animation managed by an <code>Interval</code> – followed by a nested <code>Timeout</code> JavaScript method.</p>
</li>
<li><p>The Timeout method has the <code>displayResult</code> function as a callback function. This function will take the responsibility of the remaining steps.</p>
</li>
<li><p>It is inside the <code>displayResult</code> where the calculation of the winner takes place. The same function controls the rest of the display of results with different displays delegated to two other functions based on who wins the game.</p>
</li>
</ul>
<h3 id="heading-2-rock-paper-scissors-game-using-native-drag-amp-drop-by-hmz-c-a-project-using-the-html-drag-element">2. “Rock paper scissors game using native drag &amp; drop” by Hmz C: A project using the HTML Drag element</h3>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/HmZ2/embed/yLPGpg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p>Hmz C has not been so active as Amit, with just 7 projects in CodePen across 10 years since 2014. The pen we are going to analyze is his very first contribution to CodePen. The project appears to be a demo of the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API">HTML Drag and Drop API</a> that came with HTML5 by the time of its release. The game provides a clean and interactive experience of "Rock, Paper, Scissors" using intuitive drag-and-drop gestures, visual effects, and live score updates. It focuses on user experience and interactivity.</p>
<p>Key features of this project are:</p>
<ul>
<li><p>The drag and drop interaction, with simple but clean animations.</p>
</li>
<li><p>The scoring is central to this application. Both user and computer scores are updated and displayed in real-time.</p>
</li>
<li><p>The game provides a very clean, easy-to-understand interface without looking static.</p>
</li>
</ul>
<h3 id="heading-analysis-of-the-project-1">Analysis of the Project</h3>
<h4 id="heading-quickly-discovering-of-the-start-and-end-of-the-workflow-1">Quickly discovering of the start and end of the workflow</h4>
<p>By inspecting the code, you will be able to realize that this code follows a similar general pattern as the code we previously examined:</p>
<ul>
<li><p>Variables and states are initialized immediately when the project is accessed.</p>
</li>
<li><p>Similarly, an event handler is added to elements in the interface, but this time they won’t respond to click events but to <strong>drag and drop</strong> events.</p>
</li>
<li><p>That event handler is the one that starts and coordinates the workflow.</p>
</li>
<li><p>The workflow ends by showing results on the interface.</p>
</li>
</ul>
<p>Given the similarities between this project and the previous at this level of analysis, I reused the script I created for the previous chart and modified it accordingly.</p>
<pre><code class="lang-mermaid">---
title: “Rock paper scissors game using native drag &amp; drop” by Hmz C - Simple SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
subgraph dropEventListener[Drop Event Listener]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"}
end
end
init -.-o |"(1) initialize values and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers for: &lt;code style='color:#f1ce32;'&gt;dragstart dragenter dragleave dragover drop&lt;/code&gt;)"|interface
dropEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded, label: dragged choice} --&gt; |"(2) user drag a choice to an area: either paper, rock or scissors"| dropEventListener
dropEventListener --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
        variable["variable"]
        style variable width:135px, fill:none, stroke:none, color:#f1ce32
        subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746315569277/60685986-16dd-4cb0-8290-252ed69114bf.jpeg" alt="Simple SB-Diagram of the RPS game by Hmz C" class="image--center mx-auto" width="710" height="942" loading="lazy"></p>
<h4 id="heading-iife">IIFE</h4>
<p>This project is the only one that uses an IIFE (Immediately Invoked Function Expression) pattern to register the drag and drop listeners. It is inside that IIFE (<code>user</code>) where the <strong>drop listener</strong> is also declared.</p>
<pre><code class="lang-javascript">(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">user</span>(<span class="hljs-params"></span>) </span>{

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; hand.length; i++) {
        hand[i].ondragstart = dragStart;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dragStart</span>(<span class="hljs-params"></span>) </span>{
        userChoice = <span class="hljs-built_in">this</span>.getAttribute(<span class="hljs-string">"data-hand"</span>);
    }

    userArea.addEventListener(<span class="hljs-string">'dragenter'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
        e.preventDefault();
        <span class="hljs-built_in">this</span>.style.borderColor = <span class="hljs-string">"red"</span>;
        user_icon.style.visibility = <span class="hljs-string">"hidden"</span>;
    });

    userArea.addEventListener(<span class="hljs-string">'dragleave'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
        e.preventDefault();
        <span class="hljs-built_in">this</span>.style.borderColor = <span class="hljs-string">"#dedede"</span>;
        user_icon.style.visibility = <span class="hljs-string">"initial"</span>;
    });

    userArea.addEventListener(<span class="hljs-string">'dragover'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
        e.preventDefault();
    });

    userArea.addEventListener(<span class="hljs-string">'drop'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
        e.preventDefault();
        computerChoice = cpu();
        user_icon.classList.remove(<span class="hljs-string">"fa-spin"</span>);
        user_icon.className = <span class="hljs-string">"fa fa-hand-"</span> + userChoice + <span class="hljs-string">"-o fa-2x fa-fw"</span>;
        computer_icon.className = <span class="hljs-string">"fa fa-hand-"</span> + computerChoice + <span class="hljs-string">"-o fa-2x fa-fw"</span>;
        <span class="hljs-built_in">this</span>.style.borderColor = <span class="hljs-string">"#dedede"</span>;
        user_icon.style.visibility = <span class="hljs-string">"initial"</span>;
        whowins(computerChoice, userChoice);
    });
})();
</code></pre>
<p>This project was completed before the release of ES6 in 2015. One of the uses of the pattern is to keep the variables and functions within the IIFE isolated from the global scope. Although the IIFE is still a valid syntax, in modern JavaScript most of the functional operations where the IIFE pattern is applicable can now be implemented with the use of special keywords, special operations, and standards.</p>
<h4 id="heading-refinement-1">Refinement</h4>
<p>Apart of the event handler, there are two other functions – the <code>cpu</code> function and the <code>whowins</code> function – that will take care of other aspects of the functionality. Those functions are declared in the global scope.</p>
<pre><code class="lang-mermaid">---
title: “Rock paper scissors game using native drag &amp; drop” by Hmz C - SB-Diagram Refinement
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
subgraph dropEventListener[Drop Event Listener]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"}
end
cpu@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;cpu&lt;/code&gt;&lt;br/&gt;computer choice calc"} -.-o eventHandler
whowins@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;whowins&lt;/code&gt;&lt;br/&gt;winner calc / update / display func"} -.-o eventHandler
changeColor@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;changeColor&lt;/code&gt;&lt;br/&gt;animation func"} -.-o whowins
end
init -.-o |"(1) initialize values and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers for: &lt;code style='color:#f1ce32;'&gt;dragstart dragenter dragleave dragover drop&lt;/code&gt;)"|interface
dropEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded, label: dragged choice} --&gt; |"(2) user drag a choice to an area: either paper, rock or scissors"| dropEventListener
dropEventListener --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
        variable["variable"]
        style variable width:135px, fill:none, stroke:none, color:#f1ce32
        subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746315095906/70866854-a624-48b5-89ea-4dcb5d7f5eeb.jpeg" alt="Refinement of SB Diagram of the RPS game by Hmz C" class="image--center mx-auto" width="752" height="941" loading="lazy"></p>
<p>Now that we have an overview of how the declared functions are related, let’s identify the functional operations they are taking care of and have a better idea of the full workflow.</p>
<h4 id="heading-finalizing-the-diagram-and-comparison-to-previous-project">Finalizing the diagram and comparison to previous project</h4>
<p>After inspecting the code and making several iterations over the previous diagram, I came up with the following final result. Reusing parts of the script I made for the previous project helped me reduce the number of iterations.</p>
<pre><code class="lang-mermaid">---
title: “Rock paper scissors game using native drag &amp; drop” by Hmz C - Final SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph __
subgraph main
init
data@{shape: lean-r, label: "&lt;code style='color:#f1ce32;'&gt;userScore computerScore&lt;/code&gt; variables"} -.-o whowins
step05 --&gt; data
subgraph dropEventListener[Drop Event Listener]
    subgraph eventHandler["&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"]
        step01@{label: "step01 - compute\ncomputer choice"} --&gt; step02@{label: "step02 - prepare\ndisplay"}
        step02 --&gt; step03@{label: "step03 - result handling\nand presentation"}
    end
end
cpu@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;cpu&lt;/code&gt;&lt;br/&gt;computer choice calc"} -.-o step01
subgraph whowins["&lt;code style="color:orange"&gt;whowins&lt;/code&gt;&lt;br/&gt;winner calc / update / display func"]
    step04@{label: "step01 - calculate winner"} --&gt;
    step05@{label: "step02 - update stored data"} --&gt;
    step06@{label: "step03 - update interface"}
end
whowins -.-o step03
changeColor@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;changeColor&lt;/code&gt;&lt;br/&gt;animation func"} -.-o step06
end
init -.-o |"(1) initialize values and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers for: &lt;code style='color:#f1ce32;'&gt;dragstart dragenter dragleave dragover drop&lt;/code&gt;)"|interface
dropEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded, label: dragged choice} --&gt; |"(2) user drag a choice to an area: either paper, rock or scissors"| dropEventListener
step06 --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      stepLegend["----step----"]
      style stepLegend fill:#474949, stroke: #CCC, color:#CCC
      end
end

interface ~~~ legend
end

style start fill:black
style stop fill:black, fill-stroke:white
classDef clTopr fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,whowins clTopr
classDef step fill:#474949, stroke: #CCC, color:#CCC
class step01,step02,step03,step04,step05,step06 step
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746314611833/a54d5c7a-bd10-4a74-aea2-37176e14908b.jpeg" alt="Final SB Diagram of the RPS game by Hmz C" class="image--center mx-auto" width="693" height="932" loading="lazy"></p>
<p>Notice the strong similarities between this project and the previous one:</p>
<ul>
<li><p>The order of the steps</p>
</li>
<li><p>The imperative code structure</p>
</li>
<li><p>The event handler delegates the final steps to another function</p>
</li>
<li><p>That function (<code>whowins</code>) executes the calculation of the winner</p>
</li>
<li><p>That same function delegates the display of the results to a different function (<code>changeColor</code>)</p>
</li>
<li><p>There is a preference for using functions and variables declared in the global scope</p>
</li>
</ul>
<p>What is different in this project is:</p>
<ul>
<li><p>The event handler is declared as an anonymous callback function directly in the listener</p>
</li>
<li><p>This event handler delegates the execution of the calculation of choice of the computer to a different function, the <code>cpu</code> function.</p>
</li>
<li><p>There are no async functionalities in this project</p>
</li>
<li><p>Different from the previous project, this one has a slightly more advanced <strong>data handling</strong>. Data were stored in global variables (<code>userScore</code>, <code>computerScore</code>) that are updated just after the calculation of the winner.</p>
</li>
</ul>
<h3 id="heading-3-rock-paper-scissors-by-brad-traversy-a-project-with-a-modal-and-a-reset">3. “Rock Paper Scissors” by Brad Traversy: A project with a modal and a reset</h3>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/bradtraversy/embed/wLgPzr" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p>If you started self-learning JavaScript at some point between 2015 and today (2025), you might have heard of this guy. <a target="_blank" href="https://www.linkedin.com/in/bradtraversy">Brad Traversy</a> is a legend among those learning to code and has also made his own learning program.</p>
<p>The number of resources produced by Brad is immense. His first contribution to CodePen dates back to 2017. His activity in CodePen has declined with the time, but he has been posting valuable resources nonetheless.</p>
<p>In 2019 he made this Rock Paper Scissors game. This code delivers a straightforward but engaging implementation with a dynamic user interface. The integration of a randomized computer choice, score tracking, and modal result display creates an interactive and visually appealing experience for users.</p>
<p>Key features of this project are:</p>
<ul>
<li><p><strong>Randomized opponent choice</strong>: The computer's move is randomized each round to simulate an unpredictable opponent.</p>
</li>
<li><p><strong>Dynamic modal display</strong>: A modal window shows round results for a polished, user-friendly experience.</p>
</li>
<li><p><strong>Re-starting the game</strong>.</p>
</li>
<li><p><strong>Score persistence across rounds</strong>: The scoreboard tracks scores until the game is manually reset, allowing for continuous gameplay.</p>
</li>
</ul>
<h3 id="heading-analysis-of-the-project-2">Analysis of the Project</h3>
<h4 id="heading-quickly-discovering-of-the-start-and-end-of-the-workflow-2">Quickly discovering of the start and end of the workflow</h4>
<p>Inspecting this other code reveals a definitive pattern in the way the game is set. Similarly to the previous projects, in this one:</p>
<ul>
<li><p>it initializes variables and states immediately after the project is accessed</p>
</li>
<li><p>it has event handlers added to the corresponding HTML elements in the interface</p>
</li>
<li><p>a function is usually associated to the event handler which is the one that coordinates the workflow – in this case the <code>play</code> function.</p>
</li>
<li><p>the workflow ends by showing results on the interface</p>
</li>
</ul>
<p>Also, this project in particular involves a second event handler with a function to close a modal used to show the results. A third event handler is added to restart the game.</p>
<p>Again, given the similarities between this project and the previous ones at this level of analysis, I reused the script I created and modified it accordingly.</p>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors” by B. Traversy - Simple SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
subgraph clickEventListener[Click Event]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;play&lt;/code&gt;&lt;br/&gt;event handler"}
end
end
init --&gt; |"(1) initialize values and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers:&lt;code style="color:orange"&gt;playGame clearModal restartGame&lt;/code&gt;)"|interface
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface
end
style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746314052265/03bf54f2-a2a6-4a9b-91ec-7465082314db.jpeg" alt="Simple SB-Diagram of the RPS game by B. Traversy" class="image--center mx-auto" width="502" height="797" loading="lazy"></p>
<h4 id="heading-refinement-2">Refinement</h4>
<p>This project takes a step forward in designing an event handler, the <code>play</code> function. Its sole responsibility would be to delegate operations to other functions.</p>
<p>Three functions are called from this event handler:</p>
<ul>
<li><p><code>getComputerChoice</code></p>
</li>
<li><p><code>getWinner</code></p>
</li>
<li><p><code>showWinner</code></p>
</li>
</ul>
<p>Notice that again, all those functions are declared in the global scope – but this time, the <code>play</code> function is also in the same scope.</p>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors” by B. Traversy - SB-Diagram Refinement
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
getComp@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;getComputerChoice&lt;/code&gt;&lt;br/&gt;computer choice func"} -.-o eventHandler
getWinner@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;getWinner&lt;/code&gt;&lt;br/&gt;winner calc func"} -.-o eventHandler
displayFunc@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;showWinner&lt;/code&gt;&lt;br/&gt;display func"} -.-o eventHandler
init
subgraph clickEventListener[Click Event]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;play&lt;/code&gt;&lt;br/&gt;event handler"}
end
end
init -.-o |"(1) initialize values and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers:&lt;code style="color:orange"&gt;playGame clearModal restartGame&lt;/code&gt;)"|interface@{shape: curv-trap, label: "interface display"}
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choicess@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746312146770/922d42c9-f993-4287-8e2c-53b5a4c9b17f.jpeg" alt="Refinement of the SB-Diagram of the RPS game by B. Traversy" class="image--center mx-auto" width="529" height="821" loading="lazy"></p>
<h4 id="heading-the-play-event-handler-as-a-controller"><em>The</em> <code>play</code> <em>event handler as a Controller</em></h4>
<p>In this project, the <code>play</code> function resembles more the idea of a <strong>controller</strong>. The tasks of this event handler are reduced to act as a delegator, something that we have not seen fully implemented in the previous projects.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Play game</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">play</span>(<span class="hljs-params">e</span>) </span>{
  restart.style.display = <span class="hljs-string">'inline-block'</span>;
  <span class="hljs-keyword">const</span> playerChoice = e.target.id;
  <span class="hljs-keyword">const</span> computerChoice = getComputerChoice();
  <span class="hljs-keyword">const</span> winner = getWinner(playerChoice, computerChoice);
  showWinner(winner, computerChoice);
}
</code></pre>
<p>The <strong>Controller Pattern</strong> is a common architectural pattern used in interactive applications to manage user input by directing it to other components for processing and coordination. The <code>play</code> controller will be the one coordinating the actions once the user calls the function from the interface.</p>
<h4 id="heading-modularity-and-separatation-of-concerns-soc"><em>Modularity and Separatation of Concerns (SoC)</em></h4>
<p>However, this one is very <strong>modular</strong>, following a structured procedural flow.</p>
<p>That modularity could be associated to achieving a better Separation of Concerns, or SoC. SoC involves designing code so each section of the code is handled by a different operational unit. Despite its size, this code shows attention to that principle in the form of functions.</p>
<h4 id="heading-procedural-programming"><em>Procedural Programming</em></h4>
<p><strong>Procedural programming</strong> is a form of imperative programming where procedures are delegated to functions. The way the project is designed is more in accordance to those practices.</p>
<h4 id="heading-finalizing-the-diagram-and-comparison-to-previous-projects">Finalizing the diagram and comparison to previous projects</h4>
<p>The functional operations of the functions called by the controller are more evident from the final diagram for this project:</p>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors” by B. Traversy - Final SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
data@{shape: lean-r, label: "&lt;code style='color:#f1ce32'&gt;scoreboard&lt;/code&gt; object"} -.-o displayFunc
step05 --&gt; data
subgraph clickEventListener[Click Event]
    subgraph eventHandler["&lt;code style="color:orange"&gt;play&lt;/code&gt;&lt;br/&gt;event handler"]
        step01@{label: "step01 - prepare display"} --&gt; step02@{label: "step02 - compute\ncomputer choice"}
        step02 --&gt; step03@{label: "step03 - compute winner"} --&gt; step04@{label: "step04 - result handling and presentation"}
    end
end
getComp@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;getComputerChoice&lt;/code&gt;&lt;br/&gt;computer choice func"} -.-o step02
getWinner@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;getWinner&lt;/code&gt;&lt;br/&gt;winner calc func"} -.-o step03
subgraph displayFunc["&lt;code style="color:orange"&gt;showWinner&lt;/code&gt;&lt;br/&gt;display func"]
    step05@{label: "stpp01 - update stored data"} --&gt; step06@{label: "step02 - show results\nin a modal"}
end
displayFunc -.-o step04
end
init -.-o |"(1) initialize values and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers:&lt;code style="color:orange"&gt;playGame clearModal restartGame&lt;/code&gt;)"|interface@{shape: curv-trap, label: "interface display"}
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
step06 --&gt; |"(3) animations, results"| interface
restartGame@{shape: rounded} --&gt; |"(4a) restart data"| data
restartGame --&gt; |"(4b) clean interface"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      stepLegend["----step----"]
      style stepLegend fill:#474949, stroke: #CCC, color:#CCC
      end
end
end

interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,displayFunc subProc
classDef step fill:#474949, stroke: #CCC, color:#CCC
class step01,step02,step03,step04,step05,step06 step
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746311699920/0c89b8c0-aa93-418f-858f-171006c4bacc.jpeg" alt="Final SB Diagram of the RPS game by B. Traversy" class="image--center mx-auto" width="618" height="900" loading="lazy"></p>
<p>Let’s point out the different steps carried out by the delegated functions:</p>
<ul>
<li><p>The <code>getComputerChoice</code> is in charge of calculating the computer’s choice.</p>
</li>
<li><p>Results are collected by the controller and passed to another function, <code>getWinner</code>, which is in charge of calculating the winner.</p>
</li>
<li><p>Then the results of the <code>getWinner</code> function are collected by the controller and passed to the next function, <code>showWinner</code>.</p>
</li>
<li><p>The <code>showWinner</code> function has a similar design as the one made by Hmz C for his project. It will have just one less responsibility, and will take only responsibilities for data handling of a slightly more complex data type for the capture and maintance of the current score (the <code>scoreboard</code> object), as well as displaying results.</p>
</li>
</ul>
<p>Despite of the differences in the way the code was designed, the order of the steps is very similar to the projects we have discussed before.</p>
<p>Other features different from the previous code examples are the existence of a modal for result presentation and a reset functionality. We are not discussing them as they fall outside of the scope of the analyses, which is the design of the Rock, Paper, Scissors game.</p>
<h3 id="heading-4-rock-paper-scissors-oop-by-damian-a-project-written-in-oop">4. “Rock Paper Scissors OOP” by Damian: A project written in OOP</h3>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/CvtS/embed/oNXqJoZ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p>Damian has 3 projects on CodePen, all of them made in 2020. One of them is this game. The JavaScript code implements another variation of the "Rock, Paper, Scissors" game but with a more object-oriented approach using classes to manage the game logic, player choices, and statistics.</p>
<p>This is a well-organized, object-oriented version of the "Rock, Paper, Scissors" game with a clean separation of concerns (result calculation, player/computer choice, and game statistics). The use of animations and UI interactions enhances the user experience.</p>
<p>Key features of this project are:</p>
<ul>
<li><p><strong>Object-oriented design</strong>: The code is structured into classes, making it modular, reusable, and easier to maintain.</p>
</li>
<li><p><strong>Interactive UI</strong>: It dynamically updates the player’s and computer’s choices using visual cues (CSS classes) and shows animations for draws.</p>
</li>
<li><p><strong>Statistics</strong>: The game tracks and updates the number of wins, draws, and losses, which are displayed in real-time.</p>
</li>
</ul>
<h3 id="heading-analysis-of-the-project-3">Analysis of the Project</h3>
<h4 id="heading-quickly-discovering-the-start-and-end-of-the-workflow">Quickly discovering the start and end of the workflow</h4>
<p>This code seems more complex than what we’ve previously examined. But a closer look suggests certain resemblance to the previous ones, at least at a high level of generalization:</p>
<ul>
<li><p>Again, variables and states are initialized once the project is accessed.</p>
</li>
<li><p>One of the variables that’s initialized when the project is accessed is an instance of a class, the <code>Game</code> class. The instance is called <code>newGame</code>.</p>
</li>
</ul>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors OOP” by Damian - Simple SB-Diagram 01
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
    gameInst@{shape: fr-rect, label: "Game instance"}
end
gameInst -.-o |"(1) instantiate classes, values, and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (events: &lt;code&gt;startGame&lt;/code&gt; method bound to &lt;code&gt;this&lt;/code&gt;)"|interface
gameInst --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; gameInst
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| gameInst
gameInst --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class gameInst inst
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746311035208/e9dee7a0-2428-4c08-bc41-37b81846ae34.jpeg" alt="First Simple SB-Diagram of the RPS game by Damian" class="image--center mx-auto" width="523" height="939" loading="lazy"></p>
<p>Again, we can see aspects of this workflow that coincide with what we found in the previous projects – but this time those similarities are more difficult to capture. As in the previous code, the <strong>controller pattern</strong> is prevalent in this code, but instead of being associated with an anonymous function or a global one, it is now a <strong>class’s module</strong>.</p>
<p>The instantiation of the class runs the constructor which includes the immediate assignment of click event handlers into the interface (just as with the other projects). They use a module of the class, the <code>startGame</code> module, as a callback function.</p>
<p>The (syntactic) approach that Damian chose adds additional complexities to the code in terms of scope definition. You can spot some of the complexities by realizing the need to use the JavaScript <code>bind</code> method in order to keep the <code>this</code> context of the module function the same as the one of the instance when assigned to the event.</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Game</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.optionsImg = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.img'</span>);
    <span class="hljs-built_in">this</span>.optionsBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'button'</span>);

    <span class="hljs-built_in">this</span>.optionsBtns.forEach(<span class="hljs-function"><span class="hljs-params">option</span> =&gt;</span> option.addEventListener(<span class="hljs-string">'click'</span>, 
                                                                <span class="hljs-built_in">this</span>.startGame.bind(<span class="hljs-built_in">this</span>) <span class="hljs-comment">//see the bind here</span>
                                                                )
                            )

    <span class="hljs-built_in">this</span>.youWins = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.results &gt; .you-win'</span>);
    <span class="hljs-built_in">this</span>.draw = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.results &gt; .draw'</span>);
    <span class="hljs-built_in">this</span>.PcWins = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.results &gt; .pc-win'</span>);

    <span class="hljs-built_in">this</span>.stats = <span class="hljs-keyword">new</span> Stats(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    <span class="hljs-built_in">this</span>.render.call(<span class="hljs-built_in">this</span>, <span class="hljs-built_in">this</span>.stats.getStats());
  }
</code></pre>
<p>In order to represent that, I used a colored link, indicating that the game instance (through the constructor) is set to be the <code>this</code> context of the module.</p>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors OOP” by Damian - Simple SB-Diagram 02
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
    subgraph gameInst[&lt;code&gt;Game instance&lt;/code&gt;]
        gameConstrt@{shape: fr-rect, label: "&lt;code style="color:orange;"&gt;Game constructor&lt;/code&gt;"}
        startGame@{shape: fr-rect, label: "&lt;code style="color:orange;"&gt;startGame&lt;/code&gt;&lt;br/&gt;method / event handler"}
        gameConstrt -.-o startGame 
    end
end
gameConstrt -.-o |"(1) instantiate classes, values, and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (events: &lt;code style="color:orange;"&gt;startGame&lt;/code&gt; method bound to &lt;code&gt;this&lt;/code&gt;)"|interface
startGame --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; gameInst
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| startGame
gameInst --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
        start3[ ] -.-o|"context of"| stop3[ ]
        linkStyle 8 stroke:yellow, stroke-width:5px
        style start3 height:0px;
        style stop3 height:0px;
      end
      subgraph shapes
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,gameConstrt subProc
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class gameInst inst
linkStyle 0 stroke:yellow, stroke-width: 5px
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746310389266/a078bebe-dee4-47b1-a5e8-c164008878e9.jpeg" alt="Second Simple SB-Diagram of the RPS game by Damian" class="image--center mx-auto" width="526" height="940" loading="lazy"></p>
<h4 id="heading-refinement-3">Refinement</h4>
<p>By adding the evaluation of the classes, the flow diagram gets more detailed but also more complicated.</p>
<p>Damian defined 4 classes for this project: <code>Result</code>, which was just a static method, <code>Choice</code>, <code>Stats</code> and <code>Game</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Result</span> </span>{
  <span class="hljs-keyword">static</span> whoWin(yourChoice, PCchoice) {
    ...
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Choice</span> </span>{
  <span class="hljs-keyword">constructor</span>(yourChoice) {
    ...
  }

  getYourChoice = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.yourChoice;
  getPcChoice = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.PCchoice;

  drawPcChoice() {
    ...
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Stats</span> </span>{
  <span class="hljs-keyword">constructor</span>(wins, draws, loses) {
    ...
  }
  getStats = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.status;

  refreshStats(result) {
    ...
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Game</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    ...
  }

  startGame(e) {
    ...
  }

  render(stats) {
    ...
  }
}

<span class="hljs-keyword">const</span> newGame = <span class="hljs-keyword">new</span> Game();
</code></pre>
<p>The best way to keep the diagram simpler is not to detail all the steps of the functionalities. But since I’m mentioning instances, I also felt the urgency to relate those to the corresponding class. I did it using a different line type and color.</p>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors OOP” by Damian - SB-Diagram Refinement
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
    subgraph gameInst[&lt;code&gt;Game instance&lt;/code&gt;]
        gameConstrt@{shape: fr-rect, label: "&lt;code style="color:orange;"&gt;Game constructor&lt;/code&gt;"}
        startGame@{shape: fr-rect, label: "&lt;code style="color:orange;"&gt;startGame&lt;/code&gt;&lt;br/&gt;method / event handler"}
        render@{shape: fr-rect, label: "&lt;code style="color:orange;"&gt;render&lt;/code&gt;\ndisplay method"}
        gameConstrt -.-o startGame
    end
    classGame@{shape: rounded, label: "Game class"} -.- gameInst
    classStat@{shape: rounded, label: "Stat class"} -.-o gameConstrt
    classResult@{shape: rounded, label: "Result class"} -.-o startGame
    classChoice@{shape: rounded, label: "Choice class"} -.-o startGame
    classStat -.-o render
end
gameConstrt -.-o |"(1) instantiate classes, values, and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (events: &lt;code style="color:orange;"&gt;startGame&lt;/code&gt; method bound to &lt;code&gt;this&lt;/code&gt;)"|interface
gameInst --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; gameInst
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| startGame
gameInst --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
        start3[ ] -.-o|"context of"| stop3[ ]
        linkStyle 13 stroke:yellow, stroke-width:5px
        style start3 height:0px;
        style stop3 height:0px;
        start4[ ] -.-|"instance of"| stop4[ ]
        linkStyle 14 stroke:skyblue, stroke-width:4px
        style start4 height:0px;
        style stop4 height:0px;
      end
      subgraph shapes
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      stepLegend["----step----"]
      style stepLegend fill:#474949, stroke: #CCC, color:#CCC
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef isaclass fill:grey
class classGame,classChoice,classResult,classStat isaclass
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,gameConstrt subProc
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class gameInst inst
linkStyle 0,5 stroke:yellow, stroke-width: 5px
linkStyle 1 stroke:skyblue, stroke-width:4px
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746308587568/b17961e2-8c97-4446-956c-85fe1a8b8630.jpeg" alt="b17961e2-8c97-4446-956c-85fe1a8b8630" class="image--center mx-auto" width="543" height="936" loading="lazy"></p>
<p>With the diagram above, I tried to highlight some exceptional aspects of the architecture of this code. At some point, a <code>Stats</code> class’s instance will be context for the <code>render</code> method of the <code>Game</code> class’s instance. I added connections between classes and the workflow to indicate that the classes were used on those steps of the workflow.</p>
<h4 id="heading-oop-encapsulation">OOP Encapsulation</h4>
<p>One way to improve the SoC of your project is through encapsulation. <strong>Encapsulation</strong> is a concept usually related to OOP and it’s about keeping data and the methods that work with that data bundled in the same class.</p>
<p>This project shows efforts to achieve encapsulation:</p>
<ul>
<li><p>The <code>Stats</code> instance it is the one that will store the data, and the methods are in accordance to its task:</p>
<pre><code class="lang-javascript">  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Stats</span> </span>{
    <span class="hljs-keyword">constructor</span>(wins, draws, loses) {
      <span class="hljs-built_in">this</span>.status = {
        <span class="hljs-attr">wins</span>: wins,
        <span class="hljs-attr">draws</span>: draws,
        <span class="hljs-attr">loses</span>: loses,
      }
    }
    getStats = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.status;

    refreshStats(result) {
      <span class="hljs-keyword">switch</span> (result) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">"win"</span>:
          <span class="hljs-built_in">this</span>.status.wins++;
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"draw"</span>:
          <span class="hljs-built_in">this</span>.status.draws++;
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"lose"</span>:
          <span class="hljs-built_in">this</span>.status.loses++;
          <span class="hljs-keyword">break</span>;
      }
    }
  }
</code></pre>
</li>
<li><p>The <code>Choice</code> instance will be dedicated to the calculation of the choices, and so it contains methods that respond to that task:</p>
<pre><code class="lang-javascript">  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Choice</span> </span>{
    <span class="hljs-keyword">constructor</span>(yourChoice) {
      <span class="hljs-built_in">this</span>.yourChoice = yourChoice;
      <span class="hljs-built_in">this</span>.PCchoice = <span class="hljs-built_in">this</span>.drawPcChoice();
    }

    getYourChoice = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.yourChoice;
    getPcChoice = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.PCchoice;

    drawPcChoice() {
      <span class="hljs-keyword">const</span> options = [<span class="hljs-string">"rock"</span>, <span class="hljs-string">"paper"</span>, <span class="hljs-string">"scissors"</span>];

      <span class="hljs-keyword">return</span> options[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * options.length)];
    }
  }
</code></pre>
</li>
</ul>
<h4 id="heading-finalizing-the-diagram-and-comparison-to-previous-projects-1">Finalizing the diagram and comparison to previous projects</h4>
<p>For the final diagram of the project, I wanted to:</p>
<ul>
<li><p>Emphasize the importance of the <strong>methods</strong> in the classes by mentioning them in their corresponding class, and</p>
</li>
<li><p>Indicate the apparent importance of the <code>Stats</code> instance in the workflow.</p>
</li>
</ul>
<p>Here the final diagram of this project:</p>
<pre><code class="lang-mermaid">---
title: “Rock Paper Scissors OOP” by Damian - Final SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
    subgraph gameInst[&lt;code&gt;Game instance&lt;/code&gt;]
        subgraph gameConstrt["&lt;code style="color:orange;"&gt;Game constructor&lt;/code&gt;"]
            init@{shape: hex, label: "Initial Settings"} --&gt; statInst@{shape: fr-rect, label: "&lt;code&gt;Stat instance&lt;/code&gt;"}
        end
        subgraph eventHandler["&lt;code style="color:orange;"&gt;startGame&lt;/code&gt;&lt;br/&gt;method / event handler"]
            step01[step01 - animation] --&gt; step02[step02 - calculate computer choice]
            step02 --&gt; step03[step03 - prepare display]
            step03 --&gt; step04[step04 - calculate winner and update data]
            step04 --&gt; step05[step05 - show result]
        end
        gameConstrt -.-o eventHandler
        statInst -.-o |"use calc'd winner to update &lt;code style='color:#f1ce32'&gt;status&lt;/code&gt; running &lt;code style="color:orange;"&gt;refreshStats&lt;/code&gt;"| step04
        render@{shape: fr-rect, label: "&lt;code style="color:orange;"&gt;render&lt;/code&gt;\ndisplay method"}
    end
    classGame@{shape: rounded, label: "Game class&lt;br/&gt;---------------"} -.- gameInst
    classStat@{shape: rounded, label: "Stat class&lt;br/&gt;-----------------------&lt;br/&gt;+status (obj / data)&lt;br/&gt;-----------------------&lt;br/&gt;+refreshStats()&lt;br/&gt;+getStats(result)"} -.- statInst
    classResult@{shape: rounded, label: "Result class&lt;br/&gt;--------------------------&lt;br/&gt;+&lt;u&gt;whoWin(yourCh, pcCh)&lt;/u&gt;"} -.-o |"use Result &lt;code style='color:orange'&gt;static method&lt;/code&gt; to calculate winner"| step04
    classChoice@{shape: rounded, label: "Choice class&lt;br/&gt;--------------------&lt;br/&gt;+yourCh, pcCh&lt;br/&gt;--------------------&lt;br/&gt;+getYourChoice()&lt;br/&gt;+getPcChoice()&lt;br/&gt;+drawChoice()"} -.-o |use &lt;code style="color:#87f134;"&gt;Choice instance&lt;/code&gt; to draw PC choice| step02
    statInst -.-o render
    render -.-o |"take Stat's &lt;code style='color:#f1ce32'&gt;status&lt;/code&gt; and show &lt;code style='color:#f1ce32'&gt;yourCh&lt;/code&gt; / &lt;code style='color:#f1ce32'&gt;pcCh&lt;/code&gt;&lt;br/&gt;last score"| step05
end
gameConstrt -.-o |"(1) instantiate classes, values, and functions&lt;br&gt;clean interface&lt;br&gt;add event listeners (events: &lt;code style="color:orange;"&gt;startGame&lt;/code&gt; method bound to &lt;code&gt;this&lt;/code&gt;)"|interface
eventHandler --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init
choices@{shape: rounded} --&gt; |"(2) user choice: either paper, rock or scissors"| eventHandler
eventHandler --&gt; |"(3) animations, results"| interface@{shape: curv-trap, label: "interface display"}
subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
        start3[ ] --o|"context of"| stop3[ ]
        linkStyle 20 stroke:yellow, stroke-width:5px
        style start3 height:0px;
        style stop3 height:0px;
        start4[ ] -.-|"instance of"| stop4[ ]
        linkStyle 21 stroke:skyblue, stroke-width:4px
        style start4 height:0px;
        style stop4 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      stepLegend["----step----"]
      style stepLegend fill:#474949, stroke: #CCC, color:#CCC
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef isaclass fill:grey
class classGame,classChoice,classResult,classStat isaclass
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,gameConstrt subProc
classDef step fill:#474949, stroke: #CCC, color:#CCC
class init,step01,step02,step03,step04,step05 step
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class statInst,gameInst inst
linkStyle 5,11 stroke:yellow, stroke-width: 5px
linkStyle 7,8 stroke:skyblue, stroke-width:4px
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746308090111/246b7eae-5f72-4117-9c9a-62571873e112.jpeg" alt="Final SB Diagram of the RPS game by Damian" class="image--center mx-auto" width="490" height="1474" loading="lazy"></p>
<p>What makes this code similar to the previous ones is the following:</p>
<ul>
<li><p>The order of the steps is very similar</p>
</li>
<li><p>A initial step is taken to set variables to initial stages and register an event handler function to the corresponding click events</p>
</li>
<li><p>This project is also imperative in its design</p>
</li>
</ul>
<p>This project shares more similarities to the project by Brad Traversy.</p>
<ul>
<li><p>The design of the event handler is closer to a controller pattern</p>
</li>
<li><p>Therefore, modularity and SoC seem to be concepts that guided the design.</p>
</li>
<li><p>The processing of the operations are organized according to procedural programming.</p>
</li>
</ul>
<p>That’s where the similarities stop.</p>
<ul>
<li><p>The initialization of the game is through the instantiation of a class:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> newGame = <span class="hljs-keyword">new</span> Game();
</code></pre>
<p>  Different to all the previous projects, the event handler / controller is now a method of the instantiated class. The method is then registered as event handler of the click events right from the constructor when the class is instantiated.</p>
</li>
<li><p>Modularity occurs through calling methods of instantiated classes, instead of (globally declared) functions.</p>
</li>
<li><p>By using the OOP paradigm, this project shows a higher level of SoC by adding encapsulation.</p>
</li>
<li><p>There are special cases in this code where it was required to bind to the appropriate this context. Examples are the binding of the <code>Game</code> class methods.</p>
</li>
</ul>
<h3 id="heading-5-recurrent-neural-network-rock-paper-scissors-by-andrew-worcerster-a-project-implementing-ai">5. “Recurrent Neural Network – Rock, Paper, Scissors” by Andrew Worcerster: A Project Implementing AI</h3>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/amwmedia/embed/maowdm" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p> </p>
<p>Andrew Worcester hasn’t been a regular contributor to CodePen but has created about 50 projects since 2013. The project we are going to analyze was created in 2019, one year after the first release of <code>brain.js</code>.</p>
<p>This code implements a more advanced version of the "Rock, Paper, Scissors" game, which uses neural networks (via the <code>brain.js</code> library) to predict the player’s next move and even simulate emotions for the computer opponent based on its win/loss patterns.</p>
<p>This code creates an engaging, AI-driven version of "Rock, Paper, Scissors" with advanced features like move prediction and emotional simulation using neural networks.</p>
<p>Key features of this project are:</p>
<ul>
<li><p><strong>Neural Network for prediction</strong>: a model to predict the player’s next move based on their move history creates an adaptive gameplay experience, where the CPU adjusts its strategy.</p>
</li>
<li><p><strong>Emotion simulation</strong>: another model simulating emotions based on the game results (win/loss history), and it displays appropriate emojis like <code>😫</code>, <code>😊</code>, and so on, next to its moves.</p>
</li>
<li><p><strong>Interactive UI</strong>: The game uses DOM manipulation to display the player’s move, the CPU’s move, the result, and the CPU’s emotional state after each round.</p>
</li>
<li><p><strong>Dynamic scoring</strong>: Both player and CPU scores are updated and displayed after each round.</p>
</li>
</ul>
<h3 id="heading-analysis-of-the-project-4">Analysis of the Project</h3>
<h4 id="heading-quickly-discovering-of-the-start-and-end-of-the-workflow-3">Quickly discovering of the start and end of the workflow</h4>
<p>At a high level of generalization, this code follows the same pattern as the other projects, although the complexity of the functionalities has grown:</p>
<ul>
<li><p>Again, variables and states are initialized once the project is accessed.</p>
</li>
<li><p>Apart from that, an event handler of the click event is assigned carrying an anonymous function as a callback function. The <strong>contoller pattern</strong> seems to reappear in this project too.</p>
</li>
</ul>
<pre><code class="lang-mermaid">---
title: “Recurrent Neural Network – Rock, Paper, Scissors” by A. Worcerster - Simple SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
subgraph clickEventListener["Click Event (&lt;code&gt;forEach&lt;/code&gt; on &lt;code style="color:#f1ce32"&gt;options&lt;/code&gt;)"]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"}
end
end
init --&gt; |"(1) initialize values (dummies for &lt;code style="color:#f1ce32"&gt;cpuNextMove data&lt;/code&gt;) and functions (functions: brain.recurrent.LSTM &lt;code style="color:orange"&gt;net&lt;/code&gt; brain.NeuralNetwork &lt;code style="color:orange"&gt;emoNet&lt;/code&gt;, &lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;)&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers: &lt;code style="color:orange"&gt;anoymous function&lt;/code&gt;)"|interface
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface
end
style start fill:black
style stop fill:black, fill-stroke:white
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746282747010/f1c43834-0558-4d1c-8e71-f09f7f92f2c4.jpeg" alt="RPSBrain 01 - Simple SB-Diagram of the RPS game by A. Worcester" class="image--center mx-auto" width="764" height="939" loading="lazy"></p>
<p>A key difference from previous projects is the <strong>training of the neural network operations</strong>. Some of those functionalities are initialized to a initial state even before the user clicks.</p>
<h4 id="heading-refinement-4">Refinement</h4>
<p>If we still keep a high level of generalization, we can see that several of the responsibilities of the workflow will be taken by functions and even operations outside the event handler.</p>
<p>The project has several <code>brain.js</code> functionalities in place. The most evident ones are:</p>
<ul>
<li><p>The LSTM (Long Short Term Memory), which it is a neural network designed to capture associations and patterns that are separated in time or place - and it is the one associated with the prediction of the user’s next move.</p>
</li>
<li><p>A default feed-forward neural network of just one layer, designed to compare immediate patterns, and it is the one associated with the simulation of the emotions.</p>
</li>
</ul>
<p>The LSTM is used by another function, <code>calcNextMove</code>, to provide an immediate result even before the user interacts with the click event: a value for the <code>cpuNextMove</code> variable.</p>
<p><code>cpuNextMove</code> is indeed the <strong>calculation of the computer’s choice</strong> and it occurs <em>before</em> the user clicks, starting with a default value based on fake data.</p>
<pre><code class="lang-mermaid">---
title: “Recurrent Neural Network – Rock, Paper, Scissors” by A. Worcerster - SB-Diagram Refinement 01
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
constvar@{shape: lean-r, label: "&lt;span style="color:#f1ce32"&gt;constants and variables&lt;/span&gt;"} -.-o clickEventListener
LSTM@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;net&lt;/code&gt;&lt;br/&gt;LSTM"} -.-o calcNextMove
calcNextMove@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;&lt;br/&gt;computer choice"} --&gt; cpuNextMove@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;cpuNextMove&lt;/code&gt;"}
NN@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;emoNet&lt;/code&gt;&lt;br/&gt;NN"} -.-o clickEventListener
cpuNextMove -.-o clickEventListener
subgraph clickEventListener["Click Event&lt;br/&gt;(&lt;code style="font-size:13px"&gt;forEach&lt;/code&gt; on &lt;code style="color:#f1ce32"&gt;options&lt;/code&gt;)"]
    eventHandler@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"}
end
end
init --&gt; |"(1) initialize values (dummies for &lt;code style="color:#f1ce32"&gt;cpuNextMove data&lt;/code&gt;) and functions (functions: brain.recurrent.LSTM &lt;code style="color:orange"&gt;net&lt;/code&gt; brain.NeuralNetwork &lt;code style="color:orange"&gt;emoNet&lt;/code&gt;, &lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;)&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers: &lt;code style="color:orange"&gt;anoymous function&lt;/code&gt;)"|interface
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class NN,LSTM inst
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746282227818/020fd3c7-3864-4e5c-8af1-de185a04484f.jpeg" alt="First Refinement of SB Diagram of the RPS game by A. Worcerster" class="image--center mx-auto" width="828" height="1135" loading="lazy"></p>
<h4 id="heading-further-refinement">Further Refinement</h4>
<p>This project has some complexities not seen in previous projects, so I will try to go through it slowly. Let’s first see another level of refinement where the steps are shown:</p>
<pre><code class="lang-mermaid">---
title: “Recurrent Neural Network – Rock, Paper, Scissors” by A. Worcerster - SB-Diagram Refinement 02
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
constvar@{shape: lean-r, label: "&lt;span style="color:#f1ce32"&gt;constants and variables&lt;/span&gt;"} -.-o clickEventListener
step05 --&gt; data@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;data&lt;/code&gt;"}
data -.-o calcNextMove
subgraph step00["stepInit - calculate computer choice"]
    calcNextMove@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;&lt;br/&gt;computer choice"} --&gt; cpuNextMove@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;cpuNextMove&lt;/code&gt;"}
    LSTM@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;net&lt;/code&gt;&lt;br/&gt;LSTM instance"} -.-o calcNextMove
    cpuNextMove -.-o Dout((A))
    cpuNextMove -.-o Eout((B))
end
NN@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;emoNet&lt;/code&gt;&lt;br/&gt;NN"} -.-o step03
subgraph clickEventListener["Click Event&lt;br/&gt;(&lt;code style="font-size:13px"&gt;forEach&lt;/code&gt; on &lt;code style="color:#f1ce32"&gt;options&lt;/code&gt;)"]
    subgraph eventHandler["&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"]
        step01["step01 - prepare a scoring table for that option"] --&gt; step02["step02 - calculate winner"]
        step02 --&gt; step03["step03 - calculate cpu emotion"]
        step03 --&gt; step04["step04 - result handling and presentation: Winner"]
        step04 --&gt; step05["step05 - update data"]
        step05 --&gt; step06["step06 - recalculate winner and prepare display"]
        step06 --&gt; step07["step07 - calc next move; result handling and presentation: Scores"]
        Din((A)) -.-o step02
        Ein((B)) -.-o step06    
    end
end
end
init --&gt; |"(1) initialize values (dummies for &lt;code style="color:#f1ce32"&gt;cpuNextMove data&lt;/code&gt;) and functions (functions: brain.recurrent.LSTM &lt;code style="color:orange"&gt;net&lt;/code&gt; brain.NeuralNetwork &lt;code style="color:orange"&gt;emoNet&lt;/code&gt;, &lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;)&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers: &lt;code style="color:orange"&gt;anoymous function&lt;/code&gt;)"|interface
clickEventListener --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,gameConstrt subProc
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class NN,LSTM inst
classDef step fill:#474949, stroke: #CCC, color:#CCC
class step00,step01,step02,step03,step04,step05,step06,step07 step
classDef connectorcpuNextMove fill:skyblue, color:black
class Dout,Din,Eout,Ein connectorcpuNextMove
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746280491504/2cbb972c-535d-4a56-b5d7-23fb4f3eba63.jpeg" alt="Second Refinement of SB Diagram of the RPS game by A. Worcerster" class="image--center mx-auto" width="831" height="1655" loading="lazy"></p>
<p>We can see from the diagram that:</p>
<ol>
<li><p>The <code>cpuNextMove</code> is pre-calculated, and so the calculation of the computer’s choice occurs <em>before</em> the user’s next click. I have referred to this step as “stepInit” to indicate that the first calculation occurs at initialization.</p>
</li>
<li><p>After the choice of the user is captured, its value and the value of <code>cpuNextMove</code> are eventually used to calculate the winner. The result is stored to keep the history of computer scoring.</p>
</li>
<li><p>The history of computer scoring is the input used for the calculation of the emotions using the <code>emoNet</code>.</p>
</li>
<li><p>The user’s choice is then used to create the next entry for data, which keeps the history of user scoring within the <code>data</code> in a dedicated format.</p>
</li>
<li><p>After calculating the winner and displaying the results, the project will use data to calculate the cpuNextMove <em>before</em> the user clicks again.</p>
</li>
</ol>
<h4 id="heading-finalizing-the-diagram-and-comparison-to-previous-projects-2">Finalizing the diagram and comparison to previous projects</h4>
<p>We could have finished with the previous diagram as it is largerly refined, but I wanted to practice another level of refinement to reveal the role of that the <em>data flow</em> has in this project. This resulted in a more complex diagram that those previously shown:</p>
<pre><code class="lang-mermaid">---
title: “Recurrent Neural Network – Rock, Paper, Scissors” by A. Worcerster - Final SB-Diagram
config:
    theme: dark
    layout: "elk"
    flowchart:
        nodeSpacing: 50
        rankSpacing: 50
---
flowchart TD
subgraph _
subgraph main
init
constvar@{shape: lean-r, label: "&lt;span style="color:#f1ce32"&gt;constants and variables&lt;/span"} -.-o clickEventListener
step05 --&gt; data@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;data&lt;/code&gt;"}
cpuWinLoss@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;cpuWinLoss&lt;/code&gt;"} -.-o Aout((A))
cpuWinLoss@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;cpuWinLoss&lt;/code&gt;"} -.-o Bout((B))
cpuWinLoss@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;cpuWinLoss&lt;/code&gt;"} -.-o Cout((C))
subgraph step00["stepInit - calculate computer choice"]
    calcNextMove@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;&lt;br/&gt;computer choice"} --&gt; cpuNextMove@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;cpuNextMove&lt;/code&gt;"}
    LSTM@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;net&lt;/code&gt;&lt;br/&gt;LSTM instance"} -.-o calcNextMove
    cpuNextMove -.-o Dout((A))
    cpuNextMove -.-o Eout((B))
end
data -.-o calcNextMove
delay01 --&gt; calcNextMove  
NN@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;emoNet&lt;/code&gt;&lt;br/&gt;NN instance"} -.-o step03
subgraph clickEventListener["Click Event&lt;br/&gt;(&lt;code style="font-size:13px"&gt;forEach&lt;/code&gt; on &lt;code style="color:#f1ce32"&gt;options&lt;/code&gt;)"]
    subgraph eventHandler["&lt;code style="color:orange"&gt;anonymous func&lt;/code&gt;&lt;br/&gt;event handler"]
        scoreValues@{shape: lean-r, label: "&lt;code style="color:#f1ce32"&gt;scoreValues&lt;/code&gt;"}
        step01["step01 - prepare a scoring table for this specific option"] --&gt; step02["step02 - calculate winner and update of cpu history"]
        step01 -.-o scoreValues
        subgraph step03["step03 - calculate cpu emotion"]
           likely@{shape: fr-rect, label: "&lt;code style="color:orange"&gt;brain.likely&lt;/code&gt;&lt;br/&gt;estimator func"} ~~~ nonode01[ ]
           style nonode01 height:0px;
        end

        Din((A)) -.-o step02
        scoreValues -.-o step02
        step02 --&gt; step03
        step03 --&gt; step04["step04 - result handling and presentation: Winner"]
        scoreValues -.-o step04
        step04 --&gt; step05["step05 - update data"]
        step05 --&gt; step06["step06 - recalculate of winner and prepare display"]
        subgraph step07["step07 - calc next move"]
             delay01@{shape: delay, label: outer setTimeout}
            subgraph step08["step08 - result handling and presentation: Scores"]
                delay02@{shape: delay, label: inner setTimeout}
            end
        end

        Cin((C)) -.-o delay02
        step06 --&gt; delay01
        delay02 -.-o delay01
        Ain((A)) -.-o step02
        Bin((B)) -.-o likely
        Ein((B)) -.-o step04

    end
end
end
init --&gt; |"(1) initialize values (dummies for &lt;code style="color:#f1ce32"&gt;cpuNextMove data&lt;/code&gt;) and functions (functions: brain.recurrent.LSTM &lt;code style="color:orange"&gt;net&lt;/code&gt; brain.NeuralNetwork &lt;code style="color:orange"&gt;emoNet&lt;/code&gt;, &lt;code style="color:orange"&gt;calcNextMove&lt;/code&gt;)&lt;br&gt;clean interface&lt;br&gt;add event listeners (event handlers: &lt;code style="color:orange"&gt;anoymous function&lt;/code&gt;)"|interface
eventHandler --&gt; stop@{shape: framed-circle}
start@{shape: start} --&gt; init@{shape: hex, label: "Initial Settings"}
choices --&gt; |"(2) user choice: either paper, rock or scissors"| clickEventListener
clickEventListener --&gt; |"(3) animations, results"| interface

subgraph legend
      direction LR
      subgraph flowlines
        start1[ ] --&gt;|"next step"| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.-o|"assignation"| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
      end
      subgraph shapes
      variable["variable"]
      style variable width:135px, fill:none, stroke:none, color:#f1ce32
      subprocess[&lt;code style="color:orange;"&gt;subprocess&lt;/code&gt;]
      instance[&lt;code style="color:#87f134;"&gt;-instance-&lt;/code&gt;]
      style instance stroke:violet, stroke-width: 3px;
      end
end
end
interface ~~~ legend

style start fill:black
style stop fill:black, fill-stroke:white
classDef subProc fill:#1f2020, stroke: #CCC, color:#CCC
class eventHandler,gameConstrt subProc
classDef step fill:#474949, stroke: #CCC, color:#CCC
class step00,step01,step02,step03,step04,step05,step06,step07,step08 step
classDef inst fill:#1f2020, color:#87f134, stroke:violet, stroke-width: 3px
class LSTM,NN inst
classDef connectorcpuWinLoss fill:white, color:black
class Aout,Ain,Bout,Bin,Cout,Cin connectorcpuWinLoss
classDef connectorcpuNextMove fill:skyblue, color:black
class Dout,Din,Eout,Ein connectorcpuNextMove
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746277838797/066c8372-c20e-4917-acf5-b8a70a743e96.jpeg" alt="Final SB Diagram of the RPS game by A. Worcerster" class="image--center mx-auto" width="747" height="1707" loading="lazy"></p>
<p>I think the use of the data in this project deserved additional explanation. In fact, it is not only the use of machine learning procedures that makes this project more complex – but also its associated use of more advanced data structures.</p>
<p>Here’s a selection of some possible values for some of the variables used to collect data at different points of the workflow:</p>
<pre><code class="lang-javascript">data structures:

<span class="hljs-string">"cpuNextMove: "</span> <span class="hljs-string">"👊"</span>

<span class="hljs-string">"cpuEmotions: "</span> <span class="hljs-comment">// [object Array] (4)</span>
[<span class="hljs-string">"😟"</span>,<span class="hljs-string">"😨"</span>,<span class="hljs-string">"😕"</span>,<span class="hljs-string">"🤨"</span>]

<span class="hljs-string">"data: "</span> <span class="hljs-comment">// [object Array] (3)</span>
[<span class="hljs-comment">// [object Array] (4)</span>
[<span class="hljs-string">"✌️"</span>,<span class="hljs-string">"👊"</span>,<span class="hljs-string">"✋"</span>,<span class="hljs-string">"✌️"</span>],<span class="hljs-comment">// [object Array] (4)</span>
[<span class="hljs-string">"👊"</span>,<span class="hljs-string">"✋"</span>,<span class="hljs-string">"✌️"</span>,<span class="hljs-string">"✌️"</span>],<span class="hljs-comment">// [object Array] (4)</span>
[<span class="hljs-string">"✋"</span>,<span class="hljs-string">"✌️"</span>,<span class="hljs-string">"✌️"</span>,<span class="hljs-string">"✋"</span>]]

<span class="hljs-string">"scoreValues: "</span> <span class="hljs-comment">// [object Object] </span>
{
  <span class="hljs-string">"✋"</span>: <span class="hljs-number">0.3</span>,
  <span class="hljs-string">"✌️"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-string">"👊"</span>: <span class="hljs-number">0</span>
}

<span class="hljs-string">"cpuWinLoss: "</span> <span class="hljs-comment">// [object Array] (5)</span>
[<span class="hljs-number">0.3</span>,<span class="hljs-number">0.3</span>,<span class="hljs-number">0.3</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>]
</code></pre>
<p>The way <code>data</code> is made (an array of arrays) is a requirement for the LSTM algorithm in this particular case.</p>
<p>Also interesting is the use of the <code>scoreValues</code> object.</p>
<pre><code class="lang-javascript">        <span class="hljs-keyword">const</span> scoreValues = {
            [options[myMoveIdx]]: <span class="hljs-number">0.3</span>,         <span class="hljs-comment">// tie</span>
            [options[(myMoveIdx + <span class="hljs-number">1</span>) % <span class="hljs-number">3</span>]]: <span class="hljs-number">1</span>, <span class="hljs-comment">// cpu wins</span>
            [options[(myMoveIdx + <span class="hljs-number">2</span>) % <span class="hljs-number">3</span>]]: <span class="hljs-number">0</span>  <span class="hljs-comment">// cpu loses</span>
        };
</code></pre>
<p>Notice how the user’s choice (<code>myMoveIdx</code>) is used to compute the different possible <em>computer’s choices</em> as key, with the values associated to each key being the potential result <em>for the computer</em> given that user’s choice. The values have the form of a “score”:</p>
<ul>
<li><p>0 if the computer lost the match</p>
</li>
<li><p>0.3 if there was a tie</p>
</li>
<li><p>1 if the computer won the match</p>
</li>
</ul>
<p>With this object, finding if the computer won, lost or drew consists in finding the value, or score, which key is equals to <code>cpuNextMove</code>. In other words, <code>scoreValues</code> is a template used to calculate the winner.</p>
<p>The computer’s score will be then saved in an array, <code>cpuWindLoss</code>:</p>
<pre><code class="lang-javascript">cpuWinLoss.push(scoreValues[cpuNextMove]);
</code></pre>
<p>Values of the <code>cpuWinLoss</code> array will be eventually used to calculate the emotions. <code>cpuEmotions</code> is an array of emotion predictions based on the last 4 CPU scores. The predictions are obtained from a “table of emotions” that was arbitrarily suggested by the author (the <code>emo</code> object).</p>
<p>Aspects of this project that we can relate to previous ones are:</p>
<ul>
<li><p>It is made using imperative programming.</p>
</li>
<li><p>The existence of a event handler with poor modularity makes it more comparable to the way the event handler was designed in the less advanced projects, where the event handler took most of the responsibilities of the operations and the variables and utilities were declared at the global scope.</p>
</li>
<li><p>At a high level of generalization, the essential steps of the game design stay the same:</p>
<ul>
<li><p>a function (<code>calcNextMove</code>) calculates the computer’s choice.</p>
</li>
<li><p>another functionality (the operations around the emotion calculation) takes part in the realization of the “animation” associated with the corresponding display of the results.</p>
</li>
<li><p>An operation to calculate the winner is also present.</p>
</li>
<li><p>This project has also data handling logic, although much more advanced than previous projects</p>
</li>
</ul>
</li>
</ul>
<p>But how some of those steps are realized is very different in this project:</p>
<ul>
<li><p>The computer’s choice is based on the user’s scoring history, not a random function.</p>
</li>
<li><p>The computer’s choice calculation occurs <em>before</em> the user clicks. While the calculation could have been done before user interaction in previous projects without affecting their outcomes (assuming the user wouldn’t have access to the value of the computer’s choice before playing), performing it at click time here might have caused performance issues due to its higher CPU demand compared to the simpler <code>Math.random</code>. Thus, performance likely influenced the design choices.</p>
</li>
<li><p>The calculation of the winner is based not on a typical conditional control flow (<code>if-else</code>) or even a ternary operator, but rather obtained from a search across a customized data structure.</p>
</li>
<li><p>Instead of using logic that generates feedback independently of previous results, this game uses a more advanced process that factors in both the computer’s move and recent game history to generate contextual UI feedback after each turn.</p>
</li>
<li><p>The different implementations for the operations carried out by this project required different types of data structures with different levels of complexity, so data handling was more complex.</p>
</li>
</ul>
<p>If you were able to follow the analysis specifically for this project and want to compare it to a similar project implementing the same game, check the <a target="_blank" href="https://rockpaperscissors-ai.vercel.app/">example provided by brain.js team</a>. You might need to have a basic knowledge of Vue.js though.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>As a developer, you will be exposed to many different lines of creative code yourself. Or how Robert C. Martin pointed it out in his book “Clean Code”:</p>
<blockquote>
<p>“Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code.”</p>
</blockquote>
<p>So, learning to understand other people’s code is an essential skill that every programmer should develop. And this learning only happens by studying other people’s code and trying to reveal their tricks and bugs.</p>
<p>That is why finding good ways to reveal patterns when comparing code is so important. And that has been the main purpose of this project.</p>
<p>When I started, I still wasn’t sure about what to expect from the approach. Let me share with you what I learned.</p>
<p>What I found limiting from this approach was:</p>
<ul>
<li><p>The more complex the code, the more complex the diagram gets, making it difficult to read. And the more difficult the diagram, the more difficult is to work out a diagram with Mermaid.js. You will also have to make more definitions, add more color, use more tricks, and so on in order to seek clarity. And the more “solutions” you have to find to clarify your diagram, the higher the risk of making it less clear.</p>
</li>
<li><p>It takes time and practice. This kind of technique is not something that you learn all at once. You might find yourself making a lot of changes before getting the “right” result. If you are doing this with Mermaid.js or any other scripting tool, you will have to learn to script with them, too.</p>
</li>
<li><p>This approach is not about visualizing algorithms or syntax. I suspect that if you read the analyses of this article with no knowledge about machine learning or object-oriented programming, this analysis likely didn’t teach you how to do machine learning or OOP. To understand those projects you have to already have internalized the “patterns” on how to do either OOP or machine learning, or be in the right moment of your learning curve, in order to be able to extract something from the code.</p>
</li>
</ul>
<p>What I found beneficial from this approach:</p>
<ul>
<li><p>Something that I learned the hard way was that <em>good</em> programming is not only about algorithms and syntax: it is also about structure and organization. And this approach will give you a lot of information about that.</p>
</li>
<li><p>It helps you break the code apart into functional operations. You adopt a “divide and conquer” approach to studying code.</p>
</li>
<li><p>Even if you are not visualizing the algorithms, you learn algorithms. Why? Because the approach forces you to understand the code. In order to find those functional operations, you will find yourself forced to study what some sections of the code are doing. So you will have to study more code.</p>
</li>
<li><p>You learn by comparison. That is an interesting by-product: once you have found different implementations for a similar functional operation, you learn by their differences. The diagrams will then guide you to the sections of the code that are solving <em>that</em> specific problem you are interested in and you’ll be able to compare it within the context of a larger codebase.</p>
</li>
<li><p>The diagrams acts as mnemotechnical artifacts to remember pieces of code. You can use those diagrams to recall a piece of code that was interesting to you, and then you’ll be able to find it more easily than by having to go through the whole code again.</p>
</li>
</ul>
<p>What I found more interesting about this approach is the numerous questions that came to my mind during and after doing the analyses:</p>
<ul>
<li><p><em>Why did the author follow this approach?</em></p>
</li>
<li><p><em>What drove their decisions?</em></p>
</li>
<li><p><em>How could I better organize it if I worked on it?</em></p>
</li>
<li><p><em>Which techniques would I like to combine?</em></p>
</li>
<li><p><em>What should I avoid for this project?</em></p>
</li>
</ul>
<p>And so on.</p>
<p>Another effect was the feeling that I could now easily recognize the same “template” in other projects without having to make the diagrams. It starts to be more natural.</p>
<p>If you feel you want to implement this approach next time you come across several codebases you want to compare, I recommend you do the following:</p>
<ul>
<li><p><strong>Don’t stick only to visuals</strong>. Combine this with other tools. This is just one part of the whole process. For example, use AI to get a textual description of the code and use the diagrams to visualize the flow.</p>
</li>
<li><p><strong>Always go to the code as it is the “source of truth”.</strong> You (or AI, if you’re using it) might have gotten it wrong at certain points, so always check.</p>
</li>
<li><p><strong>Better to compare code examples that are at your level or just a bit higher, and be prepared to challenge yourself by studying new concepts</strong>. For example, if you have just the fundamentals of OOP clear in your head but you haven’t started to work on it, find simple examples at your current level and a few more made in OOP that solve similar problems and compare the approaches. I ensure you that you will start finding the patterns.</p>
</li>
<li><p><strong>Come up with questions</strong>. The analysis might reveal structures and workflows and help you get into new programming techniques, but it won’t tell you if those structures or techniques were the “right” solution. You have to investigate more from that point. Then you can compare theory with practice. And then you will come up with more questions!</p>
</li>
<li><p>Select projects that are not subject to change.</p>
</li>
<li><p><strong>Don’t worry about how old the projects are</strong>. Syntax is not what you are looking to learn from this analysis. It is code structure, organization, and logic. The “map in the mind of the developer”.</p>
</li>
<li><p><strong>Keep it real</strong>. If your goal is to learn to code and your time is limited, bear in mind that this kind of analyses will take extra time. It won’t result in concrete code either. It might be good planning for analyzing new codebases at a high level of generalization. But even if it’s too general, I assure you that the time you devote to it will be worthy.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Talk to Any Database Using AI – Build Your Own SQL Query Data Extractor ]]>
                </title>
                <description>
                    <![CDATA[ Recently, I took a break from writing to focus on my exams. During this time, I had an interesting experience: I had the chance to explain SQL (Structured Query Language) to my peers. While exploring SQL in-depth, I encountered a common frustration: ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/talk-to-databases-using-ai-build-a-sql-query-data-extractor/</link>
                <guid isPermaLink="false">677ef38c05d7f213e7d2a766</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prankur Pandey ]]>
                </dc:creator>
                <pubDate>Wed, 08 Jan 2025 21:52:12 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733842504544/0e9da173-718c-454e-841c-15c148e0fe93.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, I took a break from writing to focus on my exams. During this time, I had an interesting experience: I had the chance to explain SQL (Structured Query Language) to my peers. While exploring SQL in-depth, I encountered a common frustration: writing SQL queries to fetch specific data from a database.</p>
<p>This sparked an idea. What if I could build a tool where I didn’t have to write SQL queries manually? Instead, I could type in plain, natural English and let the database do the heavy lifting for me.</p>
<p>Given that we live in the era of AI, leveraging artificial intelligence was the only way to turn this vision into reality.</p>
<p>In this tutorial, I’ll walk you through creating an AI-powered SQL query data extractor. This tool will enable you to fetch data from a database effortlessly, without writing a single line of SQL code.</p>
<h3 id="heading-what-well-cover"><strong>What we’ll cover:</strong></h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisiteshttpswwwfreecodecamporgnewsreact-best-practices-ever-developer-should-knowheading-prerequsites-amp-tools">Prerequisites &amp; Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-the-app-work">How Does the App Work</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-tools">How to Set Up Your Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-database">How to Set Up the Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-structure-and-features-of-the-app">Structure and Features of the App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-back-end">How to Build the Back End</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-front-end">How to Build the Front End</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-some-important-notes">Some Important Notes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-playing-with-the-database">Playing with the Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites-amp-tools">Prerequisites &amp; Tools</h2>
<p>In this tutorial, we’ll build an AI-powered SQL query data extractor tool. It’ll allow us to interact with a database using natural language, like plain English, and receive the same results as if we had written SQL queries.</p>
<p>Here’s an overview of the tools we’ll use to create this cool app:</p>
<h3 id="heading-database">Database</h3>
<p>The database is a critical component where we’ll store data and later extract it for our AI model to use when performing NLP operations. Instead of hosting a database locally, I chose a cloud-based free database that allows data extraction via REST APIs. For this project, I opted for <a target="_blank" href="http://restdb.io">restdb.io</a> because it offers seamless SQL database provisioning and supports REST APIs.</p>
<h3 id="heading-ai-agent">AI Agent</h3>
<p>An AI Agent will act as the intermediary between the database and the AI model. This agent will manage the AI model’s operations and facilitate seamless communication. For this, I am using <a target="_blank" href="https://www.copilotkit.ai/"><strong>CopilotKit</strong></a>, which simplifies the integration process.</p>
<h3 id="heading-ai-llm-model">AI (LLM) Model</h3>
<p>The AI model translates plain English queries into SQL queries. For this, I am using <a target="_blank" href="https://groq.com/"><strong>GroqAI</strong></a>, which supports various popular AI models and provides the flexibility needed for this project.</p>
<h3 id="heading-nextjs">Next.js</h3>
<p>To develop a web application that supports both frontend and backend functionalities, I chose <strong>Next.js</strong>. It’s an ideal framework for building robust, scalable web apps with server-side rendering capabilities.</p>
<h3 id="heading-deployment">Deployment</h3>
<p>For deployment, you can choose any service. I prefer <strong>Vercel</strong>, as it integrates seamlessly with Next.js and is free for hobby projects.</p>
<p>By combining these tools, we’ll build a powerful, user-friendly application that effortlessly bridges natural language and SQL databases.</p>
<h2 id="heading-what-well-do-here">What We’ll Do Here:</h2>
<p>These are the steps we’ll follow in this tutorial to build our app:</p>
<p><strong>Step 1 – Set Up the Database:</strong> Either set up the database locally, deploy it, and access it, or use an online database tool that allows data access and extraction via REST APIs.</p>
<p><strong>Step 2 – Obtain Cloud API Keys:</strong> Get the necessary API keys for your AI model to enable seamless integration.</p>
<p><strong>Step 3 – Build a Web App:</strong> Create a web application and set up the backend to integrate CopilotKit. Configure it within the app for optimal functionality.</p>
<p><strong>Step 4 – Train CopilotKit on Your Database:</strong> Provide your database’s data to CopilotKit. It will read and understand the data to facilitate natural language processing.</p>
<p><strong>Step 5 – Integrate CopilotKit Chat:</strong> Add the CopilotKit chat interface into your application and configure it to ensure smooth operation.</p>
<p><strong>Step 6 – Test Locally:</strong> Test the app on your local machine to identify and fix any issues.</p>
<p><strong>Step 7 – Deploy the App:</strong> Once everything is working as expected, deploy the application to a hosting platform.</p>
<h2 id="heading-how-does-the-app-work">How Does the App Work?</h2>
<p>Have you ever wondered how writing plain English could allow you to fetch data from a SQL database?</p>
<p>The magic lies in CopilotKit. It lets you create AI-powered copilots that can perform operations on your applications. Think of CopilotKit as your personal AI assistant or chatbot. So how does it work?</p>
<p>Well, first we have CopilotKit which serves as our chatbot powered by advanced AI models.</p>
<p>Then when you provide data to the chatbot, it uses that data to train itself, building an understanding of your database structure and content.</p>
<p>Finally, when a natural language query (like "Who is using this email address?") is inputted, the AI model processes it, translates it into a corresponding SQL query, and retrieves the desired data from the database.</p>
<p>With CopilotKit’s powerful AI capabilities, your application can seamlessly bridge natural language and SQL, making database interactions more intuitive.</p>
<h2 id="heading-how-to-set-up-your-tools">How to Set Up Your Tools</h2>
<p>Now we’ll go through everything you need to set up the project.</p>
<h3 id="heading-1-install-nextjs-and-dependencies"><strong>1. Install Next.js and dependencies</strong>:</h3>
<p>First, you’ll need to create a NextJS app. Go to the terminal and run the following command:</p>
<pre><code class="lang-plaintext">npx create-next-app@latest my-next-app
</code></pre>
<p>Replace <code>my-next-app</code> with your desired project name.</p>
<p>Navigate to the project folder:</p>
<pre><code class="lang-plaintext">cd my-next-app
</code></pre>
<p>Start the development server:</p>
<pre><code class="lang-plaintext">npm run dev
</code></pre>
<p>Open your browser and navigate to <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a> to see your Next.js app in action.</p>
<h3 id="heading-2-install-copilotkit-and-dependencies"><strong>2. Install CopilotKit and dependencies</strong></h3>
<p>Go to the project root folder through the terminal and run the below command. It will install all the important CopilotKit dependencies and other important packages like dotenv and Axios.</p>
<pre><code class="lang-plaintext">npm install @copilotkit/react-ui @copilotkit/react-core dotenv axios
</code></pre>
<ul>
<li><p>The <strong>CopitlotKit</strong> dependency is solely for handling CopilotKit operations and configurations.</p>
</li>
<li><p>The <strong>Dotenv</strong> dependency is used to handle environment variables as we have to keep important keys in the project, such as environment variables.</p>
</li>
<li><p><strong>Axios</strong> is for handling the API calls.</p>
</li>
</ul>
<h3 id="heading-3-set-up-the-database"><strong>3. Set Up the</strong> <strong>Database</strong></h3>
<p>Visit <a target="_blank" href="http://RestDB.io">RestDB.io</a> and either login or create an account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736349488854/435a5574-54b8-40b4-a1e5-f31aa79eeae8.png" alt="restdb.io login page " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Above you can see the login page for RestDB.io you can either log in if you already have an account or create a new account .</p>
<p>Once logged in you will redirected to this page. There you’ll see the button to create a new database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736349634003/840cc3d6-c7e0-474f-9335-eca750aeacc5.png" alt="restdb.io database creation page " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>When you click on the Create New button, a pop will appear. There, you’ll have to enter the database name as shown in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736349886708/c9846627-4351-40e0-a4bd-8342b6b5bf25.png" alt="restdb.io database creation pop-up" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>When you enter the database name, then click “Go”. I have put <strong>demosql</strong> as the database name. At this point, you’ll get your newly created database link as shown in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736350379651/27708c52-c8a0-405c-93d7-374833572007.png" alt="restdb.io database listing page" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Now Click on the database URL it will take you to this page shown in the image :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736350576835/87abd648-1b8d-4d07-b30a-6f1076abdf06.png" alt="restdb.io main database page " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Now it is time to make an API Key for accessing the database. To do this, click on <strong>Settings</strong> and it will take you to a new page shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736352142460/d61be8ac-c78f-4c71-a1f0-dbc230496bc5.png" alt="restdb.io api settings page " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>On this page click on the <strong>Add New</strong> button it will open a pop up shown below in the image:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736352445417/b739b25d-e01d-4b72-b4a6-db3077866a60.png" alt="restdb.io api key creation popup" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Now you can configure your API actions here like GET, POST, PUT, and DELETE, name it whatever you want, and save it. Your database is now ready to interact via the REST API.</p>
<p>Copy the database URL and API KEY and put it into the .env file.</p>
<p>You can add tables, define the schema with columns and data types (for example, VARCHAR, INTEGER), and populate data manually or via uploads (Excel, CSV, or JSON). For this project, we’ve added 21 records.</p>
<h3 id="heading-4-set-up-the-llm-for-action">4. Set Up the LLM for Action:</h3>
<p>This part is pivotal for the project, as we’re setting up the LLM (Large Language Model) to handle the conversion of NLP (plain English) queries into SQL queries.</p>
<p>Numerous LLMs are available in the market, each with its strengths. While some are free, others are paid, which made selecting the right one for this project a challenge.</p>
<p>After extensive experimentation, I chose the <strong>Groq Adapter</strong> because:</p>
<ul>
<li><p>It consolidates various LLMs under a single platform.</p>
</li>
<li><p>It provides access through a unified API key.</p>
</li>
<li><p>It’s compatible with CopilotKit.</p>
</li>
</ul>
<h4 id="heading-how-to-set-up-groq-cloud">How to Set Up Groq Cloud</h4>
<p>To get started with Groq Cloud, <a target="_blank" href="https://console.groq.com/login">visit its website</a> and either login if already have an account or create a new account if you’re new. Once logged in, navigate to the Groq Dashboard.</p>
<p>This is the homepage of groq cloud:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736352733541/92012af5-b3c4-4277-a50f-834c1900a2de.png" alt="groq cloud homepage" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Once logged in, a new page will open that’ll look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736353229314/67313c60-47b8-4f23-b3c0-e46fcdd5201a.png" alt="groq cloud dahsboard page " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>As you can see, the sidebar has an API Keys link. Click on it, and it will open a new page as shown in the image below. You can also select any LLM of your choice which is given at the top right before the view code option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736353347970/3406fa54-ddc2-4a00-8b27-22536486fc64.png" alt="groqcloud api section" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Here, click on the Create API Key button it will open a pop up like you see below. Just enter the name of your API key and click on Submit it will create a new API key for you. Then copy this API key and paste it inside your .env file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736353563741/cd1a185a-2c77-470a-a5ce-eca564cf524a.png" alt="groq cloud api key creation page " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>To enable seamless access to various LLMs on Groq Cloud, generate an API key by going to the Groq API Keys section. Create a new API key specifically for the LLM, ensuring that it is properly configured.</p>
<p>With the LLM set-up and all components ready, you are now prepared to build the project.</p>
<h2 id="heading-structure-and-features-of-the-app">Structure and Features of the App</h2>
<p>We will approach this project in a straightforward way, focusing on simplicity and functionality. The primary goal is to create a basic webpage that allows us to:</p>
<ul>
<li><p>Verify if our API calls were successful.</p>
</li>
<li><p>View the data received from the API.</p>
</li>
<li><p>Interact with the CopilotKit chatbot integrated into the front end.</p>
</li>
</ul>
<h3 id="heading-webpage-structure">Webpage Structure</h3>
<p>Since we have already set up the <strong>Next.js app</strong>, the next step is to build a minimalistic webpage comprising:</p>
<ol>
<li><p><strong>Header Section:</strong> Displays the title of the application.</p>
</li>
<li><p><strong>Main Area:</strong></p>
<ul>
<li><p><strong>Tables:</strong> Show the data fetched from the database.</p>
</li>
<li><p><strong>Status Indicators:</strong> Show the status of API calls and database operations. If there are any issues, such as API or database failures, errors will be displayed in <strong>red text</strong> for clarity.</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-key-features">Key Features</h3>
<ul>
<li><p><strong>Error Handling:</strong> Any failures, such as API or database issues, will be clearly marked with red text for immediate visibility.</p>
</li>
<li><p><strong>Data Presentation:</strong> For demonstration purposes, the entire database will be displayed in neatly structured tables.</p>
</li>
<li><p><strong>CopilotKit Chatbot Integration:</strong> This chatbot will be configured to allow natural language interactions with the database. The <strong>blue-colored ball</strong> on the page represents the <strong>CopilotKit chatbot</strong>. This chatbot is the key interface for interacting with the database.</p>
<ul>
<li><p>Using natural language queries, we can ask questions about the database data.</p>
</li>
<li><p>The chatbot processes these queries, converts them into SQL queries, and fetches the results seamlessly.</p>
</li>
</ul>
</li>
</ul>
<p>The frontend will look something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734711368585/db3bd5fb-fee1-42a3-a638-b5b410c6fe69.png" alt="db3bd5fb-fee1-42a3-a638-b5b410c6fe69" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<h2 id="heading-how-to-build-the-back-end">How to Build the Back End</h2>
<p>Before we start building the back end, you’ll need to put all important credentials into your <strong>.env</strong> file which will look something like this:</p>
<pre><code class="lang-plaintext">NEXT_PUBLIC_COPILOTKIT_BACKEND_URL=http://localhost:3000/api/copilotkit
NEXT_PUBLIC_GROQ_CLOUD_API_KEY=
NEXT_PUBLIC_RESTDB_API_KEY=
NEXT_PUBLIC_RESTDB_BASE_URL=https://demosql-fdcb.restdb.io/rest/demo-data
</code></pre>
<p>So what are all these? Let’s go through them one by one:</p>
<ol>
<li><p><code>NEXT_PUBLIC_COPILOTKIT_BACKEND_URL=</code><a target="_blank" href="http://localhost:3000/api/copilotkit"><code>http://localhost:3000/api/copilotkit</code></a>: This specifies the base URL for the CopilotKit backend API.</p>
<ul>
<li><p>The <code>NEXT_PUBLIC_</code> prefix makes this variable accessible both on the server side and in the client-side code of a Next.js application.</p>
</li>
<li><p>The value <a target="_blank" href="http://localhost:3000/api/copilotkit"><code>http://localhost:3000/api/copilotkit</code></a> indicates the API is running locally during development.</p>
</li>
</ul>
</li>
<li><p><code>NEXT_PUBLIC_GROQ_CLOUD_API_KEY=</code>: This variable is intended to store an API key for a GROQ Cloud service. GROQ Cloud could be related to querying or data processing you will have to paste your own Groq API key.</p>
<ul>
<li>The variable is empty, indicating the API key is not set yet. It will likely need to be filled in with the appropriate value before the application can access the GROQ Cloud service.</li>
</ul>
</li>
<li><p><code>NEXT_PUBLIC_RESTDB_API_KEY=</code>: Intended to hold the API key for accessing a <strong>RESTdb</strong> service. You will have to paste your own Groq API key.</p>
<ul>
<li><p>RESTdb is a database service that provides APIs for database interactions.</p>
</li>
<li><p>The variable is also empty, meaning the key must be filled in with a valid API key for the application to authenticate and interact with the RESTdb service.</p>
</li>
</ul>
</li>
<li><p><code>NEXT_PUBLIC_RESTDB_BASE_URL=</code><a target="_blank" href="https://demosql-fdcb.restdb.io/rest/demo-data"><code>https://demosql-fdcb.restdb.io/rest/demo-data</code></a>: Defines the base URL for interacting with the RESTdb database. This URL will be created when you make your database. Here, I have given the URL of my database.</p>
<ul>
<li><p>The value <a target="_blank" href="https://demosql-fdcb.restdb.io/rest/demo-data"><code>https://demosql-fdcb.restdb.io/rest/demo-data</code></a> points to a specific RESTdb database endpoint called <code>demo-data</code>.</p>
</li>
<li><p>This could be the endpoint where the application fetches or manipulates demo data for testing or development.</p>
</li>
</ul>
</li>
</ol>
<p>We have successfully added the environment variables to our project. Now, it’s time to configure the CopilotKit API backed.</p>
<h3 id="heading-how-to-configure-the-copilotkit-back-end">How to Configure the CopilotKit Back End</h3>
<p>Open your Next.js app in any code editor – I prefer VSCode – and go to the root folder, which looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734968233629/f338c977-02dd-4ee1-ae66-7417f03e026b.png" alt="f338c977-02dd-4ee1-ae66-7417f03e026b" class="image--center mx-auto" width="349" height="597" loading="lazy"></p>
<p>Inside the app folder, make a new folder called <code>api</code>. Inside the API folder, make another folder called <code>copilotkit</code>. Then in there, make a new file called <code>route.js</code> and inside the file paste this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  CopilotRuntime,
  GroqAdapter,
  copilotRuntimeNextJSAppRouterEndpoint,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@copilotkit/runtime"</span>;

<span class="hljs-keyword">import</span> Groq <span class="hljs-keyword">from</span> <span class="hljs-string">"groq-sdk"</span>;

<span class="hljs-keyword">const</span> groq = <span class="hljs-keyword">new</span> Groq({ <span class="hljs-attr">apiKey</span>: process.env.NEXT_PUBLIC_GROQ_CLOUD_API_KEY });
<span class="hljs-built_in">console</span>.log(process.env.NEXT_PUBLIC_GROQ_CLOUD_API_KEY);
<span class="hljs-keyword">const</span> copilotKit = <span class="hljs-keyword">new</span> CopilotRuntime();

<span class="hljs-keyword">const</span> serviceAdapter = <span class="hljs-keyword">new</span> GroqAdapter({
  groq,
  <span class="hljs-attr">model</span>: <span class="hljs-string">"llama-3.1-70b-versatile"</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POST = <span class="hljs-keyword">async</span> (req) =&gt; {
  <span class="hljs-keyword">const</span> { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
    <span class="hljs-attr">runtime</span>: copilotKit,
    serviceAdapter,
    <span class="hljs-attr">endpoint</span>: <span class="hljs-string">"/api/copilotkit"</span>,
  });

  <span class="hljs-keyword">return</span> handleRequest(req);
};
</code></pre>
<p>Here’s a detailed explanation of each part:</p>
<p>This code defines a server-side handler for a Next.js API route using CopilotKit and Groq SDKs. It sets up a runtime environment to process requests to a specified endpoint.</p>
<p><strong>1. Imports:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  CopilotRuntime,
  GroqAdapter,
  copilotRuntimeNextJSAppRouterEndpoint,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@copilotkit/runtime"</span>;

<span class="hljs-keyword">import</span> Groq <span class="hljs-keyword">from</span> <span class="hljs-string">"groq-sdk"</span>;
</code></pre>
<ul>
<li><p><code>CopilotRuntime</code> and <code>GroqAdapter</code>: These are classes from the CopilotKit library used to set up and configure the runtime environment and adapters for AI-based services.</p>
<ul>
<li><p><code>CopilotRuntime</code><strong>:</strong> A runtime environment to manage the CopilotKit operations.</p>
</li>
<li><p><code>GroqAdapter</code><strong>:</strong> Adapts and connects a Groq service (used for querying or data processing) with CopilotKit.</p>
</li>
</ul>
</li>
<li><p><code>copilotRuntimeNextJSAppRouterEndpoint</code>: A utility function to create a handler for a Next.js App Router API endpoint that integrates CopilotKit.</p>
</li>
<li><p><code>Groq</code> from <code>"groq-sdk"</code>: A library for interacting with Groq services is initialized here for querying or processing data.</p>
</li>
</ul>
<p><strong>2. Initialize Groq:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> groq = <span class="hljs-keyword">new</span> Groq({ <span class="hljs-attr">apiKey</span>: process.env.NEXT_PUBLIC_GROQ_CLOUD_API_KEY });
<span class="hljs-built_in">console</span>.log(process.env.NEXT_PUBLIC_GROQ_CLOUD_API_KEY);
</code></pre>
<ul>
<li><p><code>Groq</code> Initialization:</p>
<ul>
<li><p>The <code>Groq</code> an object is created with an API key (<code>NEXT_PUBLIC_GROQ_CLOUD_API_KEY</code>) fetched from environment variables.</p>
</li>
<li><p>This key authenticates the app with the Groq Cloud service.</p>
</li>
</ul>
</li>
<li><p><code>console.log(</code><a target="_blank" href="http://process.env.NEXT"><code>process.env.NEXT</code></a><code>_PUBLIC_GROQ_CLOUD_API_KEY)</code>: Logs the API key to the server console. <strong>Note:</strong> Avoid logging sensitive data in production to ensure security.</p>
</li>
</ul>
<p><strong>3. Initialize CopilotKit Runtime</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> copilotKit = <span class="hljs-keyword">new</span> CopilotRuntime();
</code></pre>
<ul>
<li><code>CopilotRuntime</code> Initialization: Creates an instance of CopilotKit's runtime environment to manage CopilotKit's features and services.</li>
</ul>
<p><strong>4. Configure Service Adapter</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> serviceAdapter = <span class="hljs-keyword">new</span> GroqAdapter({
  groq,
  <span class="hljs-attr">model</span>: <span class="hljs-string">"llama-3.1-70b-versatile"</span>,
});
</code></pre>
<ul>
<li><p><code>GroqAdapter</code>:</p>
<ul>
<li><p>Configures an adapter to connect CopilotKit with Groq.</p>
</li>
<li><p>The <code>model</code> parameter specifies the AI model to use. Here, it is <code>"llama-3.1-70b-versatile"</code>, a versatile language model with 70 billion parameters.</p>
</li>
</ul>
</li>
</ul>
<p><strong>5. Exported POST Handler</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POST = <span class="hljs-keyword">async</span> (req) =&gt; {
  <span class="hljs-keyword">const</span> { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
    <span class="hljs-attr">runtime</span>: copilotKit,
    serviceAdapter,
    <span class="hljs-attr">endpoint</span>: <span class="hljs-string">"/api/copilotkit"</span>,
  });

  <span class="hljs-keyword">return</span> handleRequest(req);
};
</code></pre>
<ul>
<li><p>Defines a <code>POST</code> handler for a Next.js App Router API endpoint.</p>
</li>
<li><p><strong>Key Components:</strong></p>
<ol>
<li><p><code>copilotRuntimeNextJSAppRouterEndpoint</code>:</p>
<ul>
<li><p>Sets up the handler for the <code>/api/copilotkit</code> endpoint.</p>
</li>
<li><p>Takes <code>runtime</code> (CopilotKit) and <code>serviceAdapter</code> (GroqAdapter) as inputs to configure the endpoint's behaviour.</p>
</li>
</ul>
</li>
<li><p><code>handleRequest</code>:</p>
<ul>
<li><p>A function that processes incoming HTTP requests (in this case, <code>POST</code> requests).</p>
</li>
<li><p>This allows the CopilotKit runtime and service adapter to handle requests dynamically.</p>
</li>
</ul>
</li>
</ol>
</li>
<li><p><code>return handleRequest(req);</code>: Invokes the handler and processes the incoming request (<code>req</code>), returning the appropriate response.</p>
</li>
</ul>
<p>How it all works:</p>
<ol>
<li><p>The Groq SDK is initialized with an API key for authentication.</p>
</li>
<li><p>A CopilotKit runtime is set up.</p>
</li>
<li><p>A GroqAdapter connects the runtime to the Groq service with a specified AI model.</p>
</li>
<li><p>The <code>/api/copilotkit</code> endpoint is configured to handle POST requests, pass the requests to CopilotKit's runtime, and return the processed response.</p>
</li>
</ol>
<p>With this setup, you have successfully integrated CopilotKit into your Next.js application. The backend is now fully functional, enabling seamless communication with the database via REST APIs and the CopilotKit interface.</p>
<h2 id="heading-how-to-build-the-front-end">How to Build the Front End</h2>
<p>For the front end, we’ll keep it as simple as we can. We just need a few things to get this project done: we need a Header component and a Table component.</p>
<ol>
<li><p><strong>Header component</strong>: To display the title or description of the application.</p>
</li>
<li><p><strong>Table component</strong>: To visualize the data fetched from the database.</p>
</li>
</ol>
<p>To achieve this, we’ll use ShadCN, a popular frontend component library known for its clean design and ease of use.</p>
<p>ShadCN provides pre-built components that help speed up development without compromising on quality. By leveraging this library, we can focus on functionality while ensuring the UI looks polished and professional.</p>
<h3 id="heading-how-to-install-shadcn-in-a-next-project"><strong>How to Install ShadCN in a Next Project</strong></h3>
<p>Run the following command to install ShadCN components:</p>
<pre><code class="lang-javascript">npx shadcn@latest init
</code></pre>
<p>This command:</p>
<ul>
<li><p>Initialize ShadCN in your project.</p>
</li>
<li><p>Creates a <code>components</code> folder for storing ShadCN components.</p>
</li>
<li><p>Updates the <code>tailwind.config.js</code> file with required configurations.</p>
</li>
</ul>
<p>You will be asked a few questions to configure <code>components.json</code>:</p>
<pre><code class="lang-bash">Which style would you like to use? › New YorkWhich 
color would you like to use as base color? › Zinc
Do you want to use CSS variables <span class="hljs-keyword">for</span> colors? › no / yesadd components
</code></pre>
<p>To add specific components, use the following command:</p>
<pre><code class="lang-bash">npx shadcn@latest add &lt;component-name&gt;
</code></pre>
<p>For example, to add a table component:</p>
<pre><code class="lang-bash">npx shadcn@latest add table
</code></pre>
<p>The <code>components</code> folder now contains a ready-to-use <code>button</code> component.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734970231792/2e5ea193-f829-435e-b4dc-68bd8ce793ca.png" alt="2e5ea193-f829-435e-b4dc-68bd8ce793ca" class="image--center mx-auto" width="349" height="597" loading="lazy"></p>
<p>In the frontend, we have a <code>components</code> folder that contains the Table component. This component is responsible for displaying the database data in a structured tabular format.</p>
<p>Apart from the <code>Table</code> component, there are two additional files in the front end. These files serve different purposes and will be integrated later in the project for specific functionalities.</p>
<p>This modular structure ensures the front end remains clean and organized, making it easier to manage and expand as needed.</p>
<p>Let’s explore each file:</p>
<ol>
<li><p><strong>Table.jsx:</strong> This file is auto-generated by ShadCN when we installed the Table component. It contains the default configuration for the table component provided by the ShadCN library. <strong>Do not modify this file</strong>, as it is essential for the component’s proper functionality.</p>
</li>
<li><p><strong>Tabledata.jsx:</strong> This file is where we populate the table with data fetched from the database through API calls. The <code>Tabledata.jsx</code> file bridges the gap between the backend API and the frontend table display.</p>
</li>
</ol>
<p>Let’s take a closer look at the code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/ui/table"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tabledata</span>(<span class="hljs-params">{ data }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Table</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TableCaption</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-green-600 font-bold ml-8"</span>&gt;</span>
        Live data from database.
      <span class="hljs-tag">&lt;/<span class="hljs-name">TableCaption</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TableHeader</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TableRow</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center "</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>Id<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>name<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>email<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>phone_number<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>address<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>city<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>state<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>zip_code<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span>&gt;</span>country<span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TableHead</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>created at <span class="hljs-tag">&lt;/<span class="hljs-name">TableHead</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">TableRow</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">TableHeader</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TableBody</span>&gt;</span>
        {data.map((db) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">TableRow</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{db._id}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-medium text-wrap w-12"</span>&gt;</span>
              {db._id}
            <span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-medium"</span>&gt;</span>{db.name}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span>&gt;</span>{db.email}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span>&gt;</span>{db.phone_number}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>{db.address}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>{db.city}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>{" "}
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>{db.state}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>{db.zip_code}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>{" "}
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>{db.country}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">TableCell</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-right"</span>&gt;</span>{db.created_at}<span class="hljs-tag">&lt;/<span class="hljs-name">TableCell</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">TableRow</span>&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">TableBody</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Table</span>&gt;</span></span>
  );
}
</code></pre>
<p>This code renders a styled, dynamic table with data passed from a database or API.</p>
<ul>
<li><p><strong>Imports</strong>: Uses custom table components (<code>Table</code>, <code>TableRow</code>, <code>TableCell</code>, and so on) from <code>@/components/ui/table</code>.</p>
</li>
<li><p><strong>Props</strong>: Accepts a <code>data</code> prop, an array of objects representing table rows.</p>
</li>
<li><p><strong>Table Caption</strong>: Displays a caption, "Live data from database," styled with Tailwind CSS.</p>
</li>
<li><p><strong>Table Header</strong>: Defines column headers such as <code>Id</code>, <code>name</code>, <code>email</code>, and more.</p>
</li>
<li><p><strong>Dynamic Rows</strong>: Maps over the <code>data</code> array to generate <code>TableRow</code> elements dynamically, using <code>_id</code> as the unique key.</p>
</li>
<li><p><strong>Data Cells</strong>: Displays object fields (<code>_id</code>, <code>name</code>, <code>email</code>, and so on) in <code>TableCell</code> components with custom styles.</p>
</li>
<li><p><strong>Tailwind CSS</strong>: Styles applied for alignment, font weight, and spacing.</p>
</li>
</ul>
<h3 id="heading-nlqueryformjsx"><strong>NLQueryForm.jsx</strong></h3>
<p>In this file, we handle the API calls, define CopilotKit actions, and pass the fetched data to the Table component. This file acts as the central logic hub for connecting the backend API, AI actions, and the frontend display.</p>
<p>Key functionalities of <code>NLQueryForm.jsx</code>:</p>
<ol>
<li><p><strong>API integration</strong>: Fetches data from the database and handles errors or loading states.</p>
</li>
<li><p><strong>CopilotKit actions</strong>: Defines AI actions that allow querying and interacting with the database using natural language.</p>
</li>
<li><p><strong>Data passing</strong>: Sends the processed data to the <code>Table</code> component for display.</p>
</li>
</ol>
<p>Below is the code:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useCopilotReadable, useCopilotAction } <span class="hljs-keyword">from</span> <span class="hljs-string">"@copilotkit/react-core"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> { Tabledata } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Tabledata"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NLQueryForm</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [nlQuery, setNlQuery] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [data, setData] = useState([]);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"🚀 ~ NLQueryForm ~ data:"</span>, data);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">true</span>);

  <span class="hljs-keyword">const</span> API_KEY = process.env.NEXT_PUBLIC_RESTDB_API_KEY;
  <span class="hljs-keyword">const</span> BASE_URL = process.env.NEXT_PUBLIC_RESTDB_BASE_URL;
  <span class="hljs-built_in">console</span>.table({ API_KEY, BASE_URL });
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchData</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">if</span> (!API_KEY || !BASE_URL) {
        setError(<span class="hljs-string">"API configuration is missing"</span>);
        setLoading(<span class="hljs-literal">false</span>);
        <span class="hljs-keyword">return</span>;
      }
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(BASE_URL, {
          <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">"x-apikey"</span>: API_KEY,
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          },
        });
        setData(response.data);
        setLoading(<span class="hljs-literal">false</span>);
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error fetching data:"</span>, error);
        setError(
          error <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span> ? error.message : <span class="hljs-string">"An unknown error occurred"</span>
        );
        setLoading(<span class="hljs-literal">false</span>);
      }
    }
    fetchData();
  }, [API_KEY, BASE_URL]);

  useCopilotReadable({
    <span class="hljs-attr">description</span>: <span class="hljs-string">"Query database with detailed information"</span>,
    <span class="hljs-attr">value</span>: <span class="hljs-built_in">JSON</span>.stringify(data.slice(<span class="hljs-number">0</span>, <span class="hljs-number">25</span>)),
  });
  useCopilotAction({
    <span class="hljs-attr">name</span>: <span class="hljs-string">"fetchData"</span>,
    <span class="hljs-attr">description</span>: <span class="hljs-string">"Search and filter data based on natural language query"</span>,
    <span class="hljs-attr">parameters</span>: [
      {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"nlQuery"</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
        <span class="hljs-attr">description</span>: <span class="hljs-string">"Natural language search term for database"</span>,
        <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>,
      },
    ],

    <span class="hljs-attr">handler</span>: <span class="hljs-keyword">async</span> ({ data }) =&gt; {
      setNlQuery(data);
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(data);
    },
  });

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {error &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> "<span class="hljs-attr">red</span>" }}&gt;</span>{error}<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">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-green-600 font-bold text-center"</span>&gt;</span>
          Live data from database.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</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 text-green-600 font-bold text-center"</span>&gt;</span>
          Total Records: {data.length}
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Tabledata</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</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>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> NLQueryForm;
</code></pre>
<p>Here’s a detailed explanation of the <code>NLQueryForm</code> component:</p>
<p><strong>Imports and Dependencies:</strong></p>
<ul>
<li><p>Utilizes React for state management (<code>useState</code>) and side effects (<code>useEffect</code>).</p>
</li>
<li><p>Imports <code>axios</code> for HTTP requests.</p>
</li>
<li><p>Imports <code>useCopilotReadable</code> and <code>useCopilotAction</code> from <code>@copilotkit/react-core</code> to integrate CopilotKit functionality.</p>
</li>
<li><p>Imports a custom <code>Tabledata</code> component for rendering data.</p>
</li>
</ul>
<p><strong>Component Setup:</strong></p>
<ul>
<li><p>Defines a functional React component <code>NLQueryForm</code>.</p>
</li>
<li><p>Initializes state variables:</p>
<ul>
<li><p><code>nlQuery</code>: Holds the natural language query input.</p>
</li>
<li><p><code>data</code>: Stores fetched data from the API.</p>
</li>
<li><p><code>error</code>: Stores any errors that occur during data fetching.</p>
</li>
<li><p><code>loading</code>: Tracks the loading state of the component.</p>
</li>
</ul>
</li>
</ul>
<p><strong>API Configuration:</strong></p>
<ul>
<li><p>Fetches API keys and base URL from environment variables (<code>NEXT_PUBLIC_RESTDB_API_KEY</code> and <code>NEXT_PUBLIC_RESTDB_BASE_URL</code>).</p>
</li>
<li><p>Logs these values for debugging purposes using <code>console.table</code>.</p>
</li>
</ul>
<p><strong>Data Fetching:</strong></p>
<ul>
<li><p>Uses <code>useEffect</code> to fetch data from the API on the initial render.</p>
</li>
<li><p>Makes a GET request to the API using <code>axios</code> with required headers.</p>
</li>
<li><p>Updates <code>data</code> with the response and stops the loading state.</p>
</li>
<li><p>Handles errors by logging them and updating the <code>error</code> state.</p>
</li>
</ul>
<p><strong>CopilotKit Integration:</strong></p>
<ul>
<li><p><code>useCopilotReadable</code>: Exposes a readable description and a slice of the first 25 records of <code>data</code>.</p>
</li>
<li><p><code>useCopilotAction</code>: Defines a CopilotKit action named <code>fetchData</code> which:</p>
<ul>
<li><p>Accepts a natural language query (<code>nlQuery</code>) as input.</p>
</li>
<li><p>Updates the <code>nlQuery</code> state and returns it as a string.</p>
</li>
</ul>
</li>
</ul>
<p><strong>Conditional Rendering:</strong></p>
<ul>
<li><p>Displays a loading message (<code>Loading...</code>) if <code>loading</code> is true.</p>
</li>
<li><p>Displays an error message in red text if an error occurs.</p>
</li>
</ul>
<p><strong>Rendering:</strong></p>
<ul>
<li><p>Shows a message indicating live data and the total record count.</p>
</li>
<li><p>Passes the <code>data</code> state to the <code>Tabledata</code> component for rendering.</p>
</li>
</ul>
<p><strong>Export:</strong></p>
<ul>
<li>Exports the <code>NLQueryForm</code> component as the default export.</li>
</ul>
<h3 id="heading-pagejs"><strong>Page.js</strong></h3>
<p>Now go to the <code>page.js</code> file inside the app folder and add this code:</p>
<ul>
<li><pre><code class="lang-javascript">  <span class="hljs-string">"use client"</span>;

  <span class="hljs-keyword">import</span> NLQueryForm <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/ui/nl-query-form"</span>;
  <span class="hljs-keyword">import</span> { CopilotPopup } <span class="hljs-keyword">from</span> <span class="hljs-string">"@copilotkit/react-ui"</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">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">"min-h-screen bg-background"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-primary text-primary-foreground py-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold"</span>&gt;</span>
              Natural Language SQL Query Builder
            <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 class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container py-8"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">NLQueryForm</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">CopilotPopup</span>
          <span class="hljs-attr">instructions</span>=<span class="hljs-string">{</span>
            "<span class="hljs-attr">You</span> <span class="hljs-attr">are</span> <span class="hljs-attr">assisting</span> <span class="hljs-attr">the</span> <span class="hljs-attr">user</span> <span class="hljs-attr">as</span> <span class="hljs-attr">best</span> <span class="hljs-attr">as</span> <span class="hljs-attr">you</span> <span class="hljs-attr">can.</span> <span class="hljs-attr">Answer</span> <span class="hljs-attr">in</span> <span class="hljs-attr">the</span> <span class="hljs-attr">best</span> <span class="hljs-attr">way</span> <span class="hljs-attr">possible</span> <span class="hljs-attr">given</span> <span class="hljs-attr">the</span> <span class="hljs-attr">data</span> <span class="hljs-attr">you</span> <span class="hljs-attr">have.</span>"
          }
          <span class="hljs-attr">labels</span>=<span class="hljs-string">{{</span>
            <span class="hljs-attr">title:</span> "<span class="hljs-attr">Popup</span> <span class="hljs-attr">Assistant</span>",
            <span class="hljs-attr">initial:</span> "<span class="hljs-attr">Need</span> <span class="hljs-attr">any</span> <span class="hljs-attr">help</span>?",
          }}
        /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
  }
</code></pre>
</li>
</ul>
<p>Here’s a simple explanation of the code above:</p>
<ul>
<li><p><strong>Client-Side Rendering</strong>:</p>
<ul>
<li><code>"use client";</code> indicates the file is using React's client-side rendering.</li>
</ul>
</li>
<li><p><strong>Importing Components</strong>:</p>
<ul>
<li><p><code>NLQueryForm</code> is imported from a local component directory to be used in the app.</p>
</li>
<li><p><code>CopilotPopup</code> is imported from the <code>@copilotkit/react-ui</code> package for displaying an interactive popup.</p>
</li>
</ul>
</li>
<li><p><strong>Main Function</strong>:</p>
<ul>
<li><code>Home</code> is a React functional component that defines the UI for the home page.</li>
</ul>
</li>
<li><p><strong>Page Layout</strong>:</p>
<ul>
<li>A full-page container (<code>min-h-screen</code>) with a background color (<code>bg-background</code>) wraps all content.</li>
</ul>
</li>
<li><p><strong>Header</strong>:</p>
<ul>
<li><p>Contains a title with the text <strong>"Natural Language SQL Query Builder"</strong>.</p>
</li>
<li><p>Styled with a primary background and text colors (<code>bg-primary</code>, <code>text-primary-foreground</code>).</p>
</li>
</ul>
</li>
<li><p><strong>Main Content</strong>:</p>
<ul>
<li>Renders the <code>NLQueryForm</code> component inside a container with padding (<code>py-8</code>).</li>
</ul>
</li>
<li><p><strong>Popup Component</strong>:</p>
<ul>
<li><p>Adds a <code>CopilotPopup</code> at the bottom with:</p>
<ul>
<li><p><strong>Instructions</strong>: Describes the assistant's role.</p>
</li>
<li><p><strong>Labels</strong>: Includes a title and initial message for the popup.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Purpose</strong>:</p>
<ul>
<li>The page is designed to let users interact with a natural language SQL query builder and receive assistance via a popup.</li>
</ul>
</li>
</ul>
<h3 id="heading-configuring-copilotkit-for-the-whole-app"><strong>Configuring CopilotKit for the Whole App</strong></h3>
<p>This is going to be the last step of building the application. Navigate to the <code>layout.js</code> file and add this code:</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> <span class="hljs-string">"./globals.css"</span>;
<span class="hljs-keyword">import</span> { CopilotKit } <span class="hljs-keyword">from</span> <span class="hljs-string">"@copilotkit/react-core"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@copilotkit/react-ui/styles.css"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">"Create Next App"</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">"Generated by create next app"</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">RootLayout</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">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">CopilotKit</span> <span class="hljs-attr">runtimeUrl</span>=<span class="hljs-string">"/api/copilotkit"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">CopilotKit</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
  );
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ul>
<li><p><strong>Imports:</strong></p>
<ul>
<li><p><code>./globals.css</code>: Imports global CSS styles for the application.</p>
</li>
<li><p><code>@copilotkit/react-core</code>: Imports the core functionality of CopilotKit.</p>
</li>
<li><p><code>@copilotkit/react-ui/styles.css</code>: Includes predefined styles for the CopilotKit UI components.</p>
</li>
</ul>
</li>
<li><p><strong>Metadata:</strong></p>
<ul>
<li>The <code>metadata</code> object defines the app's title and description, which are useful for setting meta tags in the generated HTML for SEO and user information.</li>
</ul>
</li>
<li><p><strong>RootLayout function:</strong></p>
<ul>
<li>This function serves as the root layout wrapper for the application. It ensures consistent structure across all pages and integrates the CopilotKit runtime.</li>
</ul>
</li>
<li><p><strong>Structure:</strong></p>
<ul>
<li><p>The layout returns an <code>&lt;html&gt;</code> element with a <code>lang</code> attribute set to <code>en</code> for English.</p>
</li>
<li><p>Inside the <code>&lt;body&gt;</code> tag, the CopilotKit component is wrapped around the <code>children</code> prop.<br>  This setup:</p>
<ul>
<li><p>Connects the app to the CopilotKit runtime using the API endpoint <code>/api/copilotkit</code>.</p>
</li>
<li><p>Provides access to CopilotKit's functionality, such as handling natural language queries, throughout the application.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="heading-some-important-notes">Some Important Notes</h2>
<p>Designing and deploying a database can take various forms, depending on the tools and requirements. For this project, I have chosen the simplest and most accessible approach.</p>
<h4 id="heading-why-copilotkit">Why CopilotKit?</h4>
<p>CopilotKit is a powerful tool that converts NLP queries into actionable backend code. If you have an alternative that works similarly, feel free to use it. It bridges the gap between natural language input and technical execution, making it ideal for projects like this.</p>
<h4 id="heading-why-groqcloud">Why GroqCloud?</h4>
<p>I selected <strong>GroqCloud</strong> because it’s free and provides access to multiple LLMs with a single API key. While you can opt for alternatives like ChatGPT, note that they may require paid plans. GroqCloud’s versatility and affordability make it perfect for this tutorial.</p>
<h4 id="heading-database-considerations">Database Considerations</h4>
<p>The size of your database can vary from very small to enormous. However, interacting with the database depends on the token limits of the LLM you’re using.</p>
<p>Since I’m working with free-tier tools, my focus is on a small database to ensure seamless interactions.</p>
<h4 id="heading-security-best-practices">Security Best Practices</h4>
<p>Never expose your credentials publicly. Always store sensitive information like API keys in an <code>.env</code> file to keep your project secure.</p>
<h4 id="heading-future-enhancements">Future Enhancements</h4>
<p>While this tutorial focuses on setting up and querying a database, the potential of CopilotKit extends to <strong>CRUD operations</strong> (Create, Read, Update, Delete). In my next tutorial, I will demonstrate how to implement full CRUD operations using CopilotKit for a more dynamic and functional application.</p>
<h2 id="heading-playing-with-the-database">Playing with the Database</h2>
<p>You can explore the live project via the following link and ask any questions related to the database data: <a target="_blank" href="https://talktodb-inky.vercel.app/">live link</a> .</p>
<p>For a deeper understanding of the code, here’s the GitHub repository link: <a target="_blank" href="https://github.com/prankurpandeyy/talktodb">github</a> .</p>
<p>Also, here’s a screenshot demonstrating its practical use. In this example, instead of writing a plain SQL query like <code>SELECT * FROM demo_data WHERE email = '</code><a target="_blank" href="mailto:riverashannon@lee.com"><code>riverashannon@lee.com</code></a><code>';</code> to extract the name of the person, we used an NLP query to achieve the exact same result.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735061714011/bec86e4a-bb7b-4d7f-97e9-284d54060db5.png" alt="bec86e4a-bb7b-4d7f-97e9-284d54060db5" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you’ve enjoyed building this simple AI chatbot to interact with the database. In this project, we’ve used a simple SQL database, but you can apply this approach to any database as long as you can retrieve the data.</p>
<p>In the future, I plan to implement many new projects involving AI and other tools. AI tools are truly game-changing in the IT field, and I look forward to providing you with more detailed insights and practical implementations of the latest tools emerging in the space.</p>
<p>So this is the end from my side. If you found this article useful, then do share it and connect with me – I am open to opportunities:</p>
<ul>
<li><p>Follow Me on X: <a target="_blank" href="https://x.com/prankurpandeyy">Prankur's Twitter</a></p>
</li>
<li><p>Follow me on LinkedIn: <a target="_blank" href="https://linkedin.com/in/prankurpandeyy">Prankur's Linkedin</a></p>
</li>
<li><p>Look at my Portfolio here: <a target="_blank" href="https://prankurpandeyy.netlify.app/">Prankur's Portfolio</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Meme Generator Using HTML Canvas ]]>
                </title>
                <description>
                    <![CDATA[ We all come across memes almost every day on the internet. Whether you're scrolling through social media or chatting with friends, there's a good chance you'll stumble on a meme, or even share one yourself. A meme can be an image, a video, or gif tha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-meme-generator-using-html-canvas/</link>
                <guid isPermaLink="false">673c929fd0881b2b1feaf916</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Timothy Olanrewaju ]]>
                </dc:creator>
                <pubDate>Tue, 19 Nov 2024 13:29:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731783150771/d3ba743f-c945-482e-a25d-9d093c7e866b.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>We all come across memes almost every day on the internet. Whether you're scrolling through social media or chatting with friends, there's a good chance you'll stumble on a meme, or even share one yourself. A meme can be an image, a video, or gif that is meant to be funny or convey a message in a lighthearted way.</p>
<p>Memes are fun and we all love them. What if I told you that you can create yours from scratch? Well, that is what I’ll be showing you in this article using HTML Canvas. No need for fancy software - just a little code and some creativity to make your own custom memes.</p>
<p>If you are excited about this, let’s jump straight into it!</p>
<h2 id="heading-what-youll-need">What You’ll Need</h2>
<p>To follow along with this tutorial, you will need:</p>
<ul>
<li><p>Basic knowledge of HTML, CSS and JavaScript.</p>
</li>
<li><p>A text Editor (like Visual Studio Code or Sublime Text).</p>
</li>
<li><p>A modern web browser.</p>
</li>
</ul>
<h2 id="heading-step-one-set-up-your-project">Step One: Set Up your Project</h2>
<p>Create a folder and create these three files in the folder:</p>
<ul>
<li><p><code>Index.html</code></p>
</li>
<li><p><code>Style.css</code></p>
</li>
<li><p><code>Script.js</code></p>
</li>
</ul>
<h2 id="heading-step-two-html-structure">Step Two: HTML Structure</h2>
<p>First, let’s create the basic structure of the HTML file. Our structure would include a file upload button for images, a text input for adding captions (both at the top and bottom), buttons to generate and download the meme and a canvas for displaying the image and caption(s).</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Meme Generator<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Meme Generator<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"imageInput"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"controls"</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">id</span>=<span class="hljs-string">"topText"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter Top Text"</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">id</span>=<span class="hljs-string">"bottomText"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter Bottom Text"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"generate"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"generateMeme()"</span>&gt;</span>Generate Meme<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"download"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"downloadMeme()"</span>&gt;</span>Download Meme<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">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"memeCanvas"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"580"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"450"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"script.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-step-three-styling-with-css">Step Three: Styling with CSS</h2>
<p>Next, we apply styling to the HTML elements we just created to make it more appealing and user-friendly. Here, we just apply basic CSS to center the content and add colors to both the background and buttons.</p>
<pre><code class="lang-css"><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">font-family</span>: Arial, sans-serif;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">121</span>, <span class="hljs-number">121</span>, <span class="hljs-number">170</span>);
    <span class="hljs-attribute">color</span>: white;
}
<span class="hljs-selector-tag">canvas</span> {
    <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
}
<span class="hljs-selector-class">.controls</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
}
<span class="hljs-selector-class">.controls</span> <span class="hljs-selector-tag">input</span>, <span class="hljs-selector-class">.controls</span> <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span>;
}
<span class="hljs-selector-id">#generate</span>{
    <span class="hljs-attribute">background-color</span>: green;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">6px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">3px</span>;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">cursor</span>: pointer;
}
<span class="hljs-selector-id">#download</span>{
    <span class="hljs-attribute">background-color</span>: blue;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">6px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">3px</span>;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<p>Here's how our webpage looks in the browser after applying the styling:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731772678380/edd33637-73dd-40b6-8b8d-352d350ac52e.png" alt="meme generator page on the browser" class="image--center mx-auto" width="1364" height="644" loading="lazy"></p>
<h2 id="heading-step-four-add-javascript-to-handle-logic">Step Four: Add JavaScript to Handle Logic</h2>
<p>Now, let’s code the functionalities of our app using JavaScript.</p>
<h3 id="heading-initialization">Initialization</h3>
<p>First, we need to initialize some important elements that would enable us render our image on the canvas.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'memeCanvas'</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">'2d'</span>);
<span class="hljs-keyword">const</span> imageInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'imageInput'</span>);
<span class="hljs-keyword">let</span> uploadedImage = <span class="hljs-literal">null</span>;
</code></pre>
<p>In this code:</p>
<ul>
<li><p><code>canvas</code>: Refers to the HTML <code>&lt;canvas&gt;</code> element with the ID of <code>memeCanvas</code>. This is where the meme image and text will be drawn.</p>
</li>
<li><p><code>ctx</code>: While using canvas, there are methods that can be applied that enables for drawing shapes, images and texts on the canvas. We specified a <em>Context-Type</em> of <strong>2d</strong>, making the canvas render in 2D context.</p>
</li>
<li><p><code>imageInput</code>: Refers to an <code>&lt;input&gt;</code> element of type <code>file</code> (with the ID of <code>imageInput</code>) that allows you upload an image.</p>
</li>
<li><p><code>uploadedImage</code>: A variable to store the uploaded images so it can be drawn on the canvas.</p>
</li>
</ul>
<h3 id="heading-how-to-upload-images">How to Upload Images</h3>
<p>Next, we want to be able to choose a particular file, read it and draw the selected image file on the canvas.</p>
<p>Our meme generator will accept only files with a <code>type</code> of <code>image</code><strong>.</strong></p>
<pre><code class="lang-javascript">imageInput.addEventListener(<span class="hljs-string">'change'</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> file = event.target.files[<span class="hljs-number">0</span>];
  <span class="hljs-keyword">const</span> reader = <span class="hljs-keyword">new</span> FileReader();

  reader.onload = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> Image();
    img.src = e.target.result;
    img.onload = <span class="hljs-function">() =&gt;</span> {
      uploadedImage = img;
      drawImage();
    };
  };

  reader.readAsDataURL(file);
});
</code></pre>
<p>In this code:</p>
<ul>
<li><p><code>imageInput.addEventListener('change')</code>: Adds and listens for a <code>change</code> event to the file input field that triggers when the user selects a file.</p>
</li>
<li><p><a target="_blank" href="http://event.target"><code>event.target</code></a><code>.files[0]</code>: Accesses the first file the user selects.</p>
</li>
<li><p><code>FileReader</code>: Reads file data and allows it to be accessed as a URL.</p>
</li>
<li><p><code>reader.onload</code>: This function is triggered after the file is read. It does the following:</p>
<ul>
<li><p>Creates a new <code>Image</code> object.</p>
</li>
<li><p>Sets the image’s <code>src</code> property to the file's data URL.</p>
</li>
<li><p>Waits for the image to load and then:</p>
<ul>
<li><p>Stores the image in the <code>uploadedImage</code> variable.</p>
</li>
<li><p>Calls <code>drawImage()</code> to draw the image on the canvas.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-how-to-draw-the-image-and-text-caption">How to Draw the Image and Text Caption</h3>
<p>Here, we’ll be drawing the image, fixing the captions inputted by the user on top of the image (overlay), and styling and positioning the text captions.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawImage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (uploadedImage) {
    <span class="hljs-comment">// Clear canvas and set canvas dimensions to fit the image</span>
    ctx.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);
    ctx.drawImage(uploadedImage, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

    <span class="hljs-comment">// Get text values</span>
    <span class="hljs-keyword">const</span> topText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'topText'</span>).value;
    <span class="hljs-keyword">const</span> bottomText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'bottomText'</span>).value;

    <span class="hljs-comment">// Set text styles</span>
    ctx.font = <span class="hljs-string">'30px Impact'</span>;
    ctx.fillStyle = <span class="hljs-string">'white'</span>;
    ctx.strokeStyle = <span class="hljs-string">'black'</span>;
    ctx.lineWidth = <span class="hljs-number">2</span>;
    ctx.textAlign = <span class="hljs-string">'center'</span>;

    <span class="hljs-comment">// Draw top text</span>
    ctx.fillText(topText, canvas.width / <span class="hljs-number">2</span>, <span class="hljs-number">50</span>);
    ctx.strokeText(topText, canvas.width / <span class="hljs-number">2</span>, <span class="hljs-number">50</span>);

    <span class="hljs-comment">// Draw bottom text</span>
    ctx.fillText(bottomText, canvas.width / <span class="hljs-number">2</span>, canvas.height - <span class="hljs-number">20</span>);
    ctx.strokeText(bottomText, canvas.width / <span class="hljs-number">2</span>, canvas.height - <span class="hljs-number">20</span>);
  }
}
</code></pre>
<p>In this code:</p>
<ul>
<li><p><code>ctx.clearRect(0, 0, canvas.width, canvas.height)</code>: Clears the canvas so it can be redrawn.</p>
</li>
<li><p><code>ctx.drawImage()</code>: Draws the uploaded image on the canvas and stretches it to fit the set dimensions.</p>
</li>
<li><p><code>topText</code> and <code>bottomText</code>: Captures the user’s input from two text fields – <code>&lt;input id="topText"&gt;</code> and <code>&lt;input id="bottomText"&gt;</code>.</p>
</li>
</ul>
<p><strong>Text styling</strong>:</p>
<ul>
<li><p><code>ctx.font</code>: Sets the font style.</p>
</li>
<li><p><code>ctx.fillStyle</code>: Sets the fill color for the text.</p>
</li>
<li><p><code>ctx.strokeStyle</code>: Sets the outline color for the text.</p>
</li>
<li><p><code>ctx.lineWidth</code>: Sets the thickness of the outline.</p>
</li>
<li><p><code>ctx.textAlign</code>: Ensures text is centered relative to the X-coordinate.</p>
</li>
<li><p><code>ctx.fillText()</code> and <code>ctx.strokeText()</code>:</p>
<ul>
<li><p>Draws the text at specified positions.</p>
</li>
<li><p><code>canvas.width / 2</code> ensures text is horizontally centered.</p>
</li>
<li><p><code>50</code> and <code>canvas.height - 20</code> define vertical positions for the top and bottom text.</p>
</li>
</ul>
</li>
</ul>
<p>You can customize the text styling to your liking.</p>
<h3 id="heading-how-to-generate-the-meme">How to Generate the Meme</h3>
<p>Next, we’ll trigger the function that generates the meme by drawing user-provided text on the image.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateMeme</span>(<span class="hljs-params"></span>) </span>{
  drawImage();
}
</code></pre>
<p>The code above calls the <code>drawImage</code> function to ensure that the canvas is updated with the image and user-entered text.</p>
<h3 id="heading-how-to-download-the-meme">How to Download the Meme</h3>
<p>Finally, we want to be able to download our meme as an image into our device. Here is how we can achieve that:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadMeme</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> link = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'a'</span>);
  link.download = <span class="hljs-string">'meme.png'</span>;
  link.href = canvas.toDataURL();
  link.click();
}
</code></pre>
<p>In this code:</p>
<ul>
<li><p><code>document.createElement('a')</code>: Creates a temporary <code>&lt;a&gt;</code> element.</p>
</li>
<li><p><a target="_blank" href="http://link.download"><code>link.download</code></a> <code>= 'meme.png'</code>: Sets the file name for the downloaded meme (every meme you download will carry the name of <code>meme.png</code> – you can change it if you wish to).</p>
</li>
<li><p><code>link.href = canvas.toDataURL()</code>: Converts the canvas content into a Data URL.</p>
</li>
<li><p><a target="_blank" href="http://link.click"><code>link.click</code></a><code>()</code>: Simulates a click on the link, triggering the download.</p>
</li>
</ul>
<p>With this, we now have a fully functional meme generator.</p>
<h3 id="heading-full-javascript-code">Full JavaScript Code</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'memeCanvas'</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">'2d'</span>);
<span class="hljs-keyword">const</span> imageInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'imageInput'</span>);
<span class="hljs-keyword">let</span> uploadedImage = <span class="hljs-literal">null</span>;

<span class="hljs-comment">// Load the image onto the canvas</span>
imageInput.addEventListener(<span class="hljs-string">'change'</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> file = event.target.files[<span class="hljs-number">0</span>];
  <span class="hljs-keyword">const</span> reader = <span class="hljs-keyword">new</span> FileReader();

  reader.onload = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> Image();
    img.src = e.target.result;
    img.onload = <span class="hljs-function">() =&gt;</span> {
      uploadedImage = img;
      drawImage();
    };
  };

  reader.readAsDataURL(file);
});

<span class="hljs-comment">// Draw image and text on canvas</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawImage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (uploadedImage) {
    <span class="hljs-comment">// Clear canvas and set canvas dimensions to fit the image</span>
    ctx.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);
    ctx.drawImage(uploadedImage, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

    <span class="hljs-comment">// Get text values</span>
    <span class="hljs-keyword">const</span> topText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'topText'</span>).value;
    <span class="hljs-keyword">const</span> bottomText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'bottomText'</span>).value;

    <span class="hljs-comment">// Set text styles</span>
    ctx.font = <span class="hljs-string">'30px Impact'</span>;
    ctx.fillStyle = <span class="hljs-string">'white'</span>;
    ctx.strokeStyle = <span class="hljs-string">'black'</span>;
    ctx.lineWidth = <span class="hljs-number">2</span>;
    ctx.textAlign = <span class="hljs-string">'center'</span>;

    <span class="hljs-comment">// Draw top text</span>
    ctx.fillText(topText, canvas.width / <span class="hljs-number">2</span>, <span class="hljs-number">50</span>);
    ctx.strokeText(topText, canvas.width / <span class="hljs-number">2</span>, <span class="hljs-number">50</span>);

    <span class="hljs-comment">// Draw bottom text</span>
    ctx.fillText(bottomText, canvas.width / <span class="hljs-number">2</span>, canvas.height - <span class="hljs-number">20</span>);
    ctx.strokeText(bottomText, canvas.width / <span class="hljs-number">2</span>, canvas.height - <span class="hljs-number">20</span>);
  }
}

<span class="hljs-comment">// Generate meme by drawing text on the uploaded image</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateMeme</span>(<span class="hljs-params"></span>) </span>{
  drawImage();
}

<span class="hljs-comment">// Download the meme as an image</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadMeme</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> link = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'a'</span>);
  link.download = <span class="hljs-string">'meme.png'</span>;
  link.href = canvas.toDataURL();
  link.click();
}
</code></pre>
<h3 id="heading-steps-on-creating-your-meme-on-meme-generator">Steps on Creating your Meme on Meme Generator</h3>
<ul>
<li><p>Click on the <strong>Browse</strong> button and select a particular image.</p>
</li>
<li><p>Enter text into either of the two input types – labelled Top and Bottom Text.</p>
</li>
<li><p>Click on <strong>Generate Meme</strong> button to create your meme.</p>
</li>
<li><p>Click on <strong>Download Meme</strong> to download your generated meme.</p>
</li>
</ul>
<p>This is the meme generator in full action with the steps demonstrated:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731787007980/24206a68-6e2e-4883-81f2-1db5169cb093.gif" alt="Meme Generator project working in the browser" class="image--center mx-auto" width="1152" height="648" loading="lazy"></p>
<h3 id="heading-final-results">Final Results</h3>
<p>Here are two memes created by our meme generator.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731780897736/d136df1d-c3a4-416d-90ca-e6e400d10a0e.png" alt="meme 1" class="image--center mx-auto" width="580" height="450" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731780929605/f67cc533-2bb4-4bdb-ad9f-1231caede069.png" alt="meme 2" class="image--center mx-auto" width="580" height="450" loading="lazy"></p>
<p>Pretty cool right?</p>
<p>Now, you can try it out and create your own viral meme!</p>
<p>For more programming articles and posts, you can follow me on <a target="_blank" href="https://x.com/SmoothTee_DC">X</a> or connect with me on <a target="_blank" href="https://www.linkedin.com/in/timothy-olanrewaju750/">LinkedIn</a>.</p>
<p>See you in the next one!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Counter Button with React, TailwindCSS, and TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ How can you keep track of the number of times a user clicks a button? How are the hearts on Instagram or the likes on Facebook counted?  In this tutorial, we will build a button that tracks the number of times a button has been clicked. Along the way... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-counter-button-with-react/</link>
                <guid isPermaLink="false">66bce123d84f19f03de63fd1</guid>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Devin Lane ]]>
                </dc:creator>
                <pubDate>Wed, 10 Jul 2024 14:41:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Build-a-counter-button-with-React-6-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>How can you keep track of the number of times a user clicks a button? How are the hearts on Instagram or the likes on Facebook counted? </p>
<p>In this tutorial, we will build a button that tracks the number of times a button has been clicked. Along the way, you will learn some fundamental concepts in React such as components, JSX, passing props between components, and managing state with hooks. You will also get small introductions to Tailwind and TypeScript.</p>
<p>This tutorial builds upon examples and concepts outlined in the "Learn" section of the React documentation, which you can find <a target="_blank" href="https://react.dev/learn">here</a>.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li>Basic familiarity with JavaScript, such as working with variables, functions, arrays, and objects. </li>
<li>Basic familiarity with CSS and HTML.</li>
<li>Basic familiarity with the command line.</li>
<li><a target="_blank" href="https://nodejs.org/en">Node</a> installed.</li>
<li>A code editor of your choice (I'll be using <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> here)</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-chapter-1-how-to-build-the-counter-button">How to Build the Counter Button</a></li>
<li><a class="post-section-overview" href="#heading-chapter-2-how-to-refactor-the-project">How to Refactor the Project</a></li>
<li><a class="post-section-overview" href="#heading-chapter-3-two-components-with-independent-and-shared-state">Two Components with Independent and Shared State</a></li>
<li><a class="post-section-overview" href="#heading-chapter-4-how-to-add-both-pairs-of-buttons-to-our-site">How to Add Both Pairs of Buttons to our Website</a></li>
<li><a class="post-section-overview" href="#heading-chapter-5-how-to-deploy-the-site-to-netlify">How to Deploy the Site to Netlify</a></li>
</ol>
<h2 id="heading-chapter-1-how-to-build-the-counter-button">Chapter 1: How to Build the Counter Button</h2>
<h3 id="heading-what-is-react">What is React?</h3>
<p>Before we dive in, let's define React. <a target="_blank" href="https://react.dev/">React</a> is a JavaScript library for creating user interfaces out of pieces called <em>components</em>. Components are JavaScript functions that can receive and display data interactively to your users.</p>
<h3 id="heading-project-setup">Project setup</h3>
<p>We're going to use <a target="_blank" href="https://nextjs.org/">Next.js</a> for our local React setup.</p>
<p>Within the directory you'd like to store this project, open your terminal and execute the following command:</p>
<pre><code class="lang-zsh">npx create-next-app@latest
</code></pre>
<p>Name your project however you like, and answer the commands as follows:</p>
<pre><code class="lang-zsh">What is your project named? react-counter-button
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? Yes
Would you like to use `src/` directory? No
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import <span class="hljs-built_in">alias</span> (@/*)? No
</code></pre>
<p>Now let's <code>cd</code> into our project directory</p>
<pre><code class="lang-zsh"><span class="hljs-built_in">cd</span> react-counter-button
</code></pre>
<p>And run the project in Visual Studio Code:</p>
<pre><code class="lang-zsh">code .
</code></pre>
<p>Note: if you don't have the <code>code</code> command in your PATH, you can press ⇧⌘P (Ctrl+Shift+P on Windows/Linux) and type in 'Shell Command: Install 'code' command in PATH'. Alternatively, you can drag the folder onto the Visual Studio Code icon in MacOS. Or, within Visual Studio Code, you can select File -&gt; Open, and find "react-counter-button", or the name of your project. </p>
<p>In your terminal run:</p>
<pre><code class="lang-zsh">npm run dev
</code></pre>
<p>Open your browser to <code>localhost:3000</code> and you should see the following page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-14-at-7.10.35-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next.js boilerplate</em></p>
<p>We now have the project up and running. Back over in our code editor, we can begin the work.</p>
<h3 id="heading-remove-boilerplate">Remove boilerplate</h3>
<p>In <code>app/page.tsx</code>, let's delete most of the boilerplate code except the two <code>main</code> tags. Then let's add a title for our project in an <code>h1</code> tag in between the <code>main</code> tags. Our code should look like this:</p>
<pre><code class="lang-js"><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">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">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center justify-between p-24"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>Here's what we should now see:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-16-at-8.22.30-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Initial state of our project</em></p>
<h3 id="heading-writing-our-first-component">Writing our first component</h3>
<p>Let's create our first component. A React component is a function that returns markup. Below and outside of the scope of our <code>Home</code> function, let's write the following:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</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">button</span>&gt;</span>I have been clicked X times<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>;
}
</code></pre>
<p>Here we have a function <code>Button</code> that returns some markup in JSX. JSX looks a lot like HTML, but it can display dynamic content, and has stricter rules than HTML. You can learn more about JSX in the React docs <a target="_blank" href="https://react.dev/learn/writing-markup-with-jsx">here</a>.</p>
<p>The <code>Button</code> function must be uppercase to be recognized as a valid React component. This contrasts it with an HTML tag, which is lower case.</p>
<p>You'll notice that we still see no change on our webpage – we need to render this component in order to see it on the screen. </p>
<p>We can use our <code>Button</code> component as if it were an HTML tag we created. If we nest the <code>Button</code> component within the <code>Home</code> component, we should see it on the screen:</p>
<pre><code class="lang-js"><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">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">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center justify-between p-24"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">main</span>&gt;</span></span>
    );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</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">button</span>&gt;</span>I have been clicked X times<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-20-at-6.37.57-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Rendering the <code>Button</code> component (with less than ideal CSS)</em></p>
<h3 id="heading-styling-our-first-component-with-tailwind">Styling our first component with Tailwind</h3>
<p>You'll notice the button is on the bottom of the screen. This is because the styles on <code>main</code> include <code>justify-between</code> in the <code>flex-col</code> direction. If we remove <code>justify-between</code> we should see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-20-at-6.36.57-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Improving the CSS of the initial state of our application</em></p>
<p>You can read more about aligning items in a flexbox from MDN <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Aligning_Items_in_a_Flex_Container">here</a>.</p>
<p>You'll also notice that the button is un-styled. This is because <a target="_blank" href="https://tailwindcss.com/">Tailwind</a> removes default styling on buttons as a part of their "preflight" styles. If you're curious to see where these styles come from, you can open <code>node_modules/tailwindcss/src/css/preflight.css</code> and check out ~line 193 (permalink on GitHub <a target="_blank" href="https://github.com/tailwindlabs/tailwindcss/blob/332347ed834a3078547923ccfddc1c22035011b6/packages/tailwindcss/preflight.css#L182">here</a>):</p>
<pre><code class="lang-css"><span class="hljs-comment">/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/</span>

<span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-attr">[type=<span class="hljs-string">'button'</span>]</span>,
<span class="hljs-selector-attr">[type=<span class="hljs-string">'reset'</span>]</span>,
<span class="hljs-selector-attr">[type=<span class="hljs-string">'submit'</span>]</span> {
  <span class="hljs-attribute">-webkit-appearance</span>: button; <span class="hljs-comment">/* 1 */</span>
  <span class="hljs-attribute">background-color</span>: transparent; <span class="hljs-comment">/* 2 */</span>
  <span class="hljs-attribute">background-image</span>: none; <span class="hljs-comment">/* 2 */</span>
}
</code></pre>
<p>We're not going to change the styles within <code>node_modules</code> – instead we'll add our own styling to the Button component. One of the benefits of Tailwind is that our CSS is co-located with our JavaScript, making quick changes to styles easier than opening a separate stylesheet file. </p>
<p>Let's make the following changes:</p>
<pre><code class="lang-js"><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">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">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">main</span>&gt;</span></span>
    );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</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">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>&gt;</span>
            I have been clicked X times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>We've added styles to our button, and we've also added a <code>gap-4</code> to our <code>main</code> parent flex box to provide a space between the <code>h1</code> and the <code>button</code>. (You can read more about the CSS property "gap" in the MDN <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/gap">here</a>.) We should now see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-20-at-6.49.22-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Viewing our styled <code>Button</code> component</em></p>
<h3 id="heading-wait-but-what-is-tailwind">Wait, but what is Tailwind?</h3>
<p>Now that we've styled our button component and spaced the items out, let's reflect on what Tailwind is, and what it provided for us. <a target="_blank" href="https://tailwindcss.com/">Tailwind</a> is a CSS framework that provides a set of "utility" classes that we can use to style each element. </p>
<p>But what is a utility class? You'll see that to style our button, we added classes such as <code>bg-blue-500</code> – which corresponds to setting the CSS <code>background-color</code> property to blue, and <code>rounded</code> – which corresponds to <code>border-radius: 0.25rem</code>. </p>
<p>Each class is defined according to its <em>utility:</em> changing the background color, the border radius, and so on. Through adding these utility classes to our elements, we arrive at our desired styles. </p>
<p>Tailwind sits in contrast to other frameworks, such as Bootstrap, that provide predefined classes for elements such as buttons. In Bootstrap, we would add a class of <code>btn</code> to achieve a styled button. And of course, with standard CSS we would likely add a custom class (perhaps called <code>button</code>) to our element and create CSS rulesets in a separate stylesheet. </p>
<p>Returning to our project, so far we've set up a React project using Next.js, created our first React component, and styled our button using Tailwind. How do we introduce the counter functionality?</p>
<h3 id="heading-how-to-add-state">How to add state</h3>
<p>In order to display the number of times a button has been clicked, we need to use an event handler, and we need a way to manage <em>state.</em> </p>
<p><a target="_blank" href="https://react.dev/learn/state-a-components-memory">State</a> is component-specific memory. In our example, this is how the button will remember how many times it has been clicked. Using a special React function "<a target="_blank" href="https://react.dev/reference/react/hooks">hook</a>", we trigger a re-render and retain the data across renders – the <code>[useState](https://react.dev/reference/react/useState)</code> hook is provided by React for this purpose. </p>
<p>At the top of our <code>page.tsx</code>, let's import <code>useState</code>: </p>
<p><code>import { useState } from "react"</code></p>
<p>and within our <code>Button</code> component, let's add the following:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>)
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>&gt;</span>
            I have been clicked X times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>Let's unpack what we have here: </p>
<ul>
<li>We're using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">destructuring assignment</a> to get the values of <code>count</code> and the function <code>setCount</code> from <code>useState</code>. The convention is to name these two values <code>something</code> and <code>setSomething</code>, though we could name them anything. </li>
<li>The argument to <code>useState</code> is the initial value of our state variable. Here we've set it to 0.</li>
<li><code>count</code> is our current state.</li>
<li><code>setCount</code> is the function that updates our state and triggers a re-render. </li>
</ul>
<p>However, if you click save you'll see the following error in your terminal and in your browser:</p>
<pre><code class="lang-zsh">You<span class="hljs-string">'re importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they'</span>re Server Components by default.
Learn more: https://nextjs.org/docs/getting-started/react-essentials

   ╭─[/[...your project path]/src/app/page.tsx:1:1]
 1 │ import { useState } from <span class="hljs-string">"react"</span>;
   ·          ────────
 2 │ 
 3 │ <span class="hljs-built_in">export</span> default <span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">Home</span></span>() {
 4 │     <span class="hljs-built_in">return</span> (
   ╰────

Maybe one of these should be marked as a client entry with <span class="hljs-string">"use client"</span>:
  ./src/app/page.tsx
</code></pre>
<p>This is due to Next.js's use of <a target="_blank" href="https://www.joshwcomeau.com/react/server-components/">React Server Components</a>, which you can learn more about <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/rendering">here</a>. React Server Components is a large topic, but the bottom line is that, by default, components are Server Components in Next.js and <code>useState</code> only works in a Client Component. If we write the  <code>"use client"</code> directive at the top of our <code>page.tsx</code>, we resolve the error.</p>
<h3 id="heading-how-to-evaluate-javascript-within-jsx">How to Evaluate JavaScript within JSX</h3>
<p>If we click the button, we still don't see the numbers update. This is because we need a way to <em><a target="_blank" href="https://react.dev/learn/javascript-in-jsx-with-curly-braces">interpolate</a></em> (or evaluate) JavaScript within our JSX markup. Enter the curly braces: <code>{}</code>.</p>
<p>We can use curly braces to "escape" into JavaScript from within JSX markup. This way we can evaluate JavaScript expressions (such as adding to a counter) and dynamically display data in our components. Here's what we'll do:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyButton</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>&gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>We have added <code>{count}</code> to evaluate the value of <code>count</code> from <code>useState</code> within our button. We should see the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-26-at-4.46.56-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Displaying data in JSX with curly braces {}</em></p>
<p>We see a 0 – this comes from the <code>count</code> variable that we destructured from our <code>useState</code> hook, which we initially set to 0. We've successfully interpolated the JavaScript within our JSX markup!</p>
<h3 id="heading-event-handling">Event handling</h3>
<p>You'll notice that if we click the button, still nothing happens. How do we get the number to increment when we click it? </p>
<p>For this, we'll make use of an <a target="_blank" href="https://react.dev/learn/responding-to-events#adding-event-handlers">event handler function</a> as well as the setter function (which we named <code>setCount</code>) that we get from <code>useState</code>:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyButton</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>&gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>What we've done here is add a function <code>handleClick</code> to update the state of the <code>count</code> variable. The convention is to name event handler functions <code>handle</code> followed by the name of your event (for example, <code>handleClick</code>). </p>
<p><code>[setCount](https://react.dev/reference/react/useState#setstate)</code> is a special <code>set</code> function returned by <code>useState</code> that will update the state of the <code>count</code> variable to whatever we pass in as an argument. For example, we could call <code>setCount(2)</code>, and it would update <code>count</code> to 2. <code>setCount(7)</code> would set it to 7, and so on. </p>
<p>We are calling <code>setCount(count + 1)</code>, which evaluates to <code>setCount(0 + 1)</code>, because the initial value of <code>count</code> is 0. Upon the next click, <code>count</code> will be 1, so we'd be calling <code>setCount(1 + 1)</code>, and the next click would call <code>setCount(2 + 1)</code> and so on. </p>
<p>This allows us to update the counter with every click. But, if you click, you'll notice that <em>still</em> nothing happens – why? Perhaps take a moment to try to figure this out for yourself before reading on to help the concept stick even better.</p>
<h3 id="heading-how-to-pass-an-event-handler-as-a-prop-to-your-jsx">How to Pass an Event Handler as a Prop to Your JSX</h3>
<p>Looking at our code, there is no relationship between the user clicking the button, and the <code>handleClick</code> function. We need to pass the <code>handleClick</code> event handler to the <code>onClick</code> property on the button! Let's add that here:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyButton</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded py-2 px-4 text-white font-bold"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>Notice how we haven't said <code>onClick={handleClick()}</code>. We aren't calling the function ourselves here – we are instead passing it down. This is an important distinction, as React calls the function for us when the user clicks the button, instead of it firing immediately. </p>
<p>You can learn more about passing props to components in the React docs <a target="_blank" href="https://react.dev/learn/passing-props-to-a-component">here</a>.</p>
<h3 id="heading-our-working-project">Our working project</h3>
<p>Try it out now, the button works! </p>
<p>You now have a button that updates its count when you click it. This shows usage of interpolating JavaScript within JSX using curly braces, creating your own component and nesting it within other components, using state and hooks within React, as well as working with Next.js and Tailwind. Congratulations! </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/react-counter-button.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Our working project</em></p>
<p>Here would be a good point to <em>commit</em> our changes using Git. You can close the current terminal process by pressing <code>ctrl + c</code>, and then type in <code>git add .</code>, followed by <code>git commit -m "counter button"</code> or some other message that is meaningful.</p>
<h2 id="heading-chapter-2-how-to-refactor-the-project">Chapter 2: How to Refactor the Project</h2>
<h3 id="heading-moving-our-component-to-another-file">Moving our component to another file</h3>
<p>As our project sits, all the code is within <code>app/page.tsx</code>. What if we wanted to add another component, or several? Over time, our <code>page.tsx</code> would get large and difficult to read. </p>
<p>Instead, we can break our components up into their own files to help with readability as well as modularity (reusing the component in multiple different places).</p>
<p>Let's start by creating a folder <code>components</code> at the root of our project to store our components. Inside <code>components</code>, create a file called <code>button.tsx</code>. Then, within <code>app/page.tsx</code> cut (copy and then delete) the entire <code>Button</code> function component and paste it within <code>components/button.tsx</code>. </p>
<p><code>components/button.tsx</code> should look like this:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<h3 id="heading-fix-the-usestate-import-error">Fix the <code>useState</code> import error</h3>
<p>You'll likely notice in your code editor that <code>useState(0)</code> has red squiggly lines underneath it. In Visual Studio Code, if you hover over it, you will see an error that says:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-07-at-6.10.44-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error: cannot find name 'useState'</em></p>
<p>Why is this? We are using <code>useState</code> but we have not imported the module from React. Adding <code>import { useState } from "react";</code> to the top of our <code>button.tsx</code> file will fix this error.</p>
<p>If you look at the beginning of the function, you'll see that <code>Button()</code> is underlined with white lines in Visual Studio Code. Hovering over it will show this error. Reflect on why this might be the case – we'll address this later. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-07-at-6.13.45-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error: 'Button' is declared but its value is never read</em></p>
<h3 id="heading-importing-and-exporting-components">Importing and Exporting Components</h3>
<p>Let's return to <code>app/page.tsx</code> . You'll see two errors here: one on <code>import { useState } from "react";</code> and another on <code>&lt;Button /&gt;</code>. </p>
<p>Let's address the <code>useState</code> error first. We used <code>useState</code> within our <code>Button</code> component, but now that we've moved that component to its own file, we no longer it. Deleting it will solve our error. You can use <code>cmd (ctrl on Windows) + shift + k</code> to delete the entire line in Visual Studio Code.</p>
<p>If you've saved your <code>app/page.tsx</code> you will see this error in the console:</p>
<pre><code class="lang-zsh"> ⨯ app/page.tsx (7:14) @ Button
 ⨯ ReferenceError: Button is not defined
    at Home (./app/page.tsx:19:89)
digest: <span class="hljs-string">"2129895745"</span>
   5 |         &lt;main className=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;
   6 |             &lt;h1&gt;React Counter Button&lt;/h1&gt;
&gt;  7 |             &lt;Button /&gt;
     |              ^
   8 |         &lt;/main&gt;
   9 |     );
  10 | }
 GET / 500 <span class="hljs-keyword">in</span> 87ms
</code></pre>
<p>Why wouldn't <code>Button</code> be defined? The issue is that within our <code>app/page.tsx</code> we have no way to access the <code>Button</code> component over in <code>components/button.tsx</code>. We solve this by exporting and importing the appropriate module.</p>
<p>Within <code>components/button.tsx</code>, at the beginning of our function declaration, let's add the keywords <code>export default</code>. The file will look like this now:</p>
<pre><code class="lang-js"><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">Button</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>You'll notice that our earlier error of <code>'Button' is declared but its value is never read</code> has gone away, because now the value is being read as a default export. </p>
<p>But what have we done here? What is an export, or a default export? <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export">Exporting</a> and importing allows us to modularize JavaScript components into their own sections and use them in others. </p>
<p>There are two types: <em>default</em> exports<em>,</em> and <em>named</em> exports. Each file can have multiple <em>named</em> exports but only one <em>default</em> export. You can read more about importing and exporting components the React documentation <a target="_blank" href="https://react.dev/learn/importing-and-exporting-components">here</a>.</p>
<p>Now that we have exported the component from <code>components/button.tsx</code>, we have to import it within <code>app/page.tsx</code>. Visual Studio Code can help with "<a target="_blank" href="https://code.visualstudio.com/docs/editor/intellisense">intellisense</a>" suggestions: at the top of your file if you start typing "Button", it will suggest the correct import with the correct filepath:</p>
<p><code>import Button from "@/components/button";</code></p>
<p>Within Next.js we can use this <code>@/</code> syntax to reference the root of the project. This is a convenience added in case our import is several file layers deep. You can read the examples of the <code>@/</code> syntax in the Next.js documentation <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases">here</a>. </p>
<p>You'll see that our errors have disappeared and the project still works! We haven't added any new features but we have successfully refactored our code to make it more modular, readable, and maintainable.</p>
<p>Here let's follow the same steps to commit our changes, adding a message such as <code>refactor: move button to its own file</code>.</p>
<h2 id="heading-chapter-3-two-components-with-independent-and-shared-state">Chapter 3: Two Components with Independent and Shared State</h2>
<h3 id="heading-two-components-with-independent-state">Two Components with Independent State</h3>
<p>What if we wanted to have two buttons that can count independently of each other? This will showcase the beauty of React and component-based development implementation will be simpler than building the button from scratch entirely again.</p>
<p>Within <code>app/page.tsx</code> we can simply add another <code>&lt;Button /&gt;</code>. You can focus your cursor on <code>&lt;Button /&gt;</code> and press <code>option + shift + ↓</code> to create another <code>&lt;Button /&gt;</code>:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button"</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">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">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">Button</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>You should now see two button counters with their own independent state:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-07-at-6.40.47-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Two buttons with independent state</em></p>
<p>Component-based design with React makes re-using parts of your application easy. Easy win.</p>
<h3 id="heading-two-components-with-shared-state">Two Components with Shared State</h3>
<p>What if we wanted the buttons to share their state and update together? You'll notice that as we click each button, they separately increment. </p>
<p>In order for the buttons to share their state, we will need to move their state from each individual components "upward" to their common parent component (in this case, the <code>Home</code> function in <code>app/page.tsx</code>). You'll also hear this referred to as "<a target="_blank" href="https://react.dev/learn#sharing-data-between-components">lifting state up</a><em>".</em></p>
<p>Cut the counting logic from <code>components/button.tsx</code> and paste it into <code>app/page.tsx</code> within the body of the <code>Home</code> function. We will also need our <code>useState</code> import at the top of the file:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

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

<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button"</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">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">Button</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<h3 id="heading-passing-props-down-to-a-component">Passing Props Down to a Component</h3>
<p>Now that we have our state in the parent component of each button (<code>Home</code>), we can pass this state down via <em><a target="_blank" href="https://react.dev/learn/passing-props-to-a-component">props</a></em> to the <code>Button</code> component. We will want to pass down both the event handler <code>handleClick</code> as well as the <code>count</code> variable we wish to display:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

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

<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button"</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">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>The <code>count</code> from <code>useState</code> is passed to the <code>count</code> prop, and the function <code>handleClick</code> is passed to the <code>onClick</code> prop, both on the <code>Button</code> component. In JSX, we can define our own props (which might remind you of HTML attributes) so that we can pass data from one component to another. </p>
<p>You might see some errors related to TypeScript at this point – we will come back to these later.</p>
<h3 id="heading-read-props-in-your-child-component">Read Props in Your Child Component</h3>
<p>Now that we have passed the data as props to our component, we need to adjust our <code>Button</code> component to <em>read</em> the props from its parent component. Within <code>components/button.tsx</code>:</p>
<pre><code class="lang-js"><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">Button</span>(<span class="hljs-params">{ count, onClick }</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>React function components accept a single <code>props</code> object as an argument. Here we destructure the props that we want to pass into our <code>Button</code> component. In other words, we are taking <code>count</code>, and <code>onClick</code> from the <code>props</code> object directly, as an argument to <code>Button</code>.</p>
<p>If you save your file you'll see that this now works: you have two buttons with shared state:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screen-Recording-2024-06-14-at-12.07.37-PM.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Two buttons with shared state</em></p>
<p>But why did we pass <code>onClick</code> and not <code>handleClick</code> to the <code>Button</code> component? Isn't <code>handleClick</code> what we want to run when we click the button?</p>
<p>Within the <code>Home</code> component in <code>app/page.tsx</code>, we define <code>handleClick</code> and pass it down as a prop to the <code>Button</code> component. Within the <code>Button</code> component's body in <code>components/button.tsx</code> we read the <em>prop</em> <code>onClick</code>, not the event handler <code>handleClick</code> itself. So when the <code>Button</code> component fires, it calls the <code>onClick</code> prop, which sits "up" the component tree inside <code>Home</code>, where it then calls <code>handleClick</code>, updates the count, and then passes that state back down to both <code>Button</code> components.</p>
<h3 id="heading-tiny-crash-course-in-typescript">Tiny Crash Course in TypeScript</h3>
<p>If you check <code>components/button.tsx</code> you will see the following errors for both the <code>count</code> and <code>onClick</code> props you are reading into <code>Button</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-2024-06-14-at-2.49.55-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Binding element 'count' implicitly has an 'any' type.</em></p>
<p>(You can get these "pretty" TypeScript syntax-highlighted errors with the Visual Studio Code extension <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors">here</a>).</p>
<p>What do these errors mean, and what is TypeScript? TypeScript is a superset of JavaScript that adds <em>types</em> to JavaScript. These can help ensure that our program works as we intend. Examples of types are <code>number</code>, <code>boolean</code>, and <code>string</code>. This error is telling us that we haven't defined a type for the props <code>count</code> or <code>onClick</code>. </p>
<p>So what will the type of <code>count</code> be? If we consider the result of count, the answers could be <code>1</code>, <code>2</code>, <code>3</code>, and so on. These are all numbers, so we will assign the type <code>number</code> to <code>count</code>.</p>
<p>The <code>onClick</code> prop is a function that doesn't take any arguments or return any value – we use it for its side effect of updating <code>setCount</code>. So we assign it the type <code>() =&gt; void</code>. </p>
<p>We create an <em><a target="_blank" href="https://www.typescriptlang.org/docs/handbook/interfaces.html">interface</a></em> where we define the types for our <code>ButtonProps</code>, and then read this interface into our component:</p>
<pre><code class="lang-js">interface ButtonProps {
    <span class="hljs-attr">count</span>: number;
    onClick: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</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">Button</span>(<span class="hljs-params">{ count, onClick }: ButtonProps</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
}
</code></pre>
<p>The errors are gone and there you have it: a tiny intro to TypeScript!</p>
<p>Here let's make another commit with a message such as <code>buttons with shared state</code>.</p>
<h2 id="heading-chapter-4-how-to-add-both-pairs-of-buttons-to-our-site">Chapter 4: How to Add Both Pairs of Buttons to Our Site</h2>
<p>Let's showcase both our buttons with shared and independent state, and deploy the application.</p>
<p>Let's rename our <code>button.tsx</code> to <code>button-shared-state.tsx</code>. Let's also rename the function, the interface, the import within <code>app/page.tsx</code>, as well as the component in <code>app/page.tsx</code>. And let's switch these to <em>named</em> exports using a function expression using <code>const</code> instead of a function declaration:</p>
<pre><code class="lang-js">interface ButtonSharedStateProps {
    <span class="hljs-attr">count</span>: number;
    onClick: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ButtonSharedState = <span class="hljs-function">(<span class="hljs-params">{
    count,
    onClick,
}: ButtonSharedStateProps</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold px-4 py-2"</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
};
</code></pre>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

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

<span class="hljs-keyword">import</span> { ButtonSharedState } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button-shared-state"</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">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Counter Button<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>Now let's create a file <code>components/button-independent-state.tsx</code>:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</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">const</span> ButtonIndependentState = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-700 rounded text-white font-bold py-2 px-4"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>
        &gt;</span>
            I have been clicked {count} times
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
    );
};
</code></pre>
<p>What we've done here is similar to our logic in the beginning of this guide: we've located the state within the button component itself, so that each implementation of the button component creates and tracks its own independent state.</p>
<p>Let's import <code>ButtonIndependentState</code> into <code>app/page.tsx</code>:</p>
<pre><code class="lang-js"><span class="hljs-string">"use client"</span>;

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

<span class="hljs-keyword">import</span> { ButtonSharedState } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button-shared-state"</span>;
<span class="hljs-keyword">import</span> { ButtonIndependentState } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/button-independent-state"</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">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
        setCount(count + <span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex min-h-screen flex-col items-center p-24 gap-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold"</span>&gt;</span>React Counter Buttons<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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"</span>&gt;</span>Buttons with shared state<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonSharedState</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{count}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</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"</span>&gt;</span>Buttons with independent state<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonIndependentState</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ButtonIndependentState</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    );
}
</code></pre>
<p>We have now showcased a set of buttons that have independent state, as well as buttons that have shared state. We added a tiny bit of CSS to make things look nicer as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screen-Recording-2024-06-21-at-12.25.22-PM.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Our finished project</em></p>
<p>Here let's add another commit with a message such as <code>buttons with both shared and independent state</code>.</p>
<h2 id="heading-chapter-5-how-to-deploy-the-site-to-netlify">Chapter 5: How to Deploy the Site to Netlify</h2>
<h3 id="heading-publish-to-github">Publish to GitHub</h3>
<p>Let's deploy our application to the world to show it off. We are going to push our code to GitHub, and then deploy it to Netlify.</p>
<p>The first step is push our code to GitHub. If you don't have a GitHub account, create one first. In Visual Studio Code, you can push to GitHub from the command palette: open up the command palette by pressing <code>⇧⌘P</code> , then type in <code>publish to GitHub</code>, and select <code>publish to public GitHub repository</code>. </p>
<p>Open your GitHub account and verify that the project has successfully been uploaded.</p>
<h3 id="heading-deploy-to-netlify">Deploy to Netlify</h3>
<p>Once you've uploaded your project to GitHub, you can now deploy it to Netlify. Open up the <a target="_blank" href="https://www.netlify.com/">Netlify website</a>, and log in (or create an account if you don't have one).</p>
<p>Click <code>add new site</code>, and then <code>import an existing project</code>. When asked <code>Let’s deploy your project with…</code>, select <code>GitHub</code>. </p>
<p>Select the name of your repository from the list, then give the site a name under <code>site name</code>. You can leave the rest of the settings at their defaults and then click <code>deploy [your site name]</code>.</p>
<p>If the project builds successfully, you will have a live link of your work!</p>
<h2 id="heading-concluding-thoughts-and-next-steps">Concluding Thoughts and Next Steps</h2>
<p>In this project, you have learned fundamental concepts in React such as creating a functional component, importing and exporting modules, interpolating JavaScript within JSX using curly braces, working with state, and using React hooks. </p>
<p>You've also seen an introduction to using utility-based CSS techniques with Tailwind CSS, and you've gotten a tiny introduction into adding types to your JavaScript with TypeScript. Finally, you learned how to deploy your project to Netlify via GitHub.</p>
<p>Where can you go from here? One idea for expanding the project could be to create a "ticker": a counter that could be incremented and decremented (you would have one button that increases the number of the counter, and one that decreases it). </p>
<p>In the name of learning, one effective method for solidifying the concepts you've learned here would be to start a project completely fresh, and see if you can build everything in this tutorial without checking the tutorial. As you need to check in, you will identify which concepts benefit from further study and practice.</p>
<p>If you'd like to stay in touch, you can:</p>
<ul>
<li>Follow me on <a target="_blank" href="https://twitter.com/DevinCLane">Twitter</a> </li>
<li>Follow me on <a target="_blank" href="https://www.linkedin.com/in/devinlane/">LinkedIn</a></li>
</ul>
<p>Please post about what you've made along with any questions or feedback you might have.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create an Interactive Terminal Portfolio Website ]]>
                </title>
                <description>
                    <![CDATA[ In this article, you will learn how to create an interactive terminal-based portfolio and a résumé in JavaScript. We'll use the jQuery Terminal library (and a few other tools) to create a website that looks like a real terminal. This article will sho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-an-interactive-terminal-portfolio-website/</link>
                <guid isPermaLink="false">66ba5abdf4ac8da2b2c2e865</guid>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ terminal ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jakub T. Jankiewicz ]]>
                </dc:creator>
                <pubDate>Mon, 29 Apr 2024 14:49:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730895455049/8fefc48c-761d-4ec5-8f60-b6eb2f97a42a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, you will learn how to create an interactive terminal-based portfolio and a résumé in JavaScript. We'll use the <a target="_blank" href="https://terminal.jcubic.pl/">jQuery Terminal library</a> (and a few other tools) to create a website that looks like a real terminal.</p>
<p>This article will show more advanced usage of the jQuery Terminal library. If you want something more basic, you can check this article: <a target="_blank" href="https://itnext.io/how-to-create-interactive-terminal-like-website-888bb0972288">How to create interactive terminal like website with JavaScript</a> that is written for more entry level programmers. You can also read it (or skim it) first before you begin reading this one.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-the-terminal-and-its-history">What is the terminal and its history?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-jquery-terminal">What is jQuery Terminal?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-base-html-file">Base html file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-initialize-the-terminal">How to Initialize the Terminal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-welcome-message">Welcome message</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-line-gaps">Line Gaps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-colors-to-ascii-art">How to Add Colors to ASCII Art</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-terminal-formatting">Terminal Formatting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-lolcat-library">How to Use the Lolcat Library</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-rainbow-ascii-art-greetings">Rainbow ASCII Art Greetings</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-the-greeting-text-white">How to Make the Greeting Text White</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-your-first-command">How to Make Your First Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-default-commands">Default Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-help-commands-executable">How to Make Help Commands Executable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-syntax-highlighting">Syntax Highlighting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tab-completion">Tab Completion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-shell-commands">How to Add Shell Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-completion">How to Improve Completion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-typing-animation-command">Typing Animation Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-credits-command">Credits Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prefilled-commands">Prefilled Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sharing-link-to-terminal-session">Sharing Link to Terminal Session</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-working-terminal-portfolio-demo">Working Terminal Portfolio Demo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-executables-to-home-directory">Adding Executables to Home Directory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-next">What Next?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-share-what-youve-created">Share what you’ve created</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-terminal-and-its-history">What is the terminal and its history?</h2>
<p>Terminals have a long history. It started as an upgrade from <a target="_blank" href="https://en.wikipedia.org/wiki/Punched_card">punch cards</a>. Computers back in the day used teletypes, which was just a keyboard and a printer. You'd type on the keyboard, and the keystrokes would be sent to the computer (usually mainframe) and the output got printed on a printer.</p>
<p>Later on, the teletypes were replaced with terminals. A terminal was like the dump computer we see today. It was a CRT Monitor with a keyboard. So instead of getting the output on the printer, it would be displayed on the monitor.</p>
<p>Today we still use this type of interface (the Command Line) to talk with computers.</p>
<p>The command line is a terminal emulator and is a big part of Unix systems, like GNU/Linux or MacOS. On Windows, you have PowerShell or cmd.exe file that allows you to type commands and get responses in the form of text. You can also install GNU/Linux system on Windows in the form of WSL. CLI interfaces are used mostly by power users, developers, and system administrators.</p>
<p>If you're new to the command line, you can read this article: <a target="_blank" href="https://www.freecodecamp.org/news/command-line-for-beginners/">Command Line for Beginners – How to Use the Terminal Like a Pro [Full Handbook]</a>.</p>
<h2 id="heading-what-is-jquery-terminal">What is jQuery Terminal?</h2>
<p>jQuery Terminal is a JavaScript library. It's a plugin for the <a target="_blank" href="https://en.wikipedia.org/wiki/JQuery">jQuery library</a>. jQuery Terminal is more like a framework that has jQuery as its dependency. We'll mostly use JavaScript and very little jQuery in this article.</p>
<p>Let's create our terminal-based portfolio using jQuery Terminal.</p>
<h3 id="heading-base-html-file">Base HTML file</h3>
<p>The first thing you need to do is to include jQuery and jQuery Terminal library in your project.</p>
<p>This is a basic HTML file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery.terminal/css/jquery.terminal.min.css"</span>/&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery.terminal/js/jquery.terminal.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"my-terminal.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Then inside the <strong>my-terminal.js</strong> file, we will write our code in JavaScript.</p>
<h3 id="heading-how-to-initialize-the-terminal">How to Initialize the Terminal</h3>
<p>To create a basic terminal, you need to put in this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {};

<span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands);
</code></pre>
<p>The string <code>'body'</code> indicates the CSS selector where terminal should be created. Here we use <code>'body'</code> so the terminal will be the only thing on the page. But it doesn't have to be full screen. You can create a website where the terminal is only part of the page, like in a window that looks like part of the Operating System.</p>
<p>The first argument to the terminal method is called an interpreter. It's a way to add your commands. An object is the simplest way to create them. See <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Getting-Started#creating-the-interpreter">creating the interpreter</a> to learn more.</p>
<p>If the terminal font is too small, you can make it a little bit bigger with CSS custom properties (also known as CSS variables):</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--size</span>: <span class="hljs-number">1.2</span>;
}
</code></pre>
<h3 id="heading-welcome-message">Welcome Message</h3>
<p>The first thing we need to do is to get rid of the default greeting message and replace it with nice-looking custom <a target="_blank" href="https://en.wikipedia.org/wiki/ASCII_art">ASCII Art</a>. We will use the <a target="_blank" href="https://en.wikipedia.org/wiki/FIGlet">Filget library</a> written in JavaScript to do this.</p>
<p>There are a few Figlet libraries on npm. We will use a package named <a target="_blank" href="https://www.npmjs.com/package/figlet">figlet</a>.</p>
<p>The first thing you should do is pick the right font. Go to <a target="_blank" href="https://patorjk.com/software/taag/">figlet playground</a> and write the text you want for your greeting. We will use "Terminal Portfolio" and click "Test All". It should display your text with all the fonts. Scroll through the list and pick the font you like.</p>
<p>I picked a font "slant" that looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Przechwycenie-obrazu-ekranu_2024-04-26_22-18-26.png" alt="Terminal Portfolio ASCII Art" width="600" height="400" loading="lazy"></p>
<p>You can copy this text and put into a string, but you will have issues like with the backslash that needs to be escaped using quote characters.</p>
<pre><code class="lang-lisp">const greetings = `  ______                    _             __   ____             __  ____      ___     
 /_  __/__  _________ ___  (<span class="hljs-name">_</span>)___  ____ _/ /  / __ \\____  _____/ /_/ __/___  / (<span class="hljs-name">_</span>)___ 
  / / / _ \\/ ___/ __ \`__ \\/ / __ \\/ __ \`/ /  / /_/ / __ \\/ ___/ __/ /_/ __ \\/ / / __ \\
 / / /  __/ /  / / / / / / / / / / /_/ / /  / ____/ /_/ / /  / /_/ __/ /_/ / / / /_/ /
/_/  \\___/_/  /_/ /_/ /_/_/_/ /_/\\__,_/_/  /_/    \\____/_/   \\__/_/  \\____/_/_/\\____/`

const term = $('body').terminal(<span class="hljs-name">commands</span>, {
    greetings
})<span class="hljs-comment">;</span>
</code></pre>
<p><strong>NOTE</strong>: The second argument to the jQuery Terminal is an object with options – we used a single option <code>greetings</code>.</p>
<p>This doesn't look good and it's hard to modify. Also, if you create the greeting by hardcoding a string, it may get distorted on smaller screens. That's why we will use the figlet library in JavaScript.</p>
<p>First, we need to include the figlet library in HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/figlet/lib/figlet.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>To initialize the library in JavaScript, we need to load the fonts:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> font = <span class="hljs-string">'Slant'</span>;

figlet.defaults({ <span class="hljs-attr">fontPath</span>: <span class="hljs-string">'https://unpkg.com/figlet/fonts/'</span> });
figlet.preloadFonts([font], ready);
</code></pre>
<p>This code will load the <code>'Slant'</code> font and call the function <code>ready</code> when the font is loaded.</p>
<p>So we need to write this function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{

}
</code></pre>
<p>Now we can do two things, we can put the initialization of jQuery Terminal inside that function:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> term;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term =  $(<span class="hljs-string">'body'</span>).terminal(commands, {
      greetings
   });
}
</code></pre>
<p>With this, we can use the <code>greeting</code> option. But we can also use the <code>echo</code> method to render the greeting, and when initializing the terminal we will put <code>null</code> or <code>false</code> as a <code>greetings</code> to disable the default one:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(greetings);
}
</code></pre>
<p>This will work better because the library will initialize the terminal immediately and will not need to wait for loading the fonts.</p>
<p>Note that we still need to define the greetings using figlet. To do this we can write this function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> cols = term.cols();
    <span class="hljs-keyword">return</span> figlet.textSync(text, {
        <span class="hljs-attr">font</span>: font,
        <span class="hljs-attr">width</span>: cols,
        <span class="hljs-attr">whitespaceBreak</span>: <span class="hljs-literal">true</span>
    });
}
</code></pre>
<p>This function uses the <code>figlet::textSync()</code> method to return a string and use <code>terminal::cols()</code>, to get the number of characters per line. With this, we can make our text responsive.</p>
<p>This function can be used inside <code>ready</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(render(<span class="hljs-string">'Terminal Portfolio'</span>));
}
</code></pre>
<p>This will create a string and pass it to the <code>echo</code> method. But this will be the same as with:</p>
<pre><code class="lang-javascript">term.echo(greeting);
</code></pre>
<p>And our hard-coded greetings. So if you resize the terminal, the greetings can still get distorted. To make the text responsive, you need to <code>echo</code> a function. This function will be called on each re-render of the terminal, which will happen when you resize the page.</p>
<p>We can use the arrow function for this:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> render(<span class="hljs-string">'Terminal Portfolio'</span>));
}
</code></pre>
<p>If you want to add some text below the ASCII art, you can do this by concatenating the string after render:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> ascii = render(<span class="hljs-string">'Terminal Portfolio'</span>);
     <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${ascii}</span>\nWelcome to my Terminal Portfolio\n`</span>;
   });
}
</code></pre>
<p><strong>NOTE</strong>: If you run this code, you will notice that there is an empty line after the ASCII art. This is because the figlet library adds some spaces after the text.</p>
<p>To get rid of this, you can use <code>string::replace()</code> with a regular expression that will remove all spaces and newlines from the end.</p>
<p>We can't use <code>string::trim()</code>, because we don't want to remove the leading lines:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> cols = term.cols();
    <span class="hljs-keyword">return</span> trim(figlet.textSync(text, {
        <span class="hljs-attr">font</span>: font,
        <span class="hljs-attr">width</span>: cols,
        <span class="hljs-attr">whitespaceBreak</span>: <span class="hljs-literal">true</span>
    }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">trim</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">return</span> str.replace(<span class="hljs-regexp">/[\n\s]+$/</span>, <span class="hljs-string">''</span>);
}
</code></pre>
<p>You can also pause the terminal when it loads the fonts:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>
});

term.pause();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> render(<span class="hljs-string">'Terminal Portfolio'</span>)).resume();
}
</code></pre>
<p>You can can chain terminal methods, the same as with jQuery.</p>
<h3 id="heading-line-gaps">Line Gaps</h3>
<p>If the font you pick creates gaps between the lines, like in this image with font ANSI Shadow:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/Przechwycenie-obrazu-ekranu_2024-05-08_14-06-41.png" alt="Image: ASCII Art with line gaps" width="600" height="400" loading="lazy"></p>
<p>You can remove the gaps by adding the <code>ansi</code> option set to <code>true</code>. The option was added specifically to fix an issue with displaying <a target="_blank" href="https://en.wikipedia.org/wiki/ANSI_art">ANSI Art</a>.</p>
<pre><code class="lang-javascript">term.echo(<span class="hljs-function">() =&gt;</span> render(<span class="hljs-string">'Terminal Portfolio'</span>), { <span class="hljs-attr">ansi</span>: <span class="hljs-literal">true</span> });
</code></pre>
<p>The above ASCII Art will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/Przechwycenie-obrazu-ekranu_2024-05-08_14-57-16.png" alt="Image: ASCII Art with gaps removed" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-add-colors-to-ascii-art">How to Add Colors to ASCII Art</h2>
<p>You can spice up your ASCII Art by using a library called lolcat. lolcat is a Linux command that can style text in the terminal with rainbow colors. There is also a library called <a target="_blank" href="https://www.npmjs.com/package/isomorphic-lolcat">isomorphic-lolcat</a>, that you can use in JavaScript to make your ASCII Art in rainbow colors.</p>
<h3 id="heading-terminal-formatting">Terminal Formatting</h3>
<p>To use the lolcat library, you first need to know how to change the colors of the terminal.</p>
<p>You can do this using low-level formatting that looks like this:</p>
<pre><code class="lang-lisp">[[b<span class="hljs-comment">;red;]some text]</span>
</code></pre>
<p>The whole text is wrapped in brackets and the formatting of the text is in additional brackets, where each argument is separated by a semicolon. To learn more about the syntax, you can read the Wiki Article: <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Formatting-and-Syntax-Highlighting">Formatting and Syntax Highlighting</a>.</p>
<p>Here, we'll only use a basic change of color. Instead of red, you can use CSS color names, hex color, or <code>rgb()</code>.</p>
<h3 id="heading-how-to-use-the-lolcat-library">How to Use the Lolcat Library</h3>
<p>To use the library, we first need to include it in HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/isomorphic-lolcat"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>To format the string with colors, we can use this function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rainbow</span>(<span class="hljs-params">string</span>) </span>{
    <span class="hljs-keyword">return</span> lolcat.rainbow(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">char, color</span>) </span>{
        char = $.terminal.escape_brackets(char);
        <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;<span class="hljs-subst">${hex(color)}</span>;]<span class="hljs-subst">${char}</span>]`</span>;
    }, string).join(<span class="hljs-string">'\n'</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hex</span>(<span class="hljs-params">color</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'#'</span> + [color.red, color.green, color.blue].map(<span class="hljs-function"><span class="hljs-params">n</span> =&gt;</span> {
        <span class="hljs-keyword">return</span> n.toString(<span class="hljs-number">16</span>).padStart(<span class="hljs-number">2</span>, <span class="hljs-string">'0'</span>);
    }).join(<span class="hljs-string">''</span>);
}
</code></pre>
<p>The <code>lolcat.rainbow</code> will call a function in every character from the input string, and pass color as an object with RGB values and the character.</p>
<h3 id="heading-rainbow-ascii-art-greetings">Rainbow ASCII Art Greetings</h3>
<p>To use this code, you need to wrap the call to render with <code>rainbow</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> ascii = rainbow(render(<span class="hljs-string">'Terminal Portfolio'</span>));
     <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${ascii}</span>\nWelcome to my Terminal Portfolio\n`</span>;
   }).resume();
}
</code></pre>
<p>You can also use two calls to echo, since only the Figlet message needs to be executed inside the function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> rainbow(render(<span class="hljs-string">'Terminal Portfolio'</span>)))
       .echo(<span class="hljs-string">'Welcome to my Terminal Portfolio\n'</span>).resume();
}
</code></pre>
<p>You'll notice that when you resize the window, the rainbow changes randomly. This is the default behavior of lolcat. To change it, you need to set the <a target="_blank" href="https://en.wikipedia.org/wiki/Random_seed">random seed</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rand</span>(<span class="hljs-params">max</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * (max + <span class="hljs-number">1</span>));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">const</span> seed = rand(<span class="hljs-number">256</span>);
   term.echo(<span class="hljs-function">() =&gt;</span> rainbow(render(<span class="hljs-string">'Terminal Portfolio'</span>), seed))
       .echo(<span class="hljs-string">'Welcome to my Terminal Portfolio\n'</span>).resume();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rainbow</span>(<span class="hljs-params">string, seed</span>) </span>{
    <span class="hljs-keyword">return</span> lolcat.rainbow(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">char, color</span>) </span>{
        char = $.terminal.escape_brackets(char);
        <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;<span class="hljs-subst">${hex(color)}</span>;]<span class="hljs-subst">${char}</span>]`</span>;
    }, string, seed).join(<span class="hljs-string">'\n'</span>);
}
</code></pre>
<p>The <code>rand</code> function returns a pseudo-random number from 0 to max value. Here we created a random value from 0 to 256.</p>
<h3 id="heading-how-to-make-the-greeting-text-white">How to Make the Greeting Text White</h3>
<p>As we showed previously, you can make the text white with terminal formatting. You can use:</p>
<ul>
<li><p><code>[[;white;]Welcome to my Terminal Portfolio]</code></p>
</li>
<li><p><code>[[;#fff;]Welcome to my Terminal Portfolio]</code></p>
</li>
<li><p><code>[[;rgb(255,255,255);]Welcome to my Terminal Portfolio]</code></p>
</li>
</ul>
<p>Moreover, if you include additional file XML formatting, you can use XML-like syntax. That makes formatting much easier.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery.terminal/js/xml_formatting.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>After including the above file in HTML, you can use CSS named colors as XML tags:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">white</span>&gt;</span>Welcome to my Terminal Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">white</span>&gt;</span>
</code></pre>
<p>The XML formatting supports more tags like links and images. See <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Formatting-and-Syntax-Highlighting#extension-xml-formatter">Extension XML Formatter</a> for more info.</p>
<p><strong>NOTE</strong>: XML formatter is a function added to <code>$.terminal.defaults.formatters</code>, which transforms the input XML-like text into terminal formatting. You can add the same to your own formatters.</p>
<h2 id="heading-how-to-make-your-first-command">How to Make Your First Command</h2>
<p>After the greeting, we can write our first command. It will be helpful and will work with any commands we add later.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commanns = {
    help() {

    }
};
</code></pre>
<p>This will be our help command where we'll add a list of commands available to our terminal portfolio. We will use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat">Intl.ListFormat</a>, which creates a list of elements with and before the last element.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formatter = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.ListFormat(<span class="hljs-string">'en'</span>, {
  <span class="hljs-attr">style</span>: <span class="hljs-string">'long'</span>,
  <span class="hljs-attr">type</span>: <span class="hljs-string">'conjunction'</span>,
});
</code></pre>
<p>To create a list, we need to use <code>formatter.format()</code> and pass an array of commands. To get that array we can use <code>Object.keys()</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    help() {
        term.echo(<span class="hljs-string">`List of available commands: <span class="hljs-subst">${help}</span>`</span>);
    }
};

<span class="hljs-keyword">const</span> command_list = <span class="hljs-built_in">Object</span>.keys(commands);
<span class="hljs-keyword">const</span> help = formatter.format(command_list);
</code></pre>
<p>When you type help you should see:</p>
<pre><code class="lang-lisp">List of available commands: help
</code></pre>
<p>You also need to add the <code>echo</code> command:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    help() {
        term.echo(<span class="hljs-string">`List of available commands: <span class="hljs-subst">${help}</span>`</span>);
    },
    echo(...args) {
        term.echo(args.join(<span class="hljs-string">' '</span>));
    }
};
</code></pre>
<p>Now the help command works:</p>
<pre><code class="lang-lisp">List of available commands: help and echo
</code></pre>
<p>But if you try to execute 'echo hello' you will get an error:</p>
<pre><code class="lang-lisp">[Arity] Wrong number of arguments. The function 'echo' expects <span class="hljs-number">0</span> got <span class="hljs-number">1</span>!
</code></pre>
<p>By default, jQuery Terminal checks the number of arguments and the number of parameters the function accepts. The problem is that the <code>rest</code> operator makes all arguments optional and the length function property is 0. To fix the issue we need to disable the <code>Arity</code> check with an option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>
});
</code></pre>
<p>Now the echo commands should work.</p>
<h2 id="heading-default-commands">Default Commands</h2>
<p>By default, the jQuery Terminal has two default commands:</p>
<ul>
<li><p><code>clear</code>: this command clears everything on the terminal.</p>
</li>
<li><p><code>exit</code>: this command exits from nested interpreters.</p>
</li>
</ul>
<p>You can disable them by passing the name to the option and setting it to false. Since we won't use nested interpreters, we can disable <code>exit</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">exit</span>: <span class="hljs-literal">false</span>
});
</code></pre>
<p>But <code>clear</code> can be useful. So we can add it to the list of commands:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> command_list = [<span class="hljs-string">'clear'</span>].concat(<span class="hljs-built_in">Object</span>.keys(commands));
</code></pre>
<h2 id="heading-how-to-make-help-commands-executable">How to Make Help Commands Executable</h2>
<p>We can make the UX better to allow clicking on the command and execute it just like when the user types it.</p>
<p>We will need a few things to do this. First, we need to add formatting to each command and add an HTML class attribute. We can also make the command white so it's more visible.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> command_list = <span class="hljs-built_in">Object</span>.keys(commands);
<span class="hljs-keyword">const</span> formatted_list = command_list.map(<span class="hljs-function"><span class="hljs-params">cmd</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;white class="command"&gt;<span class="hljs-subst">${cmd}</span>&lt;/white&gt;`</span>;
});
<span class="hljs-keyword">const</span> help = formatter.format(formatted_list);
</code></pre>
<p>Next is to add <a target="_blank" href="https://en.wikipedia.org/wiki/Affordance">affordance</a>. To indicate that the user can click the command, we need to change the cursor in CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.command</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<p>The last step is to execute the command when the user clicks the command. We need to add an event handler with jQuery (jQuery Terminal dependency) or we can use the native browser <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener</a>. Here we use jQuery:</p>
<pre><code class="lang-javascript">term.on(<span class="hljs-string">'click'</span>, <span class="hljs-string">'.command'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">const</span> command = $(<span class="hljs-built_in">this</span>).text();
   term.exec(command);
});
</code></pre>
<p><code>terminal::exec()</code> is a way to execute a command programmatically, just like user would type it and press enter.</p>
<p>You can test it by typing <code>help</code> and clicking <code>help</code> again.</p>
<p>Clicking <code>echo</code> will print an empty line. We can fix it by checking if the array of arguments is not empty, before executing <code>terminal::echo()</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    echo(...args) {
        <span class="hljs-keyword">if</span> (args.length &gt; <span class="hljs-number">0</span>) {
            term.echo(args.join(<span class="hljs-string">' '</span>));
        }
    }
};
</code></pre>
<p>Now clicking on <code>echo</code> will only show the executed command.</p>
<p><strong>NOTE</strong>: If for any reason you don't want to show the prompt and the command that has been executed, you can silence the <code>exec</code> by passing <code>true</code> as the second argument.</p>
<pre><code class="lang-javascript">term.exec(<span class="hljs-string">'help'</span>, <span class="hljs-literal">true</span>);
</code></pre>
<h2 id="heading-syntax-highlighting">Syntax Highlighting</h2>
<p>As we discussed earlier, we can use custom syntax highlighting of our shell by pushing a function into <code>$.terminal.defaults.formatters</code>.We can also use the <code>$.terminal.new_formatter</code> helper function.</p>
<p>Let's make our commands white as we type them. The formatter can be an array (of regex and replacement), or a function. We have a fixed number of commands and we only want to make those that are on the list white. We can do this by adding a regular expression:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> any_command_re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">`^\s*(<span class="hljs-subst">${command_list.join(<span class="hljs-string">'|'</span>)}</span>)`</span>);
</code></pre>
<p>This regular expression will check if, at the beginning of the string, there is an optional whitespace and one of the commands. Right now the regex will look like this: <code>/^\s*(help|echo)/</code>. This is how to create new formatter:</p>
<pre><code class="lang-javascript">$.terminal.new_formatter([any_command_re, <span class="hljs-string">'&lt;white&gt;$1&lt;/white&gt;'</span>]);
</code></pre>
<p>If you would like to make command arguments in different colors, you'll need a function, where you will use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace">String::replace()</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">`^\s*(<span class="hljs-subst">${command_list.join(<span class="hljs-string">'|'</span>)}</span>) (.*)`</span>);

$.terminal.new_formatter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">string</span>) </span>{
    <span class="hljs-keyword">return</span> string.replace(re, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_, command, args</span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;white&gt;<span class="hljs-subst">${command}</span>&lt;/white&gt; &lt;aqua&gt;<span class="hljs-subst">${args}</span>&lt;/aqua&gt;`</span>;
    });
});
</code></pre>
<p>This is just an example of using <code>String::replace</code>. If you have just one replacement, you can use an array. This will be the same:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">`^\s*(<span class="hljs-subst">${command_list.join(<span class="hljs-string">'|'</span>)}</span>)(\s?.*)`</span>);

$.terminal.new_formatter([re, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_, command, args</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;white&gt;<span class="hljs-subst">${command}</span>&lt;/white&gt;&lt;aqua&gt;<span class="hljs-subst">${args}</span>&lt;/aqua&gt;`</span>;
}]);
</code></pre>
<p><strong>NOTE</strong>: If you add the class <code>&lt;white class="command"&gt;</code> to the formatter, you will be able to click on the typed command to execute it again.</p>
<h2 id="heading-tab-completion">Tab Completion</h2>
<p>Another feature we can add is to complete the command when you press the tab key. This is super easy – we only need to add the completion option set to true:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">exit</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">completion</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<p>Now when you type <code>h</code> and press tab, it will complete the command <code>help</code> for you.</p>
<h2 id="heading-how-to-add-shell-commands">How to Add Shell Commands</h2>
<p>Now we can add the most important commands that allow us to navigate through the portfolio. We will implement directories as the main entry point so the user will need to type the <code>ls</code> command to see a list of things, <code>cd</code> into that directory, and <code>ls</code> again to see the contents.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> directories = {
    <span class="hljs-attr">education</span>: [
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;education&lt;/white&gt;'</span>,

        <span class="hljs-string">'* &lt;a href="https://en.wikipedia.org/wiki/Kielce_University_of_Technology"&gt;Kielce University of Technology&lt;/a&gt; &lt;yellow&gt;"Computer Science"&lt;/yellow&gt; 2002-2007 / 2011-2014'</span>,
        <span class="hljs-string">'* &lt;a href="https://pl.wikipedia.org/wiki/Szko%C5%82a_policealna"&gt;Post-secondary&lt;/a&gt; Electronic School &lt;yellow&gt;"Computer Systems"&lt;/yellow&gt; 2000-2002'</span>,
        <span class="hljs-string">'* Electronic &lt;a href="https://en.wikipedia.org/wiki/Technikum_(Polish_education)"&gt;Technikum&lt;/a&gt; with major &lt;yellow&gt;"RTV"&lt;/yellow&gt; 1995-2000'</span>,
        <span class="hljs-string">''</span>
    ],
    <span class="hljs-attr">projects</span>: [
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;Open Source projects&lt;/white&gt;'</span>,
        [
            [<span class="hljs-string">'jQuery Terminal'</span>,
             <span class="hljs-string">'https://terminal.jcubic.pl'</span>,
             <span class="hljs-string">'library that adds terminal interface to websites'</span>
            ],
            [<span class="hljs-string">'LIPS Scheme'</span>,
             <span class="hljs-string">'https://lips.js.org'</span>,
             <span class="hljs-string">'Scheme implementation in JavaScript'</span>
            ],
            [<span class="hljs-string">'Sysend.js'</span>,
             <span class="hljs-string">'https://jcu.bi/sysend'</span>,
             <span class="hljs-string">'Communication between open tabs'</span>
            ],
            [<span class="hljs-string">'Wayne'</span>,
             <span class="hljs-string">'https://jcu.bi/wayne'</span>,
             <span class="hljs-string">'Pure in browser HTTP requests'</span>
            ],
        ].map(<span class="hljs-function">(<span class="hljs-params">[name, url, description = <span class="hljs-string">''</span>]</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-string">`* &lt;a href="<span class="hljs-subst">${url}</span>"&gt;<span class="hljs-subst">${name}</span>&lt;/a&gt; &amp;mdash; &lt;white&gt;<span class="hljs-subst">${description}</span>&lt;/white&gt;`</span>;
        }),
        <span class="hljs-string">''</span>
    ].flat(),
    <span class="hljs-attr">skills</span>: [
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;languages&lt;/white&gt;'</span>,

        [
            <span class="hljs-string">'JavaScript'</span>,
            <span class="hljs-string">'TypeScript'</span>,
            <span class="hljs-string">'Python'</span>,
            <span class="hljs-string">'SQL'</span>,
            <span class="hljs-string">'PHP'</span>,
            <span class="hljs-string">'Bash'</span>
        ].map(<span class="hljs-function"><span class="hljs-params">lang</span> =&gt;</span> <span class="hljs-string">`* &lt;yellow&gt;<span class="hljs-subst">${lang}</span>&lt;/yellow&gt;`</span>),
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;libraries&lt;/white&gt;'</span>,
        [
            <span class="hljs-string">'React.js'</span>,
            <span class="hljs-string">'Redux'</span>,
            <span class="hljs-string">'Jest'</span>,
        ].map(<span class="hljs-function"><span class="hljs-params">lib</span> =&gt;</span> <span class="hljs-string">`* &lt;green&gt;<span class="hljs-subst">${lib}</span>&lt;/green&gt;`</span>),
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;tools&lt;/white&gt;'</span>,
        [
            <span class="hljs-string">'Docker'</span>,
            <span class="hljs-string">'git'</span>,
            <span class="hljs-string">'GNU/Linux'</span>
        ].map(<span class="hljs-function"><span class="hljs-params">lib</span> =&gt;</span> <span class="hljs-string">`* &lt;blue&gt;<span class="hljs-subst">${lib}</span>&lt;/blue&gt;`</span>),
        <span class="hljs-string">''</span>
    ].flat()
};
</code></pre>
<p>This is our basic structure. You can edit it and add your own information. First, we will add a <code>cd</code> command that changes the directory.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> root = <span class="hljs-string">'~'</span>;
<span class="hljs-keyword">let</span> cwd = root;

<span class="hljs-keyword">const</span> commands = {
    cd(dir = <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">if</span> (dir === <span class="hljs-literal">null</span> || (dir === <span class="hljs-string">'..'</span> &amp;&amp; cwd !== root)) {
            cwd = root;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir.startsWith(<span class="hljs-string">'~/'</span>) &amp;&amp; dirs.includes(dir.substring(<span class="hljs-number">2</span>))) {
            cwd = dir;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir.startsWith(<span class="hljs-string">'../'</span>) &amp;&amp; cwd !== root &amp;&amp;
                   dirs.includes(dir.substring(<span class="hljs-number">3</span>))) {
            cwd = root + <span class="hljs-string">'/'</span> + dir.substring(<span class="hljs-number">3</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dirs.includes(dir)) {
            cwd = root + <span class="hljs-string">'/'</span> + dir;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Wrong directory'</span>);
        }
    }
};
</code></pre>
<p>This will handle all the cases of changing the directory. Next is to add a prompt.</p>
<p>To see what directory we are in, we need to add a custom <code>prompt</code>. We can create a function as a <code>prompt</code> option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> user = <span class="hljs-string">'guest'</span>;
<span class="hljs-keyword">const</span> server = <span class="hljs-string">'freecodecamp.org'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">prompt</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;green&gt;<span class="hljs-subst">${user}</span>@<span class="hljs-subst">${server}</span>&lt;/green&gt;:&lt;blue&gt;<span class="hljs-subst">${cwd}</span>&lt;/blue&gt;$ `</span>;
}
</code></pre>
<p>And use it as an option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">completion</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">exit</span>: <span class="hljs-literal">false</span>,
    prompt
});
</code></pre>
<p>The green color don't look very good, so we can use a color from Ubuntu to make the terminal look more real. We can overwrite the default XML color tags like this:</p>
<pre><code class="lang-javascript">$.terminal.xml_formatter.tags.green = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;#44D544;]`</span>;
};
</code></pre>
<p>Next is the <code>ls</code> command.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">print_home</span>(<span class="hljs-params"></span>) </span>{
     term.echo(dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;blue class="directory"&gt;<span class="hljs-subst">${dir}</span>&lt;/blue&gt;`</span>;
     }).join(<span class="hljs-string">'\n'</span>));
}

<span class="hljs-keyword">const</span> commands = {
    ls(dir = <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">if</span> (dir) {
            <span class="hljs-keyword">if</span> (dir.match(<span class="hljs-regexp">/^~\/?$/</span>)) {
                <span class="hljs-comment">// ls ~ or ls ~/</span>
                print_home();
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir.startsWith(<span class="hljs-string">'~/'</span>)) {
                <span class="hljs-keyword">const</span> path = dir.substring(<span class="hljs-number">2</span>);
                <span class="hljs-keyword">const</span> dirs = path.split(<span class="hljs-string">'/'</span>);
                <span class="hljs-keyword">if</span> (dirs.length &gt; <span class="hljs-number">1</span>) {
                    <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Invalid directory'</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">const</span> dir = dirs[<span class="hljs-number">0</span>];
                    <span class="hljs-built_in">this</span>.echo(directories[dir].join(<span class="hljs-string">'\n'</span>));
                }
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (cwd === root) {
                <span class="hljs-keyword">if</span> (dir <span class="hljs-keyword">in</span> directories) {
                    <span class="hljs-built_in">this</span>.echo(directories[dir].join(<span class="hljs-string">'\n'</span>));
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Invalid directory'</span>);
                }
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir === <span class="hljs-string">'..'</span>) {
                print_home();
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Invalid directory'</span>);
            }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (cwd === root) {
            print_home();
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">const</span> dir = cwd.substring(<span class="hljs-number">2</span>);
            <span class="hljs-built_in">this</span>.echo(directories[dir].join(<span class="hljs-string">'\n'</span>));
        }
    }
};
</code></pre>
<p>Similar to the green we had before, the blue color is not that great. So we can use the color from Ubuntu again. To do this, we need to use the same custom XML tags for the color blue:</p>
<pre><code class="lang-javascript">$.terminal.xml_formatter.tags.blue = <span class="hljs-function">(<span class="hljs-params">attrs</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;#55F;;<span class="hljs-subst">${attrs.<span class="hljs-keyword">class</span>}</span>]`</span>;
};
</code></pre>
<p>We added the HTML class for a reason. Let's change directory when the user clicks the directory. Just like we did with commands, we can invoke the <code>cd</code> command the same way as as a user would type it.</p>
<pre><code class="lang-javascript">term.on(<span class="hljs-string">'click'</span>, <span class="hljs-string">'.directory'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> dir = $(<span class="hljs-built_in">this</span>).text();
    term.exec(<span class="hljs-string">`cd ~/<span class="hljs-subst">${dir}</span>`</span>);
});
</code></pre>
<p><strong>NOTE</strong>: if you have long command and want to get the text for that command, it's better to use: <code>$(this).data('text')</code>. When the single formatting is wrapped (when text is longer than the width of the terminal) the <code>.text()</code> will no longer have full text, but the full text is always in the <code>data-text</code> HTML attribute.</p>
<p>We also need to update our CSS to change the cursor:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.command</span>, <span class="hljs-selector-class">.directory</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<h2 id="heading-how-to-improve-completion">How to Improve Completion</h2>
<p>Our completion is not perfect as it only completes the commands. If you'd like to have completion that also handles directories, you need to use a function:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    completion(string) {
        <span class="hljs-comment">// in every function we can use `this` to reference term object</span>
        <span class="hljs-keyword">const</span> cmd = <span class="hljs-built_in">this</span>.get_command();
        <span class="hljs-comment">// we process the command to extract the command name</span>
        <span class="hljs-comment">// and the rest of the command (the arguments as one string)</span>
        <span class="hljs-keyword">const</span> { name, rest } = $.terminal.parse_command(cmd);
        <span class="hljs-keyword">if</span> ([<span class="hljs-string">'cd'</span>, <span class="hljs-string">'ls'</span>].includes(name)) {
            <span class="hljs-keyword">if</span> (rest.startsWith(<span class="hljs-string">'~/'</span>)) {
                <span class="hljs-keyword">return</span> dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> <span class="hljs-string">`~/<span class="hljs-subst">${dir}</span>`</span>);
            }
            <span class="hljs-keyword">if</span> (rest.startsWith(<span class="hljs-string">'../'</span>) &amp;&amp; cwd != root) {
                <span class="hljs-keyword">return</span> dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> <span class="hljs-string">`../<span class="hljs-subst">${dir}</span>`</span>);
            }
            <span class="hljs-keyword">if</span> (cwd === root) {
                <span class="hljs-keyword">return</span> dirs;
            }
        }
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(commands);
    },
    prompt
});
</code></pre>
<p><strong>NOTE</strong>: The string argument was left as documentation. It can be used if you only want to complete a single word.</p>
<h2 id="heading-typing-animation-command">Typing Animation Command</h2>
<p>Another command that we will add is an animated joke. We will print random jokes using an API that looks like the user typing.</p>
<p>We will use the <a target="_blank" href="https://jokeapi.dev/">Joke API</a> for this purpose.</p>
<p>The API returns JSON with two types of responses: <code>twopart</code> and a <code>single</code>. This is the code that prints the text on the terminal:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// we use programming jokes so it fit better</span>
<span class="hljs-comment">// developer portfolio</span>
<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://v2.jokeapi.dev/joke/Programming'</span>;
<span class="hljs-keyword">const</span> commands = {
    <span class="hljs-keyword">async</span> joke() {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(url);
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
        <span class="hljs-keyword">if</span> (data.type == <span class="hljs-string">'twopart'</span>) {
            <span class="hljs-comment">// as said before in every function, passed directly</span>
            <span class="hljs-comment">// to the terminal, you can use `this` object</span>
            <span class="hljs-comment">// to reference terminal instance</span>
            <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`Q: <span class="hljs-subst">${data.setup}</span>`</span>);
            <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`A: <span class="hljs-subst">${data.delivery}</span>`</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data.type === <span class="hljs-string">'single'</span>) {
            <span class="hljs-built_in">this</span>.echo(data.joke);
        }
    },
}
</code></pre>
<p>To add typing animation, you need to add an option to the <code>echo</code> method:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">this</span>.echo(data.joke, { <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>, <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span> });
</code></pre>
<p>There is one caveat: if you have a sequence of typing animations, you need to await for the previous one to finish (the echo will return a promise when animating). When creating such animation you can wrap your code with <code>animation</code> method:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// we use programming jokes so it fits better</span>
<span class="hljs-comment">// developer portfolio</span>
<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://v2.jokeapi.dev/joke/Programming'</span>;
<span class="hljs-keyword">const</span> commands = {
    <span class="hljs-keyword">async</span> joke() {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(url);
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
        <span class="hljs-keyword">if</span> (data.type == <span class="hljs-string">'twopart'</span>) {
            <span class="hljs-comment">// this method allow to create sequence of animations</span>
            <span class="hljs-built_in">this</span>.animation(<span class="hljs-keyword">async</span> () =&gt; {
                <span class="hljs-comment">// as said before in every function, passed </span>
                <span class="hljs-comment">// directly to terminal, you can use `this` object</span>
                <span class="hljs-comment">// to reference terminal instance</span>
                <span class="hljs-comment">// and since we use arrow function we reference</span>
                <span class="hljs-comment">// this from joke function/command</span>
                <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`Q: <span class="hljs-subst">${data.setup}</span>`</span>, {
                    <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>,
                    <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>
                });
                <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`A: <span class="hljs-subst">${data.delivery}</span>`</span>, {
                    <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>,
                    <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>
                });
            });
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data.type === <span class="hljs-string">'single'</span>) {
            <span class="hljs-built_in">this</span>.echo(data.joke, {
                <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>,
                <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>
            });
        }
    }
};
</code></pre>
<p>You can read more about typing animation in this article: <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Typing-Animation">Typing Animation</a><a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Typing-Animation#sequence-of-animations">.</a></p>
<h2 id="heading-credits-command">Credits Command</h2>
<p>The last command we will add is a credits command where we will list the JavaScript libraries we used:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    credits() {
        <span class="hljs-keyword">return</span> [
            <span class="hljs-string">''</span>,
            <span class="hljs-string">'&lt;white&gt;Used libraries:&lt;/white&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://terminal.jcubic.pl"&gt;jQuery Terminal&lt;/a&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://github.com/patorjk/figlet.js/"&gt;Figlet.js&lt;/a&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://github.com/jcubic/isomorphic-lolcat"&gt;Isomorphic Lolcat&lt;/a&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://jokeapi.dev/"&gt;Joke API&lt;/a&gt;'</span>,
            <span class="hljs-string">''</span>
        ].join(<span class="hljs-string">'\n'</span>);
    }
};
</code></pre>
<p>This is an example of another way to print something on the terminal – if you return something from a function it will be printed. You can also return a <a target="_blank" href="https://www.freecodecamp.org/news/javascript-promises-explained/">Promise</a>, so you can send an <a target="_blank" href="https://en.wikipedia.org/wiki/Ajax_\(programming\)">AJAX</a> request to the server and print the results.</p>
<h2 id="heading-prefilled-commands">Prefilled Commands</h2>
<p>You can make it easier for users to know what to do with the terminal, especially if they are not that familiar with Unix. You can do this by executing example commands:</p>
<pre><code class="lang-javascript">term.exec(command)
</code></pre>
<p>You can also use animation with <code>exec</code>:</p>
<pre><code class="lang-javascript">term.exec(command, { <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span> });
</code></pre>
<h2 id="heading-sharing-link-to-terminal-session">Sharing Link to Terminal Session</h2>
<p>Another cool thing that I will show you is recording commands in the URL. You can create whole terminal session and save it in a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/URL/hash">URL hash</a>. To start recoding a session you need to execute the following:</p>
<pre><code class="lang-javascript">term.history_state(<span class="hljs-literal">true</span>);
</code></pre>
<p>When you execute the command <code>echo x</code>, it should create a URL hash that looks like this: <code>#[[0,1,"echo%20x"]]</code>.</p>
<p>To stop recording, you can use:</p>
<pre><code class="lang-javascript">term.history_state(<span class="hljs-literal">false</span>);
</code></pre>
<p>You can write this into a command <code>record start | stop</code>, so it will be easier to record sessions.</p>
<p>The last thing to do to restore the session is to use the option <code>execHash: true</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-comment">/* rest of the options */</span>
    <span class="hljs-attr">execHash</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<p>When you do this and refresh the page, while having the URL hash with the session, it should replay the session and you should see same output as you did when you recorded it.</p>
<p>If you want the <code>exec</code> to be animated you can use this option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-comment">/* rest of the options */</span>
    <span class="hljs-attr">execHash</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">execAnimation</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<p>To share the link, it’s better to use a URL shortener like <a target="_blank" href="https://tinyurl.com/"><strong>TinyURL</strong></a><strong>.</strong> Make sure you test the shortened URL to see if it works.</p>
<h2 id="heading-how-to-add-executables-to-the-home-directory">How to Add Executables to the Home Directory</h2>
<p>Another thing you can do to improve the portfolio is to help your visitor learn what commands they can use, by introducing executable when running ls. They will look like binaries on the Linux system.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// not every command needs to be binary</span>
<span class="hljs-comment">// we picked those three that works more like real programs</span>
<span class="hljs-keyword">const</span> files = [
    <span class="hljs-string">'joke'</span>,
    <span class="hljs-string">'credits'</span>,
    <span class="hljs-string">'record'</span>
];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">print_home</span>(<span class="hljs-params"></span>) </span>{
     term.echo(dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;blue class="directory"&gt;<span class="hljs-subst">${dir}</span>&lt;/blue&gt;`</span>;
     }).join(<span class="hljs-string">'\n'</span>));
     term.echo(files.map(<span class="hljs-function"><span class="hljs-params">file</span> =&gt;</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;green class="command"&gt;<span class="hljs-subst">${file}</span>&lt;/green&gt;`</span>;
     }).join(<span class="hljs-string">'\n'</span>));
}
</code></pre>
<p>With this, you will be able to click the command and execute it. So your visitors will know that they can run <code>joke</code> command without the need to type <code>help</code> command. For this to work, we need one last change, adding class to the green XML tag:</p>
<pre><code class="lang-javascript">$.terminal.xml_formatter.tags.green = <span class="hljs-function">(<span class="hljs-params">attrs</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;#44D544;;<span class="hljs-subst">${attrs.<span class="hljs-keyword">class</span>}</span>]`</span>;
};
</code></pre>
<h2 id="heading-working-terminal-portfolio-demo">Working Terminal Portfolio Demo</h2>
<p>Here is a fully working Demo of our <a target="_blank" href="https://codepen.io/jcubic/full/ZEZPWRY">Interactive Terminal Portfolio Website</a>.</p>
<h2 id="heading-what-next">What Next?</h2>
<p>You can add a lot of commands to this portfolio. The only limitation is your imagination.</p>
<p>You can check these examples for inspiration:</p>
<ul>
<li><p>CodePen Collection with <a target="_blank" href="https://codepen.io/collection/LPjoaW">jQuery Terminal Demos</a>.</p>
</li>
<li><p><a target="_blank" href="https://codepen.io/jcubic/pen/BwBYOZ">Retro (Vintage) Terminal CodePen Demo</a>.</p>
</li>
<li><p><a target="_blank" href="https://terminal.jcubic.pl/examples.php">jQuery Terminal Examples Page</a>.</p>
</li>
<li><p><a target="_blank" href="https://terminal.jcubic.pl/404">Terminal 404 Error Page</a>.</p>
</li>
<li><p><a target="_blank" href="https://fake.terminal.jcubic.pl/">Fake GNU/Linux Terminal</a>.</p>
</li>
</ul>
<p>If you have an idea that is not listed here, you can ask on <a target="_blank" href="https://stackoverflow.com/questions/tagged/jquery-terminal">StackOverflow with jquery-terminal tag</a>. If you have something more time consuming, you can also ask for <a target="_blank" href="https://support.jcubic.pl/">paid support</a>.</p>
<h2 id="heading-share-what-youve-created">Share what you’ve created</h2>
<p>If you create a cool terminal portfolio, you can <a target="_blank" href="http://twitter.com/jcubic">share it and tag me on Twitter</a>. I would love to take a look. Especially if you create something more than what’s included in the tutorial. You can also share on a <a target="_blank" href="https://jcu.bi/chat">terminal chat on my website</a> (it’s a similar terminal portfolio, but with chat).</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Dynamic Dropdown Component in React – React Compound Component Pattern Explained ]]>
                </title>
                <description>
                    <![CDATA[ Dropdowns have been an important part of websites and apps for a long time. They're an unsung heros of user interactions, silently facilitating countless actions and decisions with just a click or tap.  You probably encountered one today, whether it ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-dynamic-dropdown-component/</link>
                <guid isPermaLink="false">66bb88f7e32cc52292637d1f</guid>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Wed, 17 Apr 2024 19:21:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Article-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Dropdowns have been an important part of websites and apps for a long time. They're an unsung heros of user interactions, silently facilitating countless actions and decisions with just a click or tap. </p>
<p>You probably encountered one today, whether it was selecting a category on your favourite online store or choosing your birthdate on a registration form.</p>
<p>But what if I told you that there's a secret ingredient that can elevate your dropdowns from mundane to magnificent?</p>
<p>Join me as I dissect the mysteries of the compound component pattern and harness its abilities to build a dynamic dropdown component.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Fundamentals of HTML, CSS and Tailwind CSS</li>
<li>Fundamentals of React and React Hooks.</li>
</ul>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-understanding-dropdown-components">Understanding Dropdown Components</a></li>
<li><a class="post-section-overview" href="#heading-understanding-compound-components">Understanding Compound Components</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-dropdown-component">How to Build the Dropdown Component</a><br>– <a class="post-section-overview" href="#heading-regular-functional-react-method">Regular Functional React Method</a><br>– <a class="post-section-overview" href="#heading-the-compound-component-pattern-method">The Compound Component Pattern Method</a></li>
<li><a class="post-section-overview" href="#heading-comparison-between-the-regular-and-the-compound-component-method">Comparison between the Regular and the Compound Component Method</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-understanding-dropdown-components">Understanding Dropdown Components</h2>
<p>Dropdown components play a pivotal role in user interface design, functioning as interactive menus that empower users to make selections from a list of options. Typically, they comprise of a clickable area that, upon activation, unveils a roster of choices for the user to make a selection from.</p>
<p>The operation of a dropdown component is straightforward: when a user engages with it—often through clicking or tapping—the dropdown expands, showing the available options.</p>
<p>Subsequently, the user can opt for one of these choices, which is then either shown within the dropdown itself or utilized to update a related field or element in the interface.</p>
<p>Dropdown components provide a clean and efficient method to present a variety of choices to users, rendering them well-suited for scenarios where multiple options need to be accessible while maintaining a tidy interface.</p>
<p>Dropdowns also serve purposes such as:</p>
<ul>
<li><strong>Navigation Aid</strong>: Acting as navigational aids, dropdowns help users move through websites by providing menus to jump to different sections or pages.</li>
<li><strong>Form Inputs</strong>: Simplifying data entry, dropdowns present users with predefined options for selection, such as choosing a country, date of birth, or preferred language during account registration.</li>
<li><strong>Filters</strong>: In e-commerce platforms, dropdowns enable shoppers to refine their search results by selecting options like product categories, price ranges, or brands.</li>
<li><strong>Menu Selectors</strong>: Commonly used on restaurant websites, dropdowns display menus or allow users to choose a cuisine type, facilitating easy exploration and selection of dining options.</li>
<li><strong>Data Presentation</strong>: Dropdowns can organize and present data effectively, allowing users to filter information by criteria such as date range, geographic region, or product category in dashboards or analytics tools.</li>
</ul>
<p>An example of dropdown components can be seen here:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/01-Showing-Dropdown-Demo.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Showing Dropdown Demo</em></p>
<p><strong>Or on <a target="_blank" href="https://semantic-ui.com/modules/dropdown.html">Semantic UI’s page</a>.</strong></p>
<h2 id="heading-understanding-compound-components">Understanding Compound Components</h2>
<p>The compound component pattern is like building with LEGO blocks: you assemble smaller pieces to create something bigger and more complex. In React, it's a clever way of designing components from several smaller parts that work together seamlessly.</p>
<p>Imagine that you're building a dropdown menu. Instead of creating one monolithic component that handles everything, you break it down into smaller, reusable pieces. You might have one component for the dropdown button, another for the list of options, and yet another for handling the state and interaction logic.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/01-Compound-Component-Illustration.png" alt="Image" width="600" height="400" loading="lazy">
<em>Compound Component Illustration</em></p>
<p>Here's where it gets interesting: these smaller components communicate through a shared context. Context is like a messenger that carries information from one component to another without needing to pass it down through every level of the component tree. </p>
<p>It's a powerful tool that simplifies the process of sharing data between components, especially when they're deeply nested.</p>
<p>Now, why is this pattern so beneficial?</p>
<ul>
<li>First off, it improves readability. By breaking down a complex component into smaller, more focused pieces, the code becomes easier to understand and maintain. Each component has a clear responsibility, making it easier to debug and update.</li>
<li>Secondly, compound components enhance maintainability. Since each piece of the component handles a specific task, making changes or adding new features becomes much simpler. You can modify one part of the component without affecting the others, reducing the risk of introducing bugs.</li>
<li>Finally, compound components provide great flexibility. You can combine different parts to make special versions of the component without having to rewrite any code. This makes it easier to adjust the component for different purposes and design requirements.</li>
</ul>
<p>So, while the idea of using context to build UI components may seem unusual at first, it's a clever way of creating dynamic and reusable components that empower developers to build exceptional user experiences.</p>
<p>In the next section, we'll dive deeper into how context is used to bring compound components to life.</p>
<h2 id="heading-how-to-build-the-dropdown-component">How to Build the Dropdown Component</h2>
<p>I've prepared a GitHub repository with starter files to speed things up. Simply clone <a target="_blank" href="https://github.com/Daiveedjay/React-Dropdown-Component/tree/Starter">this repo</a> and install the dependencies.</p>
<p>In this section, we'll build a dropdown component using regular functional React, then compare it with the CC pattern to grasp the difference fully. PS: you'd like the compound component pattern. 😁</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/02-Oh-fo-sho-meme.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Oh Fo sho Snoop Dogg gif</em></p>
<h3 id="heading-regular-functional-react-method">Regular Functional React Method</h3>
<p>We'll start by creating the basic structure of our dropdown component. This will involve setting up the main dropdown container, the button to trigger the dropdown, and the list of options.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Dropdown = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><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">className</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>Assign user(s) to as task:<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">"  px-4 w-full py-2 flex items-center justify-between  rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">FiChevronDown</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#635FC7"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</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">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
</code></pre>
<p>Which gives:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/02-Dropdown-button-rendered.png" alt="Image" width="600" height="400" loading="lazy">
<em>Dropdown button rendered</em></p>
<p>Then pass in the users array into the dropdown to create a list of users.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Dropdown = <span class="hljs-function">(<span class="hljs-params">{ usersArray }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><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">className</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>Assign user(s) to as task:<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">"  px-4 w-full py-2 flex items-center justify-between  rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">FiChevronDown</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#635FC7"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-full translate-x-9  left-full translate-y-full rounded bg-[#20212c] w-max"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col p-2"</span>&gt;</span>
              {usersArray.map((user) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>
                  <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">items-center</span> <span class="hljs-attr">gap-2</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">2b2c37</span>] <span class="hljs-attr">rounded</span> <span class="hljs-attr">transition-all</span> <span class="hljs-attr">duration-200</span> `}&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span>
                    <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
                  /&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</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 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>
  );
};
</code></pre>
<p>Which gives:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/03-Dropdown-list-rendered.png" alt="Image" width="600" height="400" loading="lazy">
<em>Dropdown list rendered</em></p>
<p>At the moment, your dropdown list is showing by default. To add the toggle behavior, create a state for its visibility.</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">const</span> [isDropdownOpen, setIsDropdownOpen] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<p>Then pass down both as props to the <code>Dropdown</code> component.</p>
<pre><code class="lang-jsx">&lt;Dropdown
 usersArray={usersArray}
 isDropdownOpen={isDropdownOpen}
 setIsDropdownOpen={setIsDropdownOpen}
 /&gt;
</code></pre>
<p>Before you see the result, attach a toggle function that changes the dropdown state to true to the dropdown button.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> toggleDropdown = <span class="hljs-function">() =&gt;</span> {
    setIsDropdownOpen(<span class="hljs-literal">true</span>);
};
</code></pre>
<p>Your dropdown component should now look like this:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Dropdown = <span class="hljs-function">(<span class="hljs-params">{ usersArray, setIsDropdownOpen, isDropdownOpen }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> toggleDropdown = <span class="hljs-function">() =&gt;</span> {
    setIsDropdownOpen(<span class="hljs-literal">true</span>);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><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">className</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>Assign user(s) to as task:<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">"  px-4 w-full py-2 flex items-center justify-between  rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "</span>
        // <span class="hljs-attr">Function</span> <span class="hljs-attr">to</span> <span class="hljs-attr">show</span> <span class="hljs-attr">the</span> <span class="hljs-attr">dropdown</span> <span class="hljs-attr">on</span> <span class="hljs-attr">click</span>
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{toggleDropdown}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">FiChevronDown</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#635FC7"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      // Conditionally rendering your dropdown list
        {isDropdownOpen &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-full translate-x-9  left-full translate-y-full rounded bg-[#20212c] w-max"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col p-2"</span>&gt;</span>
              {usersArray.map((user) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>
                  <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">items-center</span> <span class="hljs-attr">gap-2</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">2b2c37</span>] <span class="hljs-attr">rounded</span> <span class="hljs-attr">transition-all</span> <span class="hljs-attr">duration-200</span> `}&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span>
                    <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
                  /&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</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 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>
  );
};
</code></pre>
<p>Your dropdown now behaves like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/03-Dropdown-with-list-conditionally-rendering.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Dropdown with list conditionally rendering</em></p>
<p>I know you’ve noticed your dropdown only opens, but doesn’t close. Don’t worry, we’d fix that later in a much cleaner way. 😉</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/04-Trust-the-process.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Trust the process</em></p>
<p>Next up, let’s create a way to assign users to the task. Start by creating a state to store the assigned users in the <code>App</code> component.</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">const</span> [assignedList, setAssignedList] = useState([]);
</code></pre>
<p>Then pass in the values as props to the <code>Dropdown</code> component.</p>
<pre><code class="lang-jsx">&lt;Dropdown
  usersArray={usersArray}
  isDropdownOpen={isDropdownOpen}
  setIsDropdownOpen={setIsDropdownOpen}
  assignedList={assignedList}
  setAssignedList={setAssignedList}
/&gt;
</code></pre>
<p>To assign users to the task, create a handler function which first checks if the user you're trying to add is already in the array, adds them if they're not, and removes them if they are.</p>
<pre><code class="lang-jsx">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleAssign</span>(<span class="hljs-params">user</span>) </span>{
    setAssignedList(<span class="hljs-function">(<span class="hljs-params">prevList</span>) =&gt;</span> {
      <span class="hljs-comment">// Check if the user already exists in the list</span>
      <span class="hljs-keyword">if</span> (prevList.includes(user)) {
        <span class="hljs-comment">// If user exists, remove it from the list</span>
        <span class="hljs-keyword">const</span> updatedList = prevList.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item !== user);
        <span class="hljs-keyword">return</span> updatedList;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If user doesn't exist, add it to the list</span>
        <span class="hljs-keyword">return</span> [...prevList, user];
      }
    });
  }
</code></pre>
<p>To confirm this function works, use the <code>assignedList</code> array to add a check icon to every assigned user.</p>
<pre><code class="lang-jsx">&lt;ul className=<span class="hljs-string">"flex flex-col p-2"</span>&gt;
  {usersArray.map(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span>
      <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">items-center</span> <span class="hljs-attr">gap-2</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">2b2c37</span>] <span class="hljs-attr">rounded</span> <span class="hljs-attr">transition-all</span> <span class="hljs-attr">duration-200</span> `}
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleAssign(user)}
    &gt;
      {assignedList.includes(user) &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">FiCheck</span> /&gt;</span>}

      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span>
        <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
        <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
  ))}
&lt;/ul&gt;
</code></pre>
<p>With this change, the dropdown should assign and unassign users on the click of each user.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/05-Assigning-and-unassigning-users-to-the-task.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Assigning and unassigning users to the task</em></p>
<p>To improve the UI, let’s create a component which shows all the assigned users.</p>
<p>Create an <code>AssignedList</code> component and pass in its respective states.</p>
<pre><code class="lang-jsx"> &lt;AssignedList
   assignedList={assignedList}
   setAssignedList={setAssignedList}
  /&gt;
</code></pre>
<p>Then use the assigned array to create some JSX.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AssignedList</span>(<span class="hljs-params">{ assignedList, setAssignedList }</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">"mt-4 p-2 shadow-sm bg-[#828fa318] rounded"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2 my-3 font-bold"</span>&gt;</span>Assigned list:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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-wrap gap-4 "</span>&gt;</span>
        {assignedList?.map((user, index) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-1 w-[47.5%] p-2 hover:bg-[#20212c] rounded transition-all duration-200"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{index + 1}.<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span>
              <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
              <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
            /&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-auto cursor-pointer p-1 hover:bg-[#2b2c37] rounded-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">FaXmark</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">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>Testing out your component now yields:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/06-Displaying-assigned-users-using-the-AssignedList-component.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Displaying assigned users using the AssignedList component</em></p>
<p>One of the final touches would be to use the <strong>x</strong> icon to remove a user from the assignment.</p>
<p>This can be done using the <code>setAssigned</code> function to filter out the user based on their <code>id</code>.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRemove</span>(<span class="hljs-params">id</span>) </span>{
    setAssignedList(<span class="hljs-function">(<span class="hljs-params">assignedList</span>) =&gt;</span>
      assignedList.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.id !== id)
    );
  }
</code></pre>
<p>Then pass in the user id from the loop.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AssignedList</span>(<span class="hljs-params">{ assignedList, setAssignedList }</span>) </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRemove</span>(<span class="hljs-params">id</span>) </span>{
    setAssignedList(<span class="hljs-function">(<span class="hljs-params">assignedList</span>) =&gt;</span>
      assignedList.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.id !== id)
    );
  }
  <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">"mt-4 p-2 shadow-sm bg-[#828fa318] rounded"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2 my-3 font-bold"</span>&gt;</span>Assigned list:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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-wrap gap-4 "</span>&gt;</span>
        {assignedList?.map((user, index) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-1 w-[47.5%] p-2 hover:bg-[#20212c] rounded transition-all duration-200"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleRemove(user.id)}&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{index + 1}.<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span>
              <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
              <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
            /&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-auto cursor-pointer p-1 hover:bg-[#2b2c37] rounded-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">FaXmark</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">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>This gives:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/07-Removing-users-from-assignment-using-the-AssignedList-component-1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Removing users from assignment using the AssignedList component</em></p>
<p>Another final touch would be to close the dropdown list at some user interaction.</p>
<p>To begin, I like using a reusable hook for this, which takes in a reference element and a function to fire when any area outside my target element is clicked.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> useClickOutside = <span class="hljs-function">(<span class="hljs-params">ref, handler</span>) =&gt;</span> {
  <span class="hljs-comment">// console.log(handler, ref);</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> listener = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
      <span class="hljs-comment">// Do nothing if clicking ref's element or descendent elements</span>
      <span class="hljs-keyword">if</span> (!ref.current || ref.current.contains(event.target)) {
        <span class="hljs-keyword">return</span>;
      }

      handler(event);
    };

    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"mousedown"</span>, listener);
    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"touchstart"</span>, listener);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">"mousedown"</span>, listener);
      <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">"touchstart"</span>, listener);
    };
  }, [ref, handler]);
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> useClickOutside;
</code></pre>
<p>Then in our <code>App</code> component, create a ref using the <code>useRef</code> hook to select an element.</p>
<pre><code class="lang-jsx">  <span class="hljs-keyword">const</span> dropdownContainerRef = useRef(<span class="hljs-literal">null</span>);
</code></pre>
<p>Then assign it to your preferred element.</p>
<pre><code class="lang-jsx"><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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [isDropdownOpen, setIsDropdownOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">const</span> [assignedList, setAssignedList] = useState([]);

  <span class="hljs-keyword">const</span> dropdownContainerRef = useRef(<span class="hljs-literal">null</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-[#2b2c37] h-[100dvh] text-white flex  p-20 gap-4 items-center flex-col"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" w-[400px] "</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{dropdownContainerRef}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl "</span>&gt;</span>Regular Functional React Pattern<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Dropdown</span>
          <span class="hljs-attr">usersArray</span>=<span class="hljs-string">{usersArray}</span>
          <span class="hljs-attr">isDropdownOpen</span>=<span class="hljs-string">{isDropdownOpen}</span>
          <span class="hljs-attr">setIsDropdownOpen</span>=<span class="hljs-string">{setIsDropdownOpen}</span>
          <span class="hljs-attr">assignedList</span>=<span class="hljs-string">{assignedList}</span>
          <span class="hljs-attr">setAssignedList</span>=<span class="hljs-string">{setAssignedList}</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">AssignedList</span>
          <span class="hljs-attr">assignedList</span>=<span class="hljs-string">{assignedList}</span>
          <span class="hljs-attr">setAssignedList</span>=<span class="hljs-string">{setAssignedList}</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>Finally, import your hook and pass in the reference element and the function handler function for closing the dropdown.</p>
<pre><code class="lang-jsx"> useClickOutside(dropdownContainerRef, <span class="hljs-function">() =&gt;</span> {
    setIsDropdownOpen(<span class="hljs-literal">false</span>);
  });
</code></pre>
<p>Testing your component now gives:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/08-Closing-the-dropdown-component-with-outside-clicks.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Closing the dropdown component with outside clicks</em></p>
<p>Or using a predefined button in the dropdown.</p>
<pre><code class="lang-jsx">&lt;button
  className=<span class="hljs-string">"px-4 w-full py-2 flex items-center justify-between rounded border border-[#828FA340] hover:border-primary cursor-pointer relative"</span>
  onClick={toggleDropdown}
&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">FiChevronDown</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#635FC7"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
  {isDropdownOpen &amp;&amp; (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-full translate-x-9 left-full translate-y-full rounded bg-[#20212c] w-max"</span>&gt;</span>
      {/* Close button */}
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute top-0 right-0 flex items-center justify-center -translate-y-full gap-2 bg-[#C0392B] px-2 py-1 rounded-t"</span>
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
          e.stopPropagation();
          setIsDropdownOpen(false);
          console.log(isDropdownOpen);
        }}
      &gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">FaXmark</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</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">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col p-2"</span>&gt;</span>
        {usersArray.map((user) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">items-center</span> <span class="hljs-attr">gap-2</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">2b2c37</span>] <span class="hljs-attr">rounded</span> <span class="hljs-attr">transition-all</span> <span class="hljs-attr">duration-200</span>`}
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleAssign(user)}
          &gt;
            {assignedList.includes(user) &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">FiCheck</span> /&gt;</span>}
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6"</span>
              <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
              <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
            /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</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>
  )}
&lt;/button&gt;
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/09-Closing-the-dropdown-with-the-designated-button.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Closing the dropdown with the designated button</em></p>
<p>The final change is an opinionated one as I’d rather show something else if there aren’t any users currently assigned to the task.</p>
<pre><code class="lang-jsx">{assignedList.length === <span class="hljs-number">0</span> ? (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 p-2 shadow-sm bg-[#828fa318] rounded"</span>&gt;</span>
    No users assigned to the task yet.
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
) : (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AssignedList</span>
    <span class="hljs-attr">assignedList</span>=<span class="hljs-string">{assignedList}</span>
    <span class="hljs-attr">setAssignedList</span>=<span class="hljs-string">{setAssignedList}</span>
  /&gt;</span></span>
)}
</code></pre>
<p>This brings the UI to:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/10-Showing-a-default-text-when-no-users-are-assigned.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Showing a default text when no users are assigned</em></p>
<h3 id="heading-the-compound-component-pattern-method">The Compound Component Pattern Method</h3>
<p>Now, for the main event. Start by creating a context which wraps the entire component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> UserAssignContext = createContext();
</code></pre>
<p>Then we gather all the necessary data and functions that our dropdown and its components will need. This includes things like the list of assigned users, a function to update that list, and whether the dropdown is currently open or not.</p>
<p>After which, you provide those values to all its children component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> UserAssignDropdown = <span class="hljs-function">(<span class="hljs-params">{
  children,
  assignedList,
  setAssignedList,
  users,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> UserAssignDropdownRef = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [isDropdownOpen, setIsDropdownOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignContext.Provider</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">assignedList</span>,
        <span class="hljs-attr">users</span>,
        <span class="hljs-attr">UserAssignDropdownRef</span>,
        <span class="hljs-attr">isDropdownOpen</span>,
        <span class="hljs-attr">setIsDropdownOpen</span>,
        <span class="hljs-attr">setAssignedList</span>,
      }}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{UserAssignDropdownRef}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">UserAssignContext.Provider</span>&gt;</span></span>
  );
};
</code></pre>
<p>With our context set up, it's time to craft the individual components that will make up our dropdown. Each component will interact with the context to access and manipulate the necessary data and functions.</p>
<p>Start by copying each style from the components we just built.</p>
<h4 id="heading-the-header-component">The Header Component</h4>
<p>This component remains the same.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Header = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 mb-2 text-sm"</span>&gt;</span>Assign task to:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>;
};
</code></pre>
<h4 id="heading-the-close-component">The Close Component</h4>
<p>This component obtains the function for toggling the dropdown from the context.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Close = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { setIsDropdownOpen } = useContext(UserAssignContext);
  <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">"absolute top-0 right-0 flex items-center justify-center -translate-y-full gap-2 bg-[#C0392B] px-2 py-1 rounded-t"</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
        e.stopPropagation();
        setIsDropdownOpen(false);
      }}&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">FaXmark</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</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">div</span>&gt;</span></span>
  );
};
</code></pre>
<h4 id="heading-the-assigned-list-component">The Assigned List Component</h4>
<p>This component displays the list of assigned users, as well as removing users from the list.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> AssignedList = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { assignedList, setAssignedList } = useContext(UserAssignContext);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRemove</span>(<span class="hljs-params">id</span>) </span>{
    setAssignedList(<span class="hljs-function">(<span class="hljs-params">assignedList</span>) =&gt;</span>
      assignedList.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.id !== id)
    );
  }

  <span class="hljs-keyword">if</span> (assignedList.length === <span class="hljs-number">0</span>)
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 p-2 shadow-sm bg-[#828fa318] rounded"</span>&gt;</span>
        No users assigned to the task yet.
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</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">"mt-4 p-2 shadow-sm bg-[#828fa318] rounded"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2 my-3 font-bold"</span>&gt;</span>Assigned list:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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-wrap gap-4 "</span>&gt;</span>
        {assignedList?.map((user, index) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-1 w-[47.5%] p-2 hover:bg-[#20212c] rounded transition-all duration-200"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleRemove(user.id)}&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{index + 1}.<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span>
              <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span>
              <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`}
            /&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-auto cursor-pointer p-1 hover:bg-[#2b2c37] rounded-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">FaXmark</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">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>
<h4 id="heading-the-item-component">The Item Component</h4>
<p>This component represents each user and the functionality of adding and removing users from the assigned list.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Item = <span class="hljs-function">(<span class="hljs-params">{ user }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { assignedList, setAssignedList } = useContext(UserAssignContext);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleAssign</span>(<span class="hljs-params">user</span>) </span>{
    setAssignedList(<span class="hljs-function">(<span class="hljs-params">prevList</span>) =&gt;</span> {
      <span class="hljs-comment">// Check if the user already exists in the list</span>
      <span class="hljs-keyword">if</span> (prevList.includes(user)) {
        <span class="hljs-comment">// If user exists, remove it from the list</span>
        <span class="hljs-keyword">const</span> updatedList = prevList.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item !== user);
        <span class="hljs-keyword">return</span> updatedList;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If user doesn't exist, add it to the list</span>
        <span class="hljs-keyword">return</span> [...prevList, user];
      }
    });
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span>
      <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">flex</span> <span class="hljs-attr">items-center</span> <span class="hljs-attr">gap-2</span> <span class="hljs-attr">p-4</span> <span class="hljs-attr">hover:bg-</span>[#<span class="hljs-attr">2b2c37</span>] <span class="hljs-attr">rounded</span> <span class="hljs-attr">transition-all</span> <span class="hljs-attr">duration-200</span> `}
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleAssign(user)}&gt;
      {assignedList.includes(user) &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">FiCheck</span> /&gt;</span>}

      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-6 h-6 "</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{user.imgUrl}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">user.name</span>} <span class="hljs-attr">image</span>`} /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
  );
};
</code></pre>
<h4 id="heading-the-button-component">The Button Component</h4>
<p>This component controls displaying the <code>List</code> component (floating dropdown).</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Button = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { setIsDropdownOpen } = useContext(UserAssignContext);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"  px-4 py-2 flex items-center justify-between w-full rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsDropdownOpen(true)}&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">FiChevronDown</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#635FC7"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</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">UserAssignDropdown.List</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
};
</code></pre>
<p>To combine this component into a singular compound component (compound component), you assign each component to the parent, like so;</p>
<pre><code class="lang-jsx">UserAssignDropdown.List = ListContainer;
UserAssignDropdown.Item = Item;
UserAssignDropdown.Header = Header;
UserAssignDropdown.Button = Button;
UserAssignDropdown.AssignedList = AssignedList;
UserAssignDropdown.Close = Close;
</code></pre>
<p>Next, import your compound component in your <code>App</code> component as a wrapper component and pass in the appropriate states.</p>
<pre><code class="lang-jsx"><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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [assignedList, setAssignedList] = useState([]);

  <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-[#2b2c37] h-[100dvh] text-white flex  p-20 gap-4 items-center flex-col"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" w-[400px] "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl "</span>&gt;</span>Compound Component Pattern<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown</span>
          <span class="hljs-attr">assignedList</span>=<span class="hljs-string">{assignedList}</span>
          <span class="hljs-attr">setAssignedList</span>=<span class="hljs-string">{setAssignedList}</span>
          <span class="hljs-attr">users</span>=<span class="hljs-string">{usersArray}</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">UserAssignDropdown</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>Then in the wrapper, render the appropriate children.</p>
<pre><code class="lang-jsx"><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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [assignedList, setAssignedList] = useState([]);

  <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-[#2b2c37] h-[100dvh] text-white flex  p-20 gap-4 items-center flex-col"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" w-[400px] "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl "</span>&gt;</span>Compound Component Pattern<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown</span>
          <span class="hljs-attr">assignedList</span>=<span class="hljs-string">{assignedList}</span>
          <span class="hljs-attr">setAssignedList</span>=<span class="hljs-string">{setAssignedList}</span>
          <span class="hljs-attr">users</span>=<span class="hljs-string">{usersArray}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Header</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Button</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.AssignedList</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">UserAssignDropdown</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>Finally, use the custom hook we created earlier to close the dropdown when you click outside the component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> UserAssignContext = createContext();
<span class="hljs-keyword">const</span> UserAssignDropdown = <span class="hljs-function">(<span class="hljs-params">{
  children,
  assignedList,
  setAssignedList,
  users,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> UserAssignDropdownRef = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [isDropdownOpen, setIsDropdownOpen] = useState(<span class="hljs-literal">false</span>);

  useClickOutside(UserAssignDropdownRef, <span class="hljs-function">() =&gt;</span> {
    setIsDropdownOpen(<span class="hljs-literal">false</span>);
  });

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignContext.Provider</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">assignedList</span>,
        <span class="hljs-attr">users</span>,
        <span class="hljs-attr">UserAssignDropdownRef</span>,
        <span class="hljs-attr">isDropdownOpen</span>,
        <span class="hljs-attr">setIsDropdownOpen</span>,
        <span class="hljs-attr">setAssignedList</span>,
      }}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{UserAssignDropdownRef}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">UserAssignContext.Provider</span>&gt;</span></span>
  );
};
</code></pre>
<p>And with that, your component works the same!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/11-replicating-the-same-funtionality-with-the-compound-component-pattern.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Replicating the same functionality with the compound component pattern</em></p>
<p>But why stop here?</p>
<p>With this pattern, changing the look of the component is as easy as changing the order in which you render them in their parent. For instance, if you wanted the button first, you simply change the order in the parent wrapper.</p>
<pre><code class="lang-jsx">&lt;UserAssignDropdown
  assignedList={assignedList}
  setAssignedList={setAssignedList}
  users={usersArray}
&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Button</span> /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Header</span> /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.AssignedList</span> /&gt;</span></span>
&lt;/UserAssignDropdown&gt;
</code></pre>
<p>And the UI responds accordingly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/04-order-of-rendering-in-compound-component-changed.png" alt="Image" width="600" height="400" loading="lazy">
<em>Order of rendering in compound component changed</em></p>
<p>This component is also flexible enough to change the layout of elements via props.</p>
<p>Just by passing style props via a parent:</p>
<pre><code class="lang-jsx">&lt;UserAssignDropdown
  assignedList={assignedList}
  setAssignedList={setAssignedList}
  users={usersArray}
&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Header</span> /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Button</span>
    <span class="hljs-attr">listStyles</span>=<span class="hljs-string">{</span>"!<span class="hljs-attr">-left-5</span> !<span class="hljs-attr">-translate-x-full</span> <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">605e80</span>] <span class="hljs-attr">text-white</span> <span class="hljs-attr">border</span>"}
  /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.AssignedList</span> /&gt;</span></span>
&lt;/UserAssignDropdown&gt;
</code></pre>
<p>And receiving those props in the child:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Button = <span class="hljs-function">(<span class="hljs-params">{ listStyles }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { setIsDropdownOpen, UserAssignDropdownRef } =
    useContext(UserAssignContext);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"  px-4 py-2 flex items-center justify-between w-full rounded border border-[#828FA340] hover:border-primary cursor-pointer relative "</span>
      <span class="hljs-attr">ref</span>=<span class="hljs-string">{UserAssignDropdownRef}</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsDropdownOpen(true)}&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">FiChevronDown</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#635FC7"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</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">UserAssignDropdown.List</span> <span class="hljs-attr">listStyles</span>=<span class="hljs-string">{listStyles}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">const</span> ListContainer = <span class="hljs-function">(<span class="hljs-params">{ listStyles }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { users, isDropdownOpen } = useContext(UserAssignContext);

  <span class="hljs-keyword">return</span> (
    isDropdownOpen &amp;&amp; (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">absolute</span> <span class="hljs-attr">bottom-full</span> <span class="hljs-attr">translate-x-9</span>  <span class="hljs-attr">left-full</span> <span class="hljs-attr">translate-y-full</span> <span class="hljs-attr">rounded</span> <span class="hljs-attr">bg-</span>[#<span class="hljs-attr">20212c</span>] <span class="hljs-attr">w-max</span> ${<span class="hljs-attr">listStyles</span>}`}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Close</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 p-2"</span>&gt;</span>
          {users?.map((user, index) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">UserAssignDropdown.Item</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</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">ul</span>&gt;</span></span>
    )
  );
};
</code></pre>
<p>You can easily change the look of the component.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/12-Using-props-to-customize-the-compound-component.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Using props to customize the compound component</em></p>
<h2 id="heading-comparison-between-the-regular-and-the-compound-component-method">Comparison between the Regular and the Compound Component method</h2>
<p>Alright, let's take a step back and compare the two approaches we've just explored.</p>
<h3 id="heading-simplicity-and-organization">Simplicity and Organization</h3>
<ul>
<li><strong>Regular Method</strong>: Picture it like baking a cake all in one big bowl. With the regular method, we can create a single component responsible for everything in the dropdown – the button, the list, and all the ingredients. It's like having a big recipe card with all the steps jumbled together. It gets the job done, but it can be a bit messy and hard to follow, especially when you're trying to tweak just one part of the recipe.</li>
<li><strong>Compound Component Method</strong>: Now imagine we've got different bowls for each ingredient, a separate one for flour, another for sugar, and so on. That's the compound component pattern. Each part of the dropdown has its own space to shine. It's like organizing your kitchen – everything has its place. This makes it easier to understand and modify. Need to change the flour? You know just where to look.</li>
</ul>
<h3 id="heading-flexibility-and-customization">Flexibility and Customization</h3>
<ul>
<li><strong>Regular Method</strong>: With our single-bowl approach, making changes to specific parts of the dropdown can be a bit like trying to swap out ingredients in that big cake mix. Sure, you can do it, but it's not always easy. Want a different flavor of cake? You might need to dig through the whole bowl to find where to add it.</li>
<li><strong>Compound Component Method</strong>: With the compound component pattern, it's like having separate containers for each flavor. Need to add chocolate chips? Just grab the chocolate container and sprinkle away. Each component has its job, making it simpler to customize. Want to change the color of the button? No problem, it's right there in its container.</li>
</ul>
<h3 id="heading-reuse-and-maintenance">Reuse and Maintenance</h3>
<ul>
<li><strong>Regular Method</strong>: When your recipe is all mixed up in one bowl, it can be hard to reuse parts of it for another dish. Plus, as your kitchen gets busier, it's easy for things to get messy and hard to keep track of. You might find yourself rewriting the recipe every time you want to make something new.</li>
<li><strong>Compound Component Method</strong>: With the compound component pattern, it's like having a set of reusable tools in your kitchen. Need to make a different kind of cake? Just grab the tools you need and get baking. Each component is like a specialized gadget – easy to reuse and maintain. And when your kitchen is organized, it's a breeze to whip up something new.</li>
</ul>
<h3 id="heading-additional-information">Additional Information</h3>
<p>Here are links to all the resources you may need from this article.</p>
<ul>
<li><a target="_blank" href="https://github.com/Daiveedjay/React-Dropdown-Component/tree/Starter">Starter files</a></li>
<li><a target="_blank" href="https://github.com/Daiveedjay/React-Dropdown-Component/tree/Regular-react-pattern">Regular Function Pattern</a></li>
<li><a target="_blank" href="https://github.com/Daiveedjay/React-Dropdown-Component/tree/Compound-component-pattern">Compound Component Pattern</a></li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In the end, both methods have their place in the kitchen – er, I mean, in your code. The regular method is like your trusty old mixing bowl – reliable and familiar, but maybe not the most efficient for every recipe. </p>
<p>The compound component Pattern is like a well-organized kitchen, with everything in its place and ready to go. It might take a bit more setup, but it can make your life a whole lot easier in the long run. So, depending on what you're cooking up, choose the method that suits your taste– and happy coding! 🍰🎨  </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 / 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 a Responsive HTML Email Template ]]>
                </title>
                <description>
                    <![CDATA[ In this beginner-friendly guide, you'll learn how to create a responsive email template. You'll follow step-by-step instructions with code snippets to design an email template that looks great on any device.  This project is perfect for newcomers eag... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-responsive-html-email-template/</link>
                <guid isPermaLink="false">66ba1be84067550ef786869e</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Fanny Nyayic ]]>
                </dc:creator>
                <pubDate>Mon, 15 Apr 2024 23:11:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/create-html-email-template-from-scratch.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this beginner-friendly guide, you'll learn how to create a responsive email template. You'll follow step-by-step instructions with code snippets to design an email template that looks great on any device. </p>
<p>This project is perfect for newcomers eager to master email design basics!</p>
<h2 id="heading-step-1-set-up-the-basic-structure">Step 1: Set Up the Basic Structure</h2>
<p>To build an email template, you can start with a basic HTML structure. This includes a <code>DOCTYPE</code> declaration for emails, defining the <code>head</code> and <code>body</code> sections, and using meta tags in the <code>head</code> section to ensure proper mobile rendering and zooming.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Responsive Email Template<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Email content goes here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-step-2-create-the-email-structure">Step 2: Create the Email Structure</h2>
<p>Use tables to create the basic structure of your email. This will ensure compatibility across different email clients.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">border</span>=<span class="hljs-string">"0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"600"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">border</span>=<span class="hljs-string">"0"</span>&gt;</span>
                <span class="hljs-comment">&lt;!-- Email content goes here --&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<h2 id="heading-step-3-add-content-and-inline-styles">Step 3: Add Content and Inline Styles</h2>
<p>Email clients vary in how they render CSS, so it's safer to use inline styles. Here's an example of a simple email body:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"font-family: 'Poppins', Arial, sans-serif"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span> <span class="hljs-attr">border</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 20px;"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"600"</span> <span class="hljs-attr">border</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"border-collapse: collapse; border: 1px solid #cccccc;"</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- Header --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"header"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background-color: #345C72; padding: 40px; text-align: center; color: white; font-size: 24px;"</span>&gt;</span>
                        Responsive Email Template
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>

                    <span class="hljs-comment">&lt;!-- Body --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"body"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 40px; text-align: left; font-size: 16px; line-height: 1.6;"</span>&gt;</span>
                        Hello, All! <span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
                        Lorem odio soluta quae dolores sapiente voluptatibus recusandae aliquam fugit ipsam.
                        <span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
                            Lorem ipsum dolor sit amet consectetur adipisicing elit. Veniam corporis sint eum nemo animi velit exercitationem impedit. Incidunt, officia facilis  atque? Ipsam voluptas fugiat distinctio blanditiis veritatis.            
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>

                    <span class="hljs-comment">&lt;!-- Call to action Button --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 0px 40px 0px 40px; text-align: center;"</span>&gt;</span>
                            <span class="hljs-comment">&lt;!-- CTA Button --&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"margin: auto;"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background-color: #345C72; padding: 10px 20px; border-radius: 5px;"</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.yourwebsite.com"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: #ffffff; text-decoration: none; font-weight: bold;"</span>&gt;</span>Book a Free Consulatation<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"body"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 40px; text-align: left; font-size: 16px; line-height: 1.6;"</span>&gt;</span>
                            Lorem ipsum dolor sit amet consectetur adipisicing elit. Veniam corporis sint eum nemo animi velit exercitationem impedit.             
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- Footer --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"footer"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background-color: #333333; padding: 40px; text-align: center; color: white; font-size: 14px;"</span>&gt;</span>
                        Copyright <span class="hljs-symbol">&amp;copy;</span> 2024 | Your brand name
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<p>Here's a breakdown of the main elements and their functions:</p>
<h3 id="heading-body-tag-and-font-setup">Body Tag and Font Setup</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"font-family: 'Poppins', Arial, sans-serif"</span>&gt;</span>
</code></pre>
<p>This sets the default font for the email to 'Poppins', with fallbacks to Arial and sans-serif if 'Poppins' isn't available.</p>
<h3 id="heading-table-structure">Table Structure</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span> <span class="hljs-attr">border</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span>&gt;</span>
</code></pre>
<p>This is the outermost table that takes up 100% of the email's width. The <code>border</code>, <code>cellspacing</code>, and <code>cellpadding</code> are set to 0 to remove default styling and spacing.</p>
<p>Nested inside this table is another <code>&lt;table class="content"&gt;</code> with a fixed width of 600px, centered by its parent with <code>td align="center"</code>. </p>
<p>This inner table includes a border and specific styling, defining it as the main content area.</p>
<h3 id="heading-the-header-section">The Header Section</h3>
<p>The header is styled with a dark blue background using inline CSS (#345C72), white text color, and larger text size (24px). It’s designed to grab attention right at the beginning of the email. </p>
<p><strong>Note</strong><em><strong>:</strong></em> You can customize this section with your brand name or logo instead.</p>
<h3 id="heading-the-body-content">The Body Content</h3>
<p>The body section contains the main message of the email, set in a font size of 16px and line height of 1.6 for better readability. The content is aligned to the left, and the use of <code>&lt;br&gt;</code> tags helps in spacing out the lines.</p>
<h3 id="heading-call-to-action-cta-button">Call to Action (CTA) Button</h3>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Call to action Button --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"padding: 0px 40px 0px 40px; text-align: center;"</span>&gt;</span>
                            <span class="hljs-comment">&lt;!-- CTA Button --&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"margin: auto;"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background-color: #345C72; padding: 10px 20px; border-radius: 5px;"</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.yourwebsite.com"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: #ffffff; text-decoration: none; font-weight: bold;"</span>&gt;</span>Book a Free Consulatation<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
</code></pre>
<p>The CTA button here is designed to stand out with a background color that matches the header, rounded corners (<code>border-radius: 5px</code>), and bold, white text. </p>
<p>The <code>&lt;a&gt;</code> tag within the button is styled to remove the default underline (<code>text-decoration: none</code>) and is linked to a webpage where recipients can "Book a Free Consultation."</p>
<h3 id="heading-footer">Footer</h3>
<p>The footer repeats the styling approach of the header but uses a darker background (#333333) and smaller font size (14px). It can contain copyright information and links or other contact details.</p>
<h3 id="heading-illustration">Illustration</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/email-template-illustration.png" alt="Image" width="600" height="400" loading="lazy">
<em>illustration of the different parts of the template: header, CTA button, and footer</em></p>
<h2 id="heading-step-4-make-it-responsive">Step 4: Make It Responsive</h2>
<p>To ensure the email looks good on mobile devices, you can use CSS media queries. While most of the styling are inline, for responsive behavior, you'll need to add a <code>&lt;style&gt;</code> block in the <code>head</code>. </p>
<p>Media queries adjust styles based on the device's width.</p>
<pre><code class="lang-css">&lt;<span class="hljs-selector-tag">style</span>&gt;
  <span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">600px</span>) {
    <span class="hljs-selector-class">.content</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span> <span class="hljs-meta">!important</span>;
        <span class="hljs-attribute">display</span>: block <span class="hljs-meta">!important</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-meta">!important</span>;
    }
    <span class="hljs-selector-class">.header</span>, <span class="hljs-selector-class">.body</span>, <span class="hljs-selector-class">.footer</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span> <span class="hljs-meta">!important</span>;
    }
  }
&lt;/<span class="hljs-selector-tag">style</span>&gt;
</code></pre>
<p>Here’s a breakdown of this specific CSS snippet:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">600px</span>) { ... }
</code></pre>
<p>This media query targets screens with a maximum width of 600 pixels. It applies the following styles only when the email is viewed on devices with a screen width of 600px or less, which typically includes smartphones and some smaller tablets.</p>
<p>Styles within the media query classes:</p>
<h3 id="heading-content">.content</h3>
<ul>
<li><code>width_: 100% !important;_</code> : This style changes the width of the <code>.content</code> table to use the full width of the screen instead of the 600px specified in the HTML. The <code>!important</code> rule is used to override any other conflicting styles.</li>
<li><code>display: block !important;</code>: Although <code>&lt;table&gt;</code> elements are naturally block-level elements, setting <code>display: block</code> explicitly can help in some email clients to ensure the element behaves as expected.</li>
<li><code>padding: 10px !important;</code>: Adds padding around the content within the <code>.content</code> table, reducing it from the original 40px in the HTML to make better use of the reduced screen space on smaller devices.</li>
</ul>
<h3 id="heading-header-body-footer">.header, .body, .footer</h3>
<ul>
<li><code>padding: 20px !important;</code>: This style uniformly sets the padding of the <em>header, body, and footer</em> sections to <em>20px</em> on all sides, optimizing the spacing for smaller screens. It overrides the various padding settings defined in the HTML, which include larger values in some cases.</li>
</ul>
<p>In the context of email design, the use of <strong>!important</strong> is fairly common to ensure that the styles are applied as intended, overriding both default styles and other potentially conflicting styles that might be applied by the email client itself. </p>
<h2 id="heading-step-5-test-across-email-clients">Step 5: Test Across Email Clients</h2>
<p>It's crucial to test your email template across different email clients (like Gmail, Outlook, and Apple Mail) and devices to ensure compatibility and responsiveness. Tools like Litmus or Email on Acid can help with this.</p>
<h2 id="heading-step-6-adding-google-font">Step 6: Adding Google Font</h2>
<p>Incorporating Google Fonts into an HTML email template can improve its visual appeal significantly. </p>
<p>However, it's important to note that not all email clients support web fonts. Some, like Apple Mail, do support Google Fonts, but others like Gmail do not. To ensure your email looks great to all recipients, always provide a fallback font.</p>
<p>Here's how you can add a Google Font to your email template, along with a fallback option for clients that don't support it:</p>
<h3 id="heading-choose-your-google-font">Choose Your Google Font</h3>
<p>First, visit the <a target="_blank" href="https://fonts.google.com/">Google Fonts</a> website and choose a font. For this example, let's use <em>"Poppins".</em></p>
<h3 id="heading-add-the-font-link-to-your-email-head">Add the Font Link to Your Email Head</h3>
<p>Include the link to the Google Font in the <code>&lt;head&gt;</code> of your HTML document. Since this might not be supported in all email clients, ensure you have a suitable fallback font in your styles.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span> <span class="hljs-attr">crossorigin</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
</code></pre>
<h3 id="heading-apply-the-font-in-your-styles">Apply the Font in Your Styles</h3>
<p>Use inline CSS to apply the Google Font to your HTML elements, and always include a generic fallback font. In email templates, it's safer to apply styles inline due to varying support for <code>&lt;style&gt;</code> tags across email clients. </p>
<p>Here's how to apply the font to the <code>body</code> of your email and include a fallback:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"font-family: 'Poppins', Arial, sans-serif;"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cellpadding</span>=<span class="hljs-string">"0"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Email content --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<h2 id="heading-what-we-created">What We Created</h2>
<p>Below is a screenshot of the email template we designed. It features a professional layout with a header containing a logo placeholder, a main body section for your message, and a dark footer with contact and subscription management links. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/email-template.png" alt="Image" width="600" height="400" loading="lazy">
<em>screenshot of the email template</em></p>
<h2 id="heading-additional-tips">Additional Tips:</h2>
<ul>
<li>Keep your CSS inline as much as possible, because many email clients do not support <code>&lt;style&gt;</code> tags.</li>
<li>Use web-safe fonts to ensure that your text appears correctly in all email clients.</li>
<li>Always provide a plain text version of your email for clients that don't support HTML, or have HTML disabled.</li>
</ul>
<p>I hope this guide provides you with a basic framework for creating a responsive email template. As you become more comfortable with email design, you can experiment with more complex layouts and styles.  </p>
<p><em><strong>Happy Coding!</strong></em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Send Emails With Nodemailer in NestJS ]]>
                </title>
                <description>
                    <![CDATA[ By Okure U. Edet While learning Nestjs, I wanted to be able to send test emails with Nodemailer but I had difficulty doing this in the context of a NestJS application. I searched the internet for a solution and after much research, I found one. It tu... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-nodemailer-in-nestjs/</link>
                <guid isPermaLink="false">66d4608df855545810e934b3</guid>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ nestjs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 10 Apr 2024 11:52:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/joanna-kosinska-uGcDWKN91Fs-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Okure U. Edet</p>
<p>While learning Nestjs, I wanted to be able to send test emails with Nodemailer but I had difficulty doing this in the context of a NestJS application. I searched the internet for a solution and after much research, I found one. It turned out to be really simple.</p>
<p>In this article, I will share my solution so you can use it in your NestJS projects.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-set-up-a-nestjs-project">How to Set Up a NestJS Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-nodemailer-in-your-app">How to Configure Nodemailer in Your App</a></li>
<li><a class="post-section-overview" href="#heading-how-to-send-emails-with-nodemailer">How to Send emails with Nodemailer</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h3 id="heading-how-to-set-up-a-nestjs-project">How to Set Up a NestJS Project</h3>
<p>Ideally, when a user clicks on a forget password route, a link should be sent to the user's email, and through that link, the user should be able to reset their password. This article will demonstrate a test case scenario of how this works using Nodemailer.</p>
<p>Open your favorite IDE or navigate to the terminal and paste the following command:</p>
<pre><code>$ npm i -g @nestjs/cli
$ nest <span class="hljs-keyword">new</span> nodemailer-app
</code></pre><p>The above commands generates a new project called  <code>nodemailer-app</code>. </p>
<p>After doing this, navigate to your project folder and install the Nodemailer packages, <code>npm i --save @nestjs-modules/mailer nodemailer</code> and types, <code>npm i --save-dev @types/nodemailer</code>.</p>
<h3 id="heading-how-to-configure-nodemailer-in-your-app">How to Configure Nodemailer in Your App</h3>
<p>Before moving on, make sure you have an account on <a target="_blank" href="https://mailtrap.io/">mailtrap.io</a>. If you do, just login and navigate to <strong>Email Testing</strong>. Create your own inbox if you don't have one. Navigate to the inbox and you should see your credentials which will be used to configure Nodemailer in your application.</p>
<p>In your project directory, go to the app module file and configure the package:</p>
<pre><code><span class="hljs-keyword">import</span> { Module } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { AppController } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.controller'</span>;
<span class="hljs-keyword">import</span> { AppService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.service'</span>;
<span class="hljs-keyword">import</span> { AuthModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./auth/auth.module'</span>;
<span class="hljs-keyword">import</span> { MailerModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs-modules/mailer'</span>;

@Module({
  <span class="hljs-attr">imports</span>: [
    AuthModule,
    MailerModule.forRoot({
      <span class="hljs-attr">transport</span>: {
        <span class="hljs-attr">host</span>: process.env.EMAIL_HOST,
        <span class="hljs-attr">auth</span>: {
          <span class="hljs-attr">user</span>: process.env.EMAIL_USERNAME,
          <span class="hljs-attr">pass</span>: process.env.EMAIL_PASSWORD,
        },
      },
    }),
  ],
  <span class="hljs-attr">controllers</span>: [AppController],
  <span class="hljs-attr">providers</span>: [AppService],
})
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppModule</span> </span>{}
</code></pre><p>In the above code, you imported the <code>MailerModule</code> from <code>@nestjs-modules/mailer</code>. Then you called a <code>forRoot()</code> method on it. Inside the <code>forRoot()</code> method, you specified a transport property which contains the host and auth properties.</p>
<p>Do not forget to get the host, port, username and password from your credentials in your inbox on <a target="_blank" href="https://mailtrap.io/">mailtrap.io</a>.</p>
<p>You can create a <code>.env</code> file which would house all your credential details. It is advisable to do so. To be able to load the appropriate <code>.env</code> file in NestJS, install this:</p>
<pre><code>$ npm i --save @nestjs/config
</code></pre><p>Then in your <code>app.module.ts</code> file, import a <code>ConfigModule</code>:</p>
<pre><code><span class="hljs-keyword">import</span> { ConfigModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/config'</span>;
</code></pre><p>Still in your <code>app.module.ts</code></p>
<pre><code><span class="hljs-comment">// include the config module in your imports array</span>

@Module({
  <span class="hljs-attr">imports</span>: [
    ConfigModule.forRoot({ <span class="hljs-attr">envFilePath</span>: <span class="hljs-string">'.env'</span>, <span class="hljs-attr">isGlobal</span>: <span class="hljs-literal">true</span> }),
  ],
  <span class="hljs-attr">controllers</span>: [AppController],
  <span class="hljs-attr">providers</span>: [AppService],
})
</code></pre><h3 id="heading-how-to-send-emails-with-nodemailer">How to Send Emails with NodeMailer</h3>
<p>After configuring Nodemailer, it is time to send emails with it.</p>
<p>In your <code>app.service.ts</code> file, paste the following code:</p>
<pre><code><span class="hljs-keyword">import</span> { MailerService } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs-modules/mailer'</span>;
<span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;

@Injectable()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppService</span> </span>{
  <span class="hljs-keyword">constructor</span>(private readonly mailService: MailerService) {}

  sendMail() {
    <span class="hljs-keyword">const</span> message = <span class="hljs-string">`Forgot your password? If you didn't forget your password, please ignore this email!`</span>;

    <span class="hljs-built_in">this</span>.mailService.sendMail({
      <span class="hljs-attr">from</span>: <span class="hljs-string">'Kingsley Okure &lt;kingsleyokgeorge@gmail.com&gt;'</span>,
      <span class="hljs-attr">to</span>: <span class="hljs-string">'joanna@gmail.com'</span>,
      <span class="hljs-attr">subject</span>: <span class="hljs-string">`How to Send Emails with Nodemailer`</span>,
      <span class="hljs-attr">text</span>: message,
    });
  }
}
</code></pre><p>In the <code>app.service.ts</code> file, the <code>MailerService</code> is injected and then used in the class to send the email. Inside the class, the <code>MailerService</code> has a <code>sendMail</code> function which takes in an object as a parameter. The object contains a <code>from</code>, <code>to</code>, <code>subject</code> and <code>text</code> property.</p>
<p>Once you have done this, in the <code>app.controller.ts</code> file, paste the following code:</p>
<pre><code><span class="hljs-keyword">import</span> { Controller, Get, Res } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { AppService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.service'</span>;

@Controller()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppController</span> </span>{
  <span class="hljs-keyword">constructor</span>(private readonly appService: AppService) {}

  @Get()
  sendMailer(@Res() response: any) {
    <span class="hljs-keyword">const</span> mail = <span class="hljs-built_in">this</span>.appService.sendMail();

    <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">200</span>).json({
      <span class="hljs-attr">message</span>: <span class="hljs-string">'success'</span>,
      mail,
    });
  }
}
</code></pre><p>All that is done in the controller is to create a <code>Get</code> request which will call the <code>sendMail</code> function you have created in the service.</p>
<p>Ideally, in a real world application, all this will be done in a forgot password function. And an email will be sent to the user once they click on a forgot password route.</p>
<p>To test this little setup, open your Postman and go to localhost:3000 and hit send.</p>
<p>Then go to your <a target="_blank" href="https://mailtrap.io/inboxes/2445842/messages">mailtrap.io</a> inbox and see your message.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this article, you have learned how to send emails with Nodemailer, a software designed to help developers send emails to multiple people at once. </p>
<p>You have also learned how to configure and set it up in the context of a NestJs application.</p>
<p>If you want to connect with me, you can follow me on <a target="_blank" href="https://twitter.com/itzz_okure">Twitter</a> or on <a target="_blank" href="https://www.linkedin.com/in/okure/">Linkedin</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Marquee Component with React ]]>
                </title>
                <description>
                    <![CDATA[ When you think of marquees, you often envision the vibrant lights and spinning displays at amusement parks, bringing back fond memories of your childhood. Similarly in web applications, marquees inject a lively visual sense by effortlessly grabbing a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-marquee-component-with-react/</link>
                <guid isPermaLink="false">66bb89057a6500a14ba5b767</guid>
                
                    <category>
                        <![CDATA[ components ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Tue, 09 Apr 2024 12:41:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Group-297.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you think of marquees, you often envision the vibrant lights and spinning displays at amusement parks, bringing back fond memories of your childhood.</p>
<p>Similarly in web applications, marquees inject a lively visual sense by effortlessly grabbing attention and infusing your online projects with dynamism.</p>
<p>Join me as we explore how to build an engaging marquee component in React.<br>This step-by-step guide targets everyone regardless of skill level, we aim to make your experience with React both enjoyable and useful.</p>
<h2 id="heading-what-well-cover"><strong>What We'll Cover:</strong></h2>
<ol>
<li><a class="post-section-overview" href="#heading-understanding-marquee-components">Understanding Marquee Components</a></li>
<li><a class="post-section-overview" href="#heading-benefits-of-marquees">Benefits of Marquees</a></li>
<li><a class="post-section-overview" href="#heading-planning-and-designing-the-marquee-component">Planning and Designing the Marquee Component</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-marquee-component">How to Implement the Marquee Component</a></li>
<li><a class="post-section-overview" href="#how-to-enhance-enhance-the-marquee-component">How to Enhance the Marquee Component</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/p/0148db11-7178-4632-b727-2321d7e96b01/best-practices-and-tips-for-marquee-component-development">Best Practices and Tips for Marquee Component Development</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Fundamentals of HTML and CSS</li>
<li>Fundamentals of ES6 JavaScript and React</li>
</ul>
<h2 id="heading-understanding-marquee-components">Understanding Marquee Components</h2>
<p>A marquee represents a continuous section of text or visual content (such as pictures) that scrolls automatically in a horizontal direction.</p>
<p>Although the official <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee">HTML Marquee element</a> is obsolete and its use is heavily discouraged, the concept of scrolling, endless elements to add zest to a webpage is still very much in use, and can be found on numerous modern websites.</p>
<p>This effect is realized through <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations">CSS animations</a>, offering a more efficient, fluid, and lightweight animation outcome.</p>
<p>A visual example of a marquee component from <a target="_blank" href="https://webflow.com/made-in-webflow/website/ujjo-Rebuild">Webflow</a> is shown below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/00-Example-marquee.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Webflow Example</em></p>
<h2 id="heading-benefits-of-marquees">Benefits of Marquees</h2>
<p>They have a bunch of usefulness such as:</p>
<ul>
<li><strong>Attention-grabbing</strong>: Marquees are excellent for drawing attention to specific content on a webpage. Whether it's a special offer, announcement, or featured content, a moving marquee naturally catches the eye.</li>
<li><strong>Visual Appeal</strong>: Adding a touch of motion to your website enhances its visual appeal. Marquees can bring life to a page, making it more dynamic and engaging for users.</li>
<li><strong>Highlighting Important Information</strong>: When you want to emphasize critical information like breaking news, upcoming events, or urgent messages, a marquee is an effective way to ensure that users don't miss out.</li>
<li><strong>Event Promotion</strong>: They are particularly useful for promoting events or time-sensitive activities. Their scrolling nature allows you to display event details, dates, and highlights space-efficiently.</li>
<li><strong>Ticker-Style Updates</strong>: For displaying real-time updates, such as stock prices, news headlines, or social media feeds, marquees provide a ticker-style format that keeps information continuously flowing for users.</li>
<li><strong>Interactive Banners</strong>: They can serve as interactive banners, allowing users to click on specific items as they scroll by. This can be a creative way to lead users to different sections or pages of your website.</li>
<li><strong>Dynamic Product Showcases</strong>: E-commerce websites can benefit from marquees by showcasing new products or featured items in a visually engaging way, encouraging users to explore the offerings.</li>
<li><strong>Call-to-Action Emphasis</strong>: If you have specific call-to-action messages, using them can give prominence and ensure that they don't go unnoticed.</li>
<li><strong>Breaking Monotony</strong>: In long pages or static content, a well-designed marquee can break the monotony and add an element of surprise, making the user experience more interesting.</li>
<li><strong>Versatility</strong>: They are versatile and can be customized to suit various styles and themes, making them a flexible tool for web designers seeking to create unique and memorable user interfaces.</li>
</ul>
<h2 id="heading-planning-and-designing-the-marquee-component">Planning and Designing the Marquee Component</h2>
<p>Before you start coding, it's important to plan and design your component and consider factors such as:</p>
<ul>
<li><strong>Define Content</strong>: Clearly outline the content you want to display in the component. This could include text, images, or a combination of both.</li>
<li><strong>Scroll Speed</strong>: Determine the desired scrolling speed to be used. Consider the optimal pace for readability and visual appeal.</li>
<li><strong>Visual Design</strong>: Sketch or visualize how you want it to look. Decide on colors, fonts, and any additional styling to align with your overall design scheme.</li>
<li><strong>Behavior at Scroll End</strong>: Consider its behavior when it reaches the end of its scroll position. Decide whether it should loop continuously, bounce back and forth, or have a specific end-state.</li>
<li><strong>User Interaction</strong>: If applicable, plan for any user interaction. This could include pausing on hover or allowing users to click on items within the marquee.</li>
<li><strong>Responsive Design</strong>: Ensure that your component is designed to be responsive, adapting seamlessly to different screen sizes and devices.</li>
<li><strong>Testing Considerations</strong>: Anticipate potential challenges or adjustments needed during the testing phase. Plan for how it will behave on various browsers and devices.</li>
<li><strong>Accessibility</strong>: Keep accessibility in mind, ensuring that users with different disabilities can still access and understand the content within it.</li>
</ul>
<h2 id="heading-how-to-implement-the-marquee-component">How to Implement the Marquee Component</h2>
<p>To implement the component, start by creating a React environment with <a target="_blank" href="https://vitejs.dev/guide/">Vite</a>.</p>
<pre><code class="lang-bash">npm create vite@latest
</code></pre>
<p>After which you navigate to your project directory, install the necessary packages and start up the dev server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/01-react-server-after-installation-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up dev server</em></p>
<p>Next, create the elements for the JSX mockup for your component.</p>
<pre><code class="lang-jsx"><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">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">" main__container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>My Marquee<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" "</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Default Behaviour<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"marquee"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"marquee__content"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{AndroidLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{BehanceLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{InstagramLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{PaypalLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SpotifyLogo}</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">ul</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"marquee__content"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{AndroidLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{BehanceLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{InstagramLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{PaypalLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SpotifyLogo}</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">ul</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">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This includes a header for the component, the behavior of the component and the data in the component to be animated.</p>
<p>It is important to duplicate the data in the component as it would be used to achieve the duplicate effect. However, we’re hiding the second list initially using the <code>aria-hidden='true'</code> property.</p>
<p>To make it more visually appealing, add these styles.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: system-ui, -apple-system, BlinkMacSystemFont, <span class="hljs-string">"Segoe UI"</span>, Roboto,
    Oxygen, Ubuntu, Cantarell, <span class="hljs-string">"Open Sans"</span>, <span class="hljs-string">"Helvetica Neue"</span>, sans-serif;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
}

* { <span class="hljs-attribute">box-sizing</span>: border-box; }

<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.2</span>;
  <span class="hljs-attribute">margin-block</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-tag">h2</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.25rem</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>;
}

<span class="hljs-selector-tag">section</span> { <span class="hljs-attribute">margin-block</span>: <span class="hljs-number">3rem</span>; }

<span class="hljs-selector-class">.main__container</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1000px</span>;
  <span class="hljs-attribute">margin-inline</span>: auto;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">124</span>, <span class="hljs-number">145</span>, <span class="hljs-number">175</span>);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">3rem</span>;
}

<span class="hljs-comment">/* Marquee styles */</span>
<span class="hljs-selector-class">.marquee</span> {
  <span class="hljs-attribute">--gap</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">overflow</span>: hidden;
  <span class="hljs-attribute">user-select</span>: none;
  <span class="hljs-attribute">gap</span>: <span class="hljs-built_in">var</span>(--gap);
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> dashed lightgray;
}

<span class="hljs-selector-class">.marquee__content</span> {
  <span class="hljs-attribute">flex-shrink</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: space-around;
  <span class="hljs-attribute">gap</span>: <span class="hljs-built_in">var</span>(--gap);
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-class">.marquee__content</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">object-fit</span>: contain;
}

<span class="hljs-selector-class">.marquee__content</span> &gt; * {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#e8daef</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">2px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.25rem</span>;
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.marquee__item</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-tag">ul</span> { <span class="hljs-attribute">padding-left</span>: <span class="hljs-number">0</span>; }
</code></pre>
<p>At the moment, your component should look like this;</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/02-UI-after-applying-styles.png" alt="Image" width="600" height="400" loading="lazy">
<em>UI after applying styles</em></p>
<p>To animate this component, start by defining custom <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes">CSS keyframes</a>.</p>
<pre><code class="lang-css"><span class="hljs-keyword">@keyframes</span> scroll {
  <span class="hljs-selector-tag">from</span> { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">0</span>); }
  <span class="hljs-selector-tag">to</span> { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(calc(-<span class="hljs-number">100%</span> - var(--gap))); }
}
</code></pre>
<p><strong>Note</strong>: The gap used is the same gap between the marquee items.<br>Then, assign those keyframes to a class.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Enable animation */</span>
<span class="hljs-selector-class">.enable-animation</span> <span class="hljs-selector-class">.marquee__content</span> {
  <span class="hljs-attribute">animation</span>: scroll <span class="hljs-number">10s</span> linear infinite;
}
</code></pre>
<p>Finally, add that class to your section element.</p>
<pre><code class="lang-jsx">&lt;section className=<span class="hljs-string">"enable-animation"</span>&gt;
</code></pre>
<p>And with that, your component should already be animating.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/03-Animated-marquee.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Animated marquee</em></p>
<h2 id="heading-how-to-enhance-the-marquee-component">How to Enhance the Marquee Component</h2>
<p>This component goes beyond the regular infinite animations (as shown above), it often possess some extra functionalities such as:</p>
<ul>
<li><strong>Pause on Hover</strong>: While using a marquee can be beneficial to show a bunch of content more dynamically, the speed of the animation or the position of the information relevant to the user might cause issues, especially for slow readers.</li>
</ul>
<p>To fix this, you can implement a pause functionality to halt it when the user hovers over it. Just add the CSS code below.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Pause on hover */</span>
<span class="hljs-selector-class">.marquee</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-class">.marquee__content</span> {
  <span class="hljs-attribute">animation-play-state</span>: paused;
}
</code></pre>
<p>And with that, it pauses on hover.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/04-Animated-marquee-that-pauses-on-hover.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Animated marquee that pauses on hover</em></p>
<ul>
<li><strong>Reverse on Double Tap:</strong> In the case where a user has passed important information and wants to see it without waiting for the looping animation to bring it back, it’s important to provide a means to achieve that.</li>
</ul>
<p>By double tapping on the component, the animation plays in the opposite direction, showing the information the user just passed. This feature not only promotes accessibility but also gives your web pages a snappy feel, as it provides a quicker way to get information.</p>
<p>To implement this, start by creating a reverse animation state.</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">const</span> [reverseAnimation, setReverseAnimation] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<p>The create a function to flip the state of the animation.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> handleDoubleClick = <span class="hljs-function">() =&gt;</span> {
    setReverseAnimation(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev);
  };
</code></pre>
<p>After that, create the CSS class rule to reverse the animation.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">/* Reverse animation */</span>
.marquee--reverse .marquee__content {
  animation-direction: reverse !important;
}
</code></pre>
<p>Then attach the handler function to the component.</p>
<pre><code class="lang-jsx">&lt;div className=<span class="hljs-string">"marquee"</span> onDoubleClick={handleDoubleClick}&gt;
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"marquee__content"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{AndroidLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{BehanceLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{InstagramLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{PaypalLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SpotifyLogo}</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">ul</span>&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"marquee__content"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{AndroidLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{BehanceLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{InstagramLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{PaypalLogo}</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SpotifyLogo}</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">ul</span>&gt;</span></span>
&lt;/div&gt;
</code></pre>
<p>Finally, conditionally add the reverse class to the component which reverses the animation on double click.</p>
<pre><code class="lang-jsx"> &lt;div className={<span class="hljs-string">`marquee <span class="hljs-subst">${reverseAnimation &amp;&amp; <span class="hljs-string">"marquee--reverse"</span>}</span>`</span>} 
      onDoubleClick={handleDoubleClick}&gt;
</code></pre>
<p>Double-clicking the component now gives;</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/05-Animated-marquee-that-revereses-on-double-click.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Animated marquee that reverses on double click</em></p>
<ul>
<li><strong>Pause/Play on Space Bar Click</strong>: Another functionality that can be added to improve UX, especially for keyboard users is to pause or play it on press of the spacebar. This mimics the functionality of how videos work on the web and would help improve accessibility for users.</li>
</ul>
<p>To implement this, start by creating a state to store the current paused state of the animation.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> [isAnimationPaused, setIsAnimationPaused] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<p>Then create the CSS rule for the paused state.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">/* Pause animation */</span>
.marquee--paused .marquee__content {
  animation-play-state: paused !important;
}
</code></pre>
<p>After that, create an effect that updates the <code>isAnimationPaused</code> state each time the space bar is pressed.</p>
<pre><code class="lang-jsx"> useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> handleKeyPress = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (event.code === <span class="hljs-string">"Space"</span>) {

        setIsAnimationPaused(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev);
      }
    };
    <span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, handleKeyPress);

   <span class="hljs-comment">// Clean-up function when component unmounts</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">document</span>.removeEventListener(<span class="hljs-string">"keydown"</span>, handleKeyPress);
    };
  }, []);
</code></pre>
<p>This way, the state toggles between true and false based on user presses.<br>Finally, dynamically add the paused class to your component.</p>
<pre><code class="lang-jsx">&lt;div className={<span class="hljs-string">`marquee <span class="hljs-subst">${reverseAnimation &amp;&amp; <span class="hljs-string">"marquee--reverse"</span>}</span> <span class="hljs-subst">${
       isAnimationPaused &amp;&amp; <span class="hljs-string">"marquee--paused"</span>}</span>`</span>} onDoubleClick={handleDoubleClick}&gt;
</code></pre>
<p>And with that, your component pauses and replays each time you press the space bar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/06-Animated-marquee-that-can-be-paused-with-space-bar.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Animated marquee that can be paused with space bar</em></p>
<h2 id="heading-best-practices-and-tips-for-marquee-component-development">Best Practices and Tips for Marquee Component Development</h2>
<p>Some of the best practices to consider when building this component includes:</p>
<ul>
<li><strong>Lazy Loading Images</strong>: In the case where your marquee has a lot of high-quality images which could be large, it is essential to optimize them before building the component.<br><a target="_blank" href="https://daiveedjay.hashnode.dev/implementing-image-lazy-loading-to-improve-website-performance-using-javascript">Lazy loading</a> defers the browser from downloading the images until they are needed (required to be shown in the viewport), cutting down on overall page load time.</li>
</ul>
<p>To achieve this, add <code>loading='lazy'</code> property to your images.</p>
<pre><code class="lang-jsx">&lt;ul className=<span class="hljs-string">"marquee__content"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{AndroidLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{BehanceLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{InstagramLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{PaypalLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SpotifyLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  &lt;/ul&gt;

  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"marquee__content"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{AndroidLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{BehanceLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{GoogleLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{InstagramLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{PaypalLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</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">" marquee__item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{SpotifyLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</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">ul</span>&gt;</span></span>
</code></pre>
<ul>
<li><strong>Mindful Animation Speed</strong>: When implementing the animation, it's crucial to strike a careful balance in terms of speed. The animation pace should be visually appealing, capturing the user's attention without sacrificing readability or causing discomfort.<br>This involves thoughtful consideration of how quickly the content scrolls across the screen.<br>By paying attention to animation speed and finding the right balance, you enhance the overall user experience, making it an effective and enjoyable element on your website.</li>
<li><strong>Think of Users with Motion Sensitivity</strong>: Inclusive design means taking into account the needs and preferences of different users, including those with motion sensitivity. Certain users may prefer less movement due to conditions such as vestibular disorders or simply for personal comfort.<br>To support these users, you can use the <code>prefers-reduced-motion</code> media query in your component.</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-comment">/* Pause animation when reduced-motion is set */</span>
@media (prefers-reduced-motion: reduce) {
  .marquee__content {
    animation-play-state: paused !important;
  }
}
</code></pre>
<ul>
<li><strong>Proper Documentation</strong>: Provide clear documentation on how users can use your component effectively so that they won't struggle to use it or don’t miss out on the all the features it has. Consider using labels around the component or a pop-up to convey short instructions on its use.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Your guide to React Marquee Components is complete! From planning to execution, you've dived into creating dynamic scrolling elements for your web projects.</p>
<p>Remember, this component is more than motion—it's an interactive story. Whether sharing crucial info, promoting events, or injecting dynamism, your marquee is a versatile addition to your toolkit.</p>
<p>But this journey is just the beginning. Adjust speeds, consider sensitivities, and adopt best practices to refine your marquee. Let creativity flow, and may your scrolling stories leave a lasting impact.</p>
<p>Prioritize user experience, experiment with enhancements, and let your development shine in the web landscape. Happy scrolling!</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: <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 Use Bento Grids Design in Your Web Projects ]]>
                </title>
                <description>
                    <![CDATA[ I believe we may have all noticed the trend of meticulously organized web layouts reminiscent of Japanese bento boxes. These 'Bento Grids' have swiftly gained traction, offering a visually appealing and structurally cohesive way to present content on... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/bento-grids-in-web-design/</link>
                <guid isPermaLink="false">66bb88f4add24ba4273250de</guid>
                
                    <category>
                        <![CDATA[ CSS Grid ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Jaja ]]>
                </dc:creator>
                <pubDate>Thu, 04 Apr 2024 10:06:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Article-cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I believe we may have all noticed the trend of meticulously organized web layouts reminiscent of Japanese bento boxes. These 'Bento Grids' have swiftly gained traction, offering a visually appealing and structurally cohesive way to present content online.</p>
<p>In this article, we'll delve into the origins, rise, and practical implementation of the bento grid trend, exploring how it intersects aesthetics with functionality in modern web design.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li>Fundamentals of HTML and CSS (CSS Grid)</li>
<li>Basics of Web Design</li>
</ul>
<h2 id="heading-the-philosophy-behind-bento-grids">The Philosophy Behind Bento Grids</h2>
<p>The concept of bento grids traces back to the Japanese tradition of serving a variety of dishes in a single, segmented container known as a bento box. This method of presentation not only ensures a balanced meal but also pleases the eye with its organization and simplicity.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Bento-Japenese.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Bento Japanese Box</em></p>
<p>Translated into web design, bento grids offer a similar experience: diverse content segmented into distinct areas, making it both accessible and aesthetically balanced.</p>
<p>The bento grid philosophy hinges on compartmentalization and organization. It creates a predictable rhythm that users can follow, reducing cognitive load and enhancing the overall user experience. The design's symmetry and order offers a sense of calm and control, appealing to users' desire for simplicity and structure amidst the chaos of the internet.</p>
<h2 id="heading-the-rise-of-bento-grids">The Rise of Bento Grids</h2>
<p>While the use of grids in design is not new, the specific trend of bento grids began to gain traction as designers sought to create more organized and mobile-responsive layouts. </p>
<p>Powerhouses like Apple use this design pattern in promotional videos for their products as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Apple-mac-promotional-infographic.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Apple mac promotional infographic</em></p>
<p>The trend also saw a surge in popularity with the advent of CSS Grid Layout and the growing focus on minimalist design that doesn't sacrifice functionality for form.</p>
<p>Platforms that emphasize content, such as online magazines, educational sites, and portfolios, were among the early adopters of bento grids. Their adoption highlighted the grid's ability to present diverse content types in a harmonious layout, thereby improving navigation and readability.</p>
<h2 id="heading-examining-the-trend">Examining the Trend</h2>
<p>Clean lines, geometric shapes, and a clear space division often characterize modern bento grids. They usually consist of:</p>
<ul>
<li>The title of a feature</li>
<li>A short description of the feature</li>
<li>Some infographic or interactive content.</li>
</ul>
<p>This trend has been propelled by its positive impact on user experience. A well-implemented bento grid guides users through the website with ease, allowing quick access to information without overwhelming them with choices.</p>
<h2 id="heading-pros-of-using-bento-grids">Pros of Using Bento Grids</h2>
<ol>
<li><strong>Enhanced Organization and Cohesion</strong>: Bento grids bring a high level of organization to web design, allowing for a cohesive presentation of varied content. This segmentation makes it easier for users to digest information in a structured manner.</li>
<li><strong>Aesthetic Appeal</strong>: The symmetry and clean lines inherent in bento grids are visually appealing, providing a neat and professional look that can enhance the visual identity of a website.</li>
<li><strong>Reduced Scroll Fatigue</strong>: By efficiently utilizing space within a single viewport, bento grids can display a significant amount of content, which can reduce the need for excessive scrolling.</li>
<li><strong>Metaphorical Clarity</strong>: The bento box metaphor effectively communicates the concept of a complete and balanced experience, which can be particularly useful for showcasing product features or a portfolio of work.</li>
<li><strong>Improved Navigation</strong>: The predictability and order of bento grids aid in straightforward navigation, as users can easily move from one compartmentalized section to another.</li>
<li><strong>Compatibility with Responsive Design</strong>: Bento grids seamlessly integrate with responsive design principles, facilitating effortless adjustment of a website's layout to accommodate diverse screen sizes and devices.</li>
<li><strong>Focus on Content</strong>: The grid layout emphasizes content without unnecessary distractions, which can be crucial for sites where content is important, such as online galleries or information-driven platforms. </li>
<li><strong>Facilitates Comprehensive Product Capture</strong>: In the context of showcasing products, bento grids offer users the convenience of capturing all essential information at once. With content neatly organized within distinct compartments, users can easily screenshot or save a single view of the grid, ensuring they capture all relevant details without the need for multiple interactions or navigating through various pages.</li>
</ol>
<h2 id="heading-cons-of-bento-grids">Cons of Bento Grids</h2>
<ol>
<li><strong>Potential Information Overload</strong>: While bento grids can reduce cognitive load through organization, there's a risk of cramming too much information into a single screen, potentially overwhelming users.</li>
<li><strong>Limited Visual Hierarchy</strong>: The uniform structure of bento grids can sometimes lead to a lack of visual hierarchy, making it harder for users to determine the importance of certain content over others.</li>
<li><strong>Considerations Regarding Hick's Law</strong>: A densely packed grid presents users with a multitude of options, potentially prolonging their decision-making process. This abundance of choices can result in the paradox of choice, where <a target="_blank" href="https://lawsofux.com/hicks-law/">users experience indecision or slower navigation due to the overwhelming array of options available</a>.</li>
<li><strong>Design Rigidity</strong>: The structured nature of bento grids can sometimes restrict creative design elements and lead to a monotonous user experience if not implemented with variation and dynamic content.</li>
<li><strong>SEO Challenges</strong>: Search engines may have difficulty parsing the relevance of content when it's distributed across numerous grid compartments, potentially impacting SEO if not structured properly.</li>
<li><strong>Accessibility Concerns</strong>: The compartmentalized nature of bento grids might present accessibility challenges, particularly for users who rely on screen readers or keyboard navigation, if not designed with accessibility standards in mind.</li>
</ol>
<h2 id="heading-bento-grids-in-practice">Bento Grids in Practice</h2>
<p>Creating bento grids typically involves CSS grid layout and flexbox, which offer robust solutions for creating complex layouts with ease. Bento grids are favored for their flexibility and responsiveness, allowing content to reflow seamlessly across different screen sizes.</p>
<p>For this article, here’s the design we’re going to create the interface below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/desktop-design-to-recreate.png" alt="Image" width="600" height="400" loading="lazy">
<em>desktop design to recreate</em></p>
<p>To begin, create a couple of <code>divs</code> in your markup.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">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">"grid"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</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">"item"</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">"item"</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">"item"</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">"item"</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">body</span>&gt;</span>
</code></pre>
<p>Then utilize the <code>grid</code> property, <code>grid-template-columns</code>, and <code>grid-template-rows</code> to define the number of rows and columns you desire.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.grid</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">36</span>, <span class="hljs-number">100%</span>, <span class="hljs-number">99%</span>);
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1500px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">1000px</span>;
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">1.5vw</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1vw</span>;
  <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-built_in">repeat</span>(<span class="hljs-number">6</span>, <span class="hljs-number">1</span>fr);
  <span class="hljs-attribute">grid-template-rows</span>: auto;
}
</code></pre>
<p>The final ingredient to achieving a bento-style grid lies in the use of the <code>grid-template-areas</code> property which is used to name grid areas according to the position you want them to occupy on the page.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.grid</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">hsl</span>(<span class="hljs-number">36</span>, <span class="hljs-number">100%</span>, <span class="hljs-number">99%</span>);
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1500px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">1000px</span>;
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">1.5vw</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1vw</span>;
  <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-built_in">repeat</span>(<span class="hljs-number">6</span>, <span class="hljs-number">1</span>fr);
  <span class="hljs-attribute">grid-template-rows</span>: auto;
  <span class="hljs-attribute">grid-template-areas</span>:
    <span class="hljs-string">"hero hero hero hero aside2 aside2"</span>
    <span class="hljs-string">"hero hero hero hero aside2 aside2"</span>
    <span class="hljs-string">"hero hero hero hero aside2 aside2"</span>
    <span class="hljs-string">"hero hero hero hero aside2 aside2"</span>
    <span class="hljs-string">"aside3 aside3 aside4 aside4 aside5 aside5 "</span>;
}
</code></pre>
<p>Finally, assign those names to the exact element you want to take up that space.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.item</span> {
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#464545</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
}

<span class="hljs-selector-class">.grid</span> <span class="hljs-selector-class">.item</span><span class="hljs-selector-pseudo">:nth-child(1)</span> {
  <span class="hljs-attribute">grid-area</span>: hero;
}
<span class="hljs-selector-class">.grid</span> <span class="hljs-selector-class">.item</span><span class="hljs-selector-pseudo">:nth-child(2)</span> {
  <span class="hljs-attribute">grid-area</span>: aside2;
}

<span class="hljs-selector-class">.grid</span> <span class="hljs-selector-class">.item</span><span class="hljs-selector-pseudo">:nth-child(3)</span> {
  <span class="hljs-attribute">grid-area</span>: aside3;
}
<span class="hljs-selector-class">.grid</span> <span class="hljs-selector-class">.item</span><span class="hljs-selector-pseudo">:nth-child(4)</span> {
  <span class="hljs-attribute">grid-area</span>: aside4;
}

<span class="hljs-selector-class">.grid</span> <span class="hljs-selector-class">.item</span><span class="hljs-selector-pseudo">:nth-child(5)</span> {
  <span class="hljs-attribute">grid-area</span>: aside5;
}
</code></pre>
<p>You should have this result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/01-Bento-structure-achieved-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Bento structure achieved</em></p>
<p>All that’s left is to fill the boxes with their appropriate content and assets. You can some assets/content in this <a target="_blank" href="https://github.com/Daiveedjay/Bento/tree/main/images">repo</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/02-Bento-with-content-filled.png" alt="Image" width="600" height="400" loading="lazy">
<em>Bento Grid with its content filled</em></p>
<p>Another usefulness of the <code>grid-template-areas</code> property is the ease by which you can achieve responsiveness with it. In our example, to make the page responsive, you pass in a new string pair on your preferred threshold.</p>
<p>On tablet screens:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">1000px</span>) {
  <span class="hljs-selector-class">.grid</span> {
    <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-built_in">repeat</span>(<span class="hljs-number">4</span>, <span class="hljs-number">1</span>fr);
    <span class="hljs-attribute">grid-template-areas</span>:
      <span class="hljs-string">"hero   hero   hero   hero"</span>
      <span class="hljs-string">"hero   hero   hero   hero"</span>
      <span class="hljs-string">"aside2 aside2 aside2 aside3"</span>
      <span class="hljs-string">"aside4 aside4 aside5 aside5"</span>;
  }
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/03-Tablet-screens.png" alt="Image" width="600" height="400" loading="lazy">
<em>Tablet Screens</em></p>
<p>And on smaller screens:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">750px</span>) {
  <span class="hljs-selector-class">.grid</span> {
    <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-built_in">repeat</span>(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>fr);
    <span class="hljs-attribute">grid-template-areas</span>:
      <span class="hljs-string">"hero   hero   hero"</span>
      <span class="hljs-string">"hero   hero   hero"</span>
      <span class="hljs-string">"aside2 aside2 aside2"</span>
      <span class="hljs-string">"aside3 aside3 aside3"</span>
      <span class="hljs-string">"aside4 aside4 aside4"</span>
      <span class="hljs-string">"aside5 aside5 aside5"</span>;
  }
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/04-Smaller-screens.png" alt="Image" width="600" height="400" loading="lazy">
<em>Smaller screens</em></p>
<h2 id="heading-common-rules-of-bento-grids">Common Rules of Bento Grids</h2>
<p>Here are some good rules of thumb when building bento grids:</p>
<ul>
<li><strong>Group Related Content</strong>: One of the fundamental principles of bento grids is to group related content together within each segment. This enhances the user's ability to quickly locate and understand the information they're seeking. By organizing content logically, designers can improve user engagement and satisfaction.</li>
<li><strong>Vary Box Sizes</strong>: Avoid using the same size for every box within the grid. Varying box sizes can create visual interest and hierarchy, drawing attention to key elements while maintaining overall balance. This variation can help guide users through the content and highlight important information effectively.</li>
<li><strong>Establish Visual Hiererchy</strong>: Although varying box sizes contribute to visual hierarchy, establishing visual hierarchy encompasses a broader range of design elements. In addition to box sizes, designers should consider factors such as colour, typography, and placement to prioritize certain elements over others.</li>
<li><strong>Prioritize Center Square</strong>: In traditional bento grids, the center square often holds a special significance and acts as a focal point. Designers can use this central square to showcase critical information or highlight key features, effectively punctuating the grid and drawing users' attention to its core elements.</li>
<li><strong>Limit the Number of Boxes</strong>: To maintain clarity and avoid overwhelming users, it's recommended to use nine or fewer boxes within the bento grid. Limiting the number of boxes ensures that the layout remains manageable and facilitates easier navigation and comprehension for users.</li>
<li><strong>Consider Swirl Pattern</strong>: While not a strict rule, considering a swirl pattern can add an extra layer of visual interest to the bento grid design. This involves arranging content in a curved or swirling pattern within the grid, creating a dynamic and engaging layout that encourages exploration.</li>
</ul>
<h2 id="heading-additional-information">Additional Information</h2>
<p>I’d like to point out a couple of things in the article not highlighted.</p>
<ul>
<li>First and foremost, the article was inspired by <a target="_blank" href="https://x.com/itsdesignertom/status/1764856109754667243?s=20">Tom Geoco</a>’s video on bento grids.</li>
<li><a target="_blank" href="https://bentogrids.com/">BentoGrids</a> is an excellent resource for finding design inspiration if you’re interested. If you’re interested in the full code, here’s the repo, <a target="_blank" href="https://github.com/Daiveedjay/Bento">GitHub</a>, and the Live version. <a target="_blank" href="https://bentogrid.netlify.app/">Demo</a>.</li>
<li>The design inspiration for the grid we built was gotten from <a target="_blank" href="https://www.frontendmentor.io/challenges/news-homepage-H6SWTa1MFl">FrontEnd Mentor</a>.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Bento grids stand out as a significant trend in the modern web design landscape, offering a blend of aesthetic appeal and functional clarity. They represent a design ethos that values order, beauty, and user-centricity. </p>
<p>As web technologies evolve, the principles underlying bento grids will continue to inform best practices, encouraging designers to create experiences that are not only visually compelling but also intuitively navigable.</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 / 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 Build a Pokemon App with GraphQL and Apollo ]]>
                </title>
                <description>
                    <![CDATA[ Pokemon is a Japanese media franchise consisting of video games, animated series and films, a trading card game, and other related media. In this blog, we will be building with a Pokemon GraphQL API that gives us data about different Pokemons. We wil... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-pokemon-app-with-graphql-and-apollo/</link>
                <guid isPermaLink="false">66c8c949e9e57963a5d82ad0</guid>
                
                    <category>
                        <![CDATA[ Apollo GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Segun Ajibola ]]>
                </dc:creator>
                <pubDate>Wed, 03 Apr 2024 12:28:07 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Parameters-vs-Arguments--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Pokemon is a Japanese media franchise consisting of video games, animated series and films, a trading card game, and other related media.</p>
<p>In this blog, we will be building with a Pokemon GraphQL API that gives us data about different Pokemons.</p>
<p>We will be using Apollo and GraphQL to handle the data fetching, and React for building our front-end application.</p>
<p>No worries if you don't know these technologies, I will be walking you through the basics as you read on.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>You should have these in your computer to follow along:</p>
<ul>
<li>Nodejs v18+</li>
<li>A code editor</li>
<li>A web browser</li>
</ul>
<p>Let's create our React app.</p>
<h3 id="heading-react-application-setup">React Application Setup</h3>
<p>To create your React app, navigate to your terminal, and use the Command Prompt. Open your Command Prompt and choose your preferred location for creating your React project. Let's go with Desktop.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> Desktop
</code></pre>
<p>The above command will navigate to your Desktop.</p>
<pre><code class="lang-bash">npm create vite@latest pokemon-app -- --template react
</code></pre>
<p><code>npm create vite@latest</code> will start to build a new project using Vite. But we attached the name of our project (<code>pokemon-app</code>) and the technology or framework our app will be using (<code>-- -- template react</code>). </p>
<p>You can set another template like <code>svelte</code>, <code>vanilla</code> or <code>vue</code> and the project will be created using that framework. Read more about Vite on <a target="_blank" href="https://vitejs.dev/guide/">its official website</a>.</p>
<p>After the Vite installation, run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> pokemon-app
npm install
npm run dev
</code></pre>
<p>We'll use the commands above to finish the React setup.</p>
<p>Run the first command, <code>cd pokemon-app</code>, to navigate to the <strong>pokeman-app</strong> folder.</p>
<p>Run <code>code .</code> to open the folder in your code editor.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1711666709423/593e293d-af0b-4cbd-b4ac-83d0c8213446.png" alt="Image" width="1270" height="960" loading="lazy">
<em>modal displaying over VSCode to accept that you trust the authors of the files opened in VSCode</em></p>
<p>Mark the trust the author checkbox if that pops up.</p>
<p>Open your code editor's terminal. If you are running VSCode on Windows, the shortcut is `Ctrl + `` .</p>
<p>Run the other 2 commands in the terminal one after the other.</p>
<pre><code class="lang-bash">npm install
</code></pre>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Your project should be running in the browser now.</p>
<p>We will be managing our data fetching using GraphQL and Apollo.</p>
<h2 id="heading-how-to-use-graphql-and-apollo">How to Use GraphQL and Apollo</h2>
<p>GraphQL is a query language for APIs and a runtime for fulfilling queries with your existing data. It allows you to request only the data you need in your application and nothing more, making it very efficient and flexible.</p>
<p>Apollo is a state management library that allows you to manage local and remote data with GraphQL. It can be used to fetch, cache, and modify application data, all while automatically updating your UI.</p>
<p>Let's install the packages you need.</p>
<h3 id="heading-installing-packages">Installing Packages</h3>
<p>Run the command below in your terminal to install the Apollo client.</p>
<pre><code class="lang-bash">npm install @apollo/client
</code></pre>
<p>Navigate to your <strong>main.jsx</strong> file and import these:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.jsx"</span>;
<span class="hljs-keyword">import</span> {
  ApolloProvider,
  ApolloClient,
  InMemoryCache,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
</code></pre>
<p>You have imported React and ReactDOM for DOM manipulation.</p>
<p><code>ApolloClient</code> is responsible for managing your application's data fetching and state management. It handles sending GraphQL queries and mutations to your GraphQL server and caching the results.</p>
<p><code>ApolloProvider</code> will be used to wrap your React application to provide the Apollo Client instance to all your components so that your application can access data fetched through Apollo Client.</p>
<p><code>InMemoryCache</code> is a cache implementation to store the results of GraphQL queries in memory for efficient access and retrieval.</p>
<p>You have also imported <strong>index.css</strong> to style your application.</p>
<h3 id="heading-how-to-create-an-apollo-client">How to Create an Apollo Client</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> ApolloClient({
  <span class="hljs-attr">uri</span>: <span class="hljs-string">"https://graphql-pokemon2.vercel.app/"</span>,
  <span class="hljs-attr">cache</span>: <span class="hljs-keyword">new</span> InMemoryCache(),
});
</code></pre>
<p>The code above creates a new instance of <code>ApolloClient</code> with the some configurations:</p>
<ol>
<li><code>uri</code>: This specifies the URL of your GraphQL API endpoint. This is the endpoint where your Apollo Client will send GraphQL queries and mutations.</li>
<li><code>cache</code>: This configures the cache implementation for Apollo Client to use an in-memory cache to access data and store the result of GraphQL queries, reducing the need to re-fetch data from the server.</li>
</ol>
<p>You can now wrap your <code>&lt;App /&gt;</code> component with <code>ApolloProvider</code>:</p>
<pre><code class="lang-javascript">ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)).render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ApolloProvider</span> <span class="hljs-attr">client</span>=<span class="hljs-string">{client}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ApolloProvider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>Note that <code>client</code> props was also passed to provide your application with <code>ApolloClient</code> configuration.</p>
<p>Go to your <strong>App.jsx</strong> component and input this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { PokemonsContainer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/PokemonsContainer"</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">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">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">PokemonsContainer</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>You imported React and <code>PokemonsContainer</code> will be created. The <code>PokemonsContainer</code> component was wrapped in main tag and will be rendered when the component is pasted in the DOM.</p>
<p>Let's create the <code>PokemonsContainer</code> component in a file located in <strong>components</strong> folder. That is:</p>
<p>📂 src/components/PokemonsContainer.jsx</p>
<h3 id="heading-pokemons-container-component">Pokemons Container Component</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;
<span class="hljs-keyword">import</span> { Pokemon } <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Pokemon"</span>;
<span class="hljs-keyword">import</span> { GET_POKEMONS } <span class="hljs-keyword">from</span> <span class="hljs-string">"../graphql/get-pokemons"</span>;
</code></pre>
<p>The <code>useQuery</code> from <code>@apollo/client</code> is used for executing queries in an Apollo application. To do that, <code>useQuery()</code> is called and a GraphQL query string is passed as a argument. When your component renders, <code>useQuery</code> returns an object from Apollo Client that contains <code>loading</code>, <code>error</code>, and <code>data</code> properties that you can use to render your UI.</p>
<p><code>Pokemon</code> component was imported to render a user interface for a Pokemon, this will be built shortly.</p>
<p><code>GET_POKEMONS</code> was also imported. This will contain a GraphQL query.</p>
<p>After importing the above functions, continue building your page.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PokemonsContainer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { loading, error, data } = useQuery(GET_POKEMONS, {
    <span class="hljs-attr">variables</span>: { <span class="hljs-attr">first</span>: <span class="hljs-number">5</span> },
  });

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Error: {error.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;

  <span class="hljs-keyword">const</span> pokemons = data?.pokemons || [];
  <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">"container"</span>&gt;</span>
      {pokemons &amp;&amp;
        pokemons.map((pokemon) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Pokemon</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{pokemon.id}</span> <span class="hljs-attr">pokemon</span>=<span class="hljs-string">{pokemon}</span> /&gt;</span>
        ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>As mentioned earlier, <code>useQuery</code> returns an object from Apollo Client that contains <code>loading</code>, <code>error</code>, and <code>data</code> properties. They are destructured here so you can access them in the page.</p>
<p>Notice that we're providing a configuration option (<code>variables</code>) to the <code>useQuery</code> hook. <code>{ variables: { first: 5 } }</code> was also passed as the second argument. The <code>variables</code> option is an object that contains all of the variables we want to pass to our GraphQL query. In this case, we passed an object <code>{ first: 5 }</code> to specify that we want the first five Pokemons.</p>
<p>If the query is still loading, <code>&lt;p&gt;Loading...&lt;/p&gt;</code> is returned to signify the user while <code>&lt;p&gt;Error: {error.message}&lt;/p&gt;</code> will be returned if there is an error.</p>
<p>The <code>pokemons</code> constant was created to hold the value of the Pokemons property of the data object. If <code>data.pokemons</code> is not available, the <code>pokemons</code> constant will be an empty array.</p>
<p>A div is returned with a <code>classname</code> of <code>container</code> which checks if <code>pokemons</code> is available and maps the array over the <code>Pokemon</code> component.</p>
<p>Let's create the <code>Pokemon</code> component:</p>
<p>📂src/components/Pokemon.jsx</p>
<h2 id="heading-pokemon-component">Pokemon Component</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Pokemon</span>(<span class="hljs-params">{ pokemon }</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">"pokemon"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"pokemon__name"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{pokemon.name}<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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"pokemon__meta"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{pokemon.maxHP}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{pokemon.maxCP}<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> <span class="hljs-attr">className</span>=<span class="hljs-string">"pokemon__image"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{pokemon.image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{pokemon.name}</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">"pokemon__attacks"</span>&gt;</span>
        {pokemon.attacks.special.slice(0, 3).map((attack) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">attack.name</span>}<span class="hljs-attr">-</span>${<span class="hljs-attr">attack.damage</span>}`}&gt;</span>{attack.name}<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>
  );
}
</code></pre>
<p>The structure of an instance of a Pokemon is defined here with the classname for styling. The <code>name</code>, <code>maxHP</code>, <code>maxCP</code>, <code>image</code> and <code>attacks</code> array will be rendered.</p>
<p>Let's create the <code>GET_POKEMONS</code> GraphQL query.</p>
<p>📂src/graphql/get-pokemons</p>
<h2 id="heading-graphql-query">GraphQL Query</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> gql <span class="hljs-keyword">from</span> <span class="hljs-string">"graphql-tag"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> GET_POKEMONS = gql<span class="hljs-string">`
  query pokemons($first: Int!) {
    pokemons(first: $first) {
      id
      name
      image
      maxHP
      maxCP
      attacks {
        special {
          name
          damage
        }
      }
    }
  }
`</span>;
</code></pre>
<p>You imported <code>gql</code> from <code>graphql-tag</code> and created a GraphQL query named <code>GET_POKEMONS</code>.</p>
<p>The <code>pokemons</code> query function was wrapped in strings for the <code>gql</code> function to parse them into query documents.</p>
<p><code>$first: Int!</code> means that your query is expecting a variable called <code>first</code>, which is an integer, and the <code>!</code> symbol after the <code>Int</code> means that the variable is required.</p>
<p>Recall that we created the <code>variables</code> object in the <code>PokemonsContainer</code> component, it's here below.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> { loading, error, data } = useQuery(GET_POKEMONS, {
   <span class="hljs-attr">variables</span>: { <span class="hljs-attr">first</span>: <span class="hljs-number">5</span> },
 });
</code></pre>
<p><code>pokemons(first: $first)</code> was also declared. <code>$first</code> will be assigned to 5 here (we passed in 9 in the above code snippet). Thus, the array will contain only 5 objects. Each object will contain <code>id</code>, <code>name</code>, <code>image</code>, <code>maxHP</code>, <code>maxCP</code>, and attacks object which will contain the special object containing name and damage.</p>
<p>The GraphQL server might contain more properties but will only return the properties listed above. That is one of the cool functionalities of GraqhQL – it gives you only the data you request for.</p>
<h2 id="heading-styling-our-application">Styling our Application</h2>
<p>Your <strong>index.css</strong> should contain this:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* RESETS
=========================================== */</span>
<span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">-webkit-box-sizing</span>: border-box;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

*,
*<span class="hljs-selector-pseudo">:before</span>,
*<span class="hljs-selector-pseudo">:after</span> {
  <span class="hljs-attribute">-webkit-box-sizing</span>: inherit;
  <span class="hljs-attribute">box-sizing</span>: inherit;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span> <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Segoe UI"</span>, Tahoma, Geneva, Verdana, sans-serif;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#202020</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fbfbfb</span>;
  <span class="hljs-attribute">font-smooth</span>: always;
  <span class="hljs-attribute">-webkit-font-smoothing</span>: antialiased;
  <span class="hljs-attribute">-moz-osx-font-smoothing</span>: grayscale;
}

<span class="hljs-comment">/* POKEMON APPLICATION
=========================================== */</span>
<span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">80%</span>;
  <span class="hljs-attribute">margin</span>: auto;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">20%</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">background-clip</span>: border-box;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <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.125</span>);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.25rem</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0.125rem</span> <span class="hljs-number">0.25rem</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.075</span>);
  <span class="hljs-attribute">overflow</span>: hidden;
  <span class="hljs-comment">/* margin: 5px; */</span>
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__name</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ecd018</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__name</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">text-transform</span>: uppercase;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">4px</span>;
  <span class="hljs-attribute">text-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">1px</span> <span class="hljs-number">2px</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.4</span>);
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__image</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__image</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: auto;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__attacks</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">padding-left</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">padding-right</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">justify-content</span>: space-between;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__attacks</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">32%</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f16820</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">3px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">7px</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">padding-left</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">padding-right</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">12px</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">word-wrap</span>: break-word;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__meta</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span> <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.container</span> <span class="hljs-selector-class">.pokemon__meta</span> <span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">text-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">1px</span> <span class="hljs-number">2px</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.4</span>);
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#7bb7b7</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
}
</code></pre>
<p>All things done right, you should have this in your browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712103327205/94a0fc27-6e6e-4441-b7e7-c3e3df005383.png" alt="Image" width="1920" height="978" loading="lazy">
<em>a picture showing the five pokemons data in the browser</em></p>
<p>You can get the GitHub code here: <a target="_blank" href="https://github.com/segunajibola/pokemon-graphql">https://github.com/segunajibola/pokemon-graphql</a></p>
<p>You can also view the live site hosted on Vercel here: <a target="_blank" href="http://pokemonsapp.vercel.app"><strong>pokemonsapp.vercel.app</strong></a></p>
<p>Check my portfolio of projects: <a target="_blank" href="https://segunajibola.com">segunajibola.com</a></p>
<h2 id="heading-conclusionhttpspokemonsappvercelapp"><a target="_blank" href="https://pokemonsapp.vercel.app/">Conclusion</a></h2>
<p>That will be all. I hope you found value here as you learn more about the web.</p>
<p>If you enjoyed this article and want to see more content related to JavaScript and web development, then follow me here, <a target="_blank" href="https://x.com/intent/follow?screen_name=iamsegunajibola">Twitter (X)</a> or connect on <a target="_blank" href="https://www.linkedin.com/mwlite/in/segun-ajibola-511502175">LinkedIn</a>. I'd be happy to count you as one of my ever-growing group of awesome friends on the internet.</p>
<p>You can also join my <a target="_blank" href="https://chat.whatsapp.com/E57KqFYQK9B1woySXTaqKr">WhatsApp developer community</a> and <a target="_blank" href="https://chat.whatsapp.com/KH7r2EA6kMgHuHwVfM4VFB">OpenSource community</a> of 330+ developers learning and building cool projects.</p>
<p>If you also want to support me, you can also <a target="_blank" href="https://www.buymeacoffee.com/segunajibola">buy me a cup of coffee</a>.</p>
<p>Thanks and bye. 👋</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an AI-enhanced Task App with React and Appwrite ]]>
                </title>
                <description>
                    <![CDATA[ In this article, you'll build a task manager application that has some artificial intelligence capabilities and is voice-enabled, sortable, and searchable.  As an extra, the application will have dark mode support that respects the users' system pref... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-task-app/</link>
                <guid isPermaLink="false">66b999a9d9d170feecefbbc5</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Appwrite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ crud ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Fatuma Abdullahi ]]>
                </dc:creator>
                <pubDate>Wed, 13 Mar 2024 09:21:45 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/Group-3--20-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, you'll build a task manager application that has some artificial intelligence capabilities and is voice-enabled, sortable, and searchable. </p>
<p>As an extra, the application will have dark mode support that respects the users' system preferences.</p>
<p>The application will be able to create, read, update and delete (CRUD) tasks as well as the ability to view a given task.  </p>
<p>You'll build this application using Appwrite as a backend, React on the frontend, Typescript for type safety and Tailwind CSS for styling.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
    <li><a href="#prerequisites">Prerequisites</a></li>
    <li><a href="#what-is-appwrite">What is Appwrite?</a></li>
    <li><a href="#how-to-set-up-the-appwrite-backend">How to Set Up the Appwrite Backend</a></li>
    <li><a href="#how-to-set-up-the-react-frontend">How to Set up the React Frontend</a></li>
    <li><a href="#how-to-connect-to-the-appwrite-project">How to Connect to the Appwrite Project</a></li>
    <li><a href="#how-to-build-the-task-manager-application">How to Build the Task Manager Application</a>
        <ul>
            <li><a href="#how-to-set-up-routing-with-react-router-v6">How to Set up Routing with React Router V6</a></li>
            <li><a href="#how-to-create-the-form-component">How to Create the Form Component</a></li>
            <li><a href="#how-to-set-up-form-to-create-task">How to Set up Form to Create Task</a></li>
            <li><a href="#how-to-make-the-tasks-editable">How to Make the Tasks Editable</a></li>
            <li><a href="#how-to-enable-viewing-of-tasks">How to Enable Viewing of Tasks</a></li>
            <li><a href="#how-to-auto-generate-descriptions-with-vercel-s-ai-sdk">How to Auto Generate Descriptions with Vercel's AI SDK</a></li>
            <li><a href="#voice-enable-the-application-with-the-react-speech-recognition-package">Voice-enable the Application with the React Speech Recognition Package</a></li>
            <li><a href="#how-to-add-search-functionality-to-the-application">How to Add Search Functionality to the Application</a></li>
            <li><a href="#how-to-add-ability-to-sort-tasks-via-due-date-and-priorityadd-ability-to-sort-tasks-via-due-date-and-priority">How to Add Ability to Sort Tasks via Due Date and Priority</a></li>
            <li><a href="#bonus-add-dark-mode-support">Bonus: Add Dark Mode Support</a></li>

        </ul>
    </li>
    <li><a href="#notes">Notes</a></li>
    <li><a href="#limitations">Limitations</a></li>
</ul>



<h2 id="heading-prerequisitesheading-prerequisites-1"><a class="post-section-overview" href="#heading-prerequisites-1">Prerequisites</a></h2>
<p>You will need the following to be able to build along with this article:</p>
<ul>
<li>Basic programming knowledge</li>
<li>Basic understanding of React, Typescript and Tailwind</li>
<li><a target="_blank" href="https://appwrite.io/">An Appwrite account</a></li>
<li>And a text editor to code along</li>
</ul>
<h2 id="heading-what-is-appwrite">What is Appwrite?</h2>
<p>Appwrite is an <a target="_blank" href="https://opensource.com/resources/what-open-source">open source</a> Backend-as-a-Service (BaaS) platform. A BaaS is a cloud service that packages backend tasks that are typically needed for most applications. </p>
<p>Appwrite offers both a managed database, authentication, functions and storage services and the ability to self-host the entire platform on your own. </p>
<p>Appwrite recently announced a host of new features that makes developers building on their platform lives more straightforward. You can read on that <a target="_blank" href="https://appwrite.io/init">here</a>. </p>
<h2 id="heading-how-to-set-up-the-appwrite-backend">How to Set Up the Appwrite Backend</h2>
<p>Before starting to build the application and interacting with Appwrite, you'll need an Appwrite account and to set up the project.</p>
<p>Once you have the account ready, you will need to create an organization, then create a project within that organization. You can name the project "Tasks App" or any other name you see fit. </p>
<p><strong>Note:</strong> Appwrite cloud restricts you to one organization per account on the hobby/free plan. If you already had an organization, you can go straight to creating a project within your existing organization.</p>
<p>In your Tasks App project, add a web platform and follow the prompts. For the hostname, add "localhost" for now. This is to allow the frontend to bypass <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a> when interacting with the Appwrite backend.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-08-at-14.44.11.png" alt="Image" width="600" height="400" loading="lazy">
<em>A picture of the Appwrite Console showing the "Add a platform" section</em></p>
<p>Copy the installation instructions as you complete setting up the web platform for the project. Keep these safe, you will need them when setting up the frontend. </p>
<p>You should now be in the Appwrite cloud console. Click on the "Databases" on the left sidebar. Then click on the pink "Create database" button. Name your database and leave the autogenerated ID as is.  </p>
<p>Now, click on the "Create collection" button, name your collection "tasks" and leave the autogenerated ID as is. Now, click on the grey "Create attribute" button as shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-08-at-15.00.42.png" alt="Image" width="600" height="400" loading="lazy">
<em>A picture of the Appwrite Console showing the "Create attribute" button</em></p>
<p>Add the following attributes: </p>
<ul>
<li>title of type String, give it a size of 49 and make it required</li>
<li>description of type String, give it a size of 200</li>
<li>due_date of type Datetime, make it required</li>
<li>done of type Boolean, give it a default of False</li>
<li>priority of type String, give it a size of 10</li>
</ul>
<p>Finally, you need to set permissions in order for your React frontend to interact with Appwrite services. In this case, allow any one to have access. This is not ideal for production and you can read more about Appwrite permissions <a target="_blank" href="https://appwrite.io/docs/advanced/platform/permissions">here</a>.</p>
<p>Go to the console, click on databases, then your task database and then your tasks collection, then click on settings and scroll down to permissions. Add permissions for "Any" role and give them full CRUD access.</p>
<p>You are now ready to start setting up the frontend and to connect it to the Appwrite project you just completed prepping.</p>
<h2 id="heading-how-to-set-up-the-react-frontend">How to Set up the React Frontend</h2>
<p>Open your text editor to your preferred location. Then open the integrated terminal and run the following command to create a Vite-based application: </p>
<pre><code class="lang-terminal">
//taskwrite is the name of the application
npm create vite@latest taskwrite
</code></pre>
<p>Choose React and then plain Typescript when prompted. This will create a React application with Typescript already set up for you. </p>
<p>Change folders into the newly created "taskwrite" one by running <code>cd taskwrite</code> from the terminal. Run the following command in the same terminal window to add Tailwind to the application:</p>
<pre><code class="lang-terminal">
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>Then in your <strong>tailwind.config.js</strong> file which is in the root of the Taskwrite application, replace the "content" key with <code>content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],</code>. The file should look like this:</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">content</span>: [<span class="hljs-string">"./index.html"</span>, <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>],
    <span class="hljs-attr">theme</span>: {
        <span class="hljs-attr">extend</span>: {},
    },
    <span class="hljs-attr">plugins</span>: [],
};
</code></pre>
<p>This tells Tailwind that it should look for its classes in the index.html file at the root and in files within the <strong>src</strong> folder that end with either <code>.js</code>, <code>.ts</code>, <code>.jsx</code> or <code>.tsx</code> extensions.</p>
<p>Then open the <strong>src</strong> folder and delete the "App.css" file. Open the <strong>index.css</strong> file and replace its contents with the following: </p>
<pre><code class="lang-css">
<span class="hljs-keyword">@import</span> url(<span class="hljs-string">'https://fonts.googleapis.com/css2?family=Inter:wght@100..900&amp;family=Quicksand:wght@300..700&amp;display=swap'</span>);
<span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-keyword">@layer</span> base {
    <span class="hljs-selector-pseudo">:root</span>{
        <span class="hljs-attribute">--base-bg</span>: <span class="hljs-number">#ffffff</span>;
        <span class="hljs-attribute">--btn-bg-primary</span>: <span class="hljs-number">#be185d</span>;
        <span class="hljs-attribute">--btn-bg-primary-hover</span>: <span class="hljs-number">#9d174d</span>;
        <span class="hljs-attribute">--btn-icon-main</span>: <span class="hljs-number">#1e293b</span>;
        <span class="hljs-attribute">--btn-bg-ok</span>: <span class="hljs-number">#4ade80</span>;
        <span class="hljs-attribute">--btn-bg-light-ok</span>: <span class="hljs-number">#bbf7d0</span>;
        <span class="hljs-attribute">--btn-bg-light</span>: <span class="hljs-number">#e5e7eb</span>;
        <span class="hljs-attribute">--low-priority</span>: <span class="hljs-number">#facc15</span>;
        <span class="hljs-attribute">--medium-priority</span>: <span class="hljs-number">#fb923c</span>;
        <span class="hljs-attribute">--high-priority</span>: <span class="hljs-number">#f87171</span>;
        <span class="hljs-attribute">--text-error</span>: <span class="hljs-number">#dc2626</span>;
        <span class="hljs-attribute">--text-ok</span>: <span class="hljs-number">#16a34a</span>;
        <span class="hljs-attribute">--text-main</span>: <span class="hljs-number">#262626</span>;
        <span class="hljs-attribute">--border-container</span>: <span class="hljs-number">#9ca3af</span>;
        <span class="hljs-attribute">--border-input</span>: <span class="hljs-number">#1e293b</span>;
        <span class="hljs-attribute">--border-error</span>: <span class="hljs-number">#dc2626</span>;
    }

    <span class="hljs-selector-tag">body</span>{
        <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--base-bg);
        <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-main);    
    }

    <span class="hljs-selector-id">#date</span><span class="hljs-selector-pseudo">::-webkit-calendar-picker-indicator</span> {
        <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--btn-bg-light); 
    }
}
</code></pre>
<p>This adds some custom css variables to the application. The variables map to Tailwind colors. </p>
<p>Next, paste the following into the <strong>tailwind.config.js</strong> at the root of the application:</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">/** @type {import('tailwindcss').Config} */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    content: [<span class="hljs-string">"./index.html"</span>, <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>],
    theme: {
        extend: {
            textColor: {
                error: <span class="hljs-string">"var(--text-error)"</span>,
                ok: <span class="hljs-string">"var(--text-ok)"</span>,
                main: <span class="hljs-string">"var(--text-main)"</span>,
                iconColor: <span class="hljs-string">"var(--btn-icon-main)"</span>,
            },
            backgroundColor: {
                base: <span class="hljs-string">"var(--base-bg)"</span>,
                primary: <span class="hljs-string">"var(--btn-bg-primary)"</span>,
                primaryHover: <span class="hljs-string">"var(--btn-bg-primary-hover)"</span>,
                ok: <span class="hljs-string">"var(--btn-bg-ok)"</span>,
                lightOk: <span class="hljs-string">"var(--btn-bg-light-ok)"</span>,
                light: <span class="hljs-string">"var(--btn-bg-light)"</span>,
                lowPriority: <span class="hljs-string">"var(--low-priority)"</span>,
                mediumPriority: <span class="hljs-string">"var(--medium-priority)"</span>,
                highPriority: <span class="hljs-string">"var(--high-priority)"</span>,
            },
            borderColor: {
                container: <span class="hljs-string">"var(--border-container)"</span>,
                input: <span class="hljs-string">"var(--border-input)"</span>,
                error: <span class="hljs-string">"var(--border-error)"</span>,
            },
        },
    },
    plugins: [],
};
</code></pre>
<p>This ties the CSS variables to the tailwind config and makes them available to use in our application.</p>
<p>Now Taskwrite is set up with React, Typescript and Tailwind.</p>
<h2 id="heading-how-to-connect-to-the-appwrite-project">How to Connect to the Appwrite Project</h2>
<p>Firstly, you need to add the Appwrite dependency to the React application. Run the following command in the terminal window to do that: <code>npm i appwrite</code>.</p>
<p>Next thing is to set up the Appwrite keys we need as environment variables. In the <strong>.gitignore</strong> file at the root of the application, add <code>*.env</code> at the top of the file then save. This will ensure that the <strong>.env</strong> file you'll create is not added to version control. </p>
<p>Now, create a <strong>.env</strong> file at the root of the React application and paste the following variables in it:</p>
<pre><code class="lang-env">
//replace the right hand side of the equal sign with the correct values from your Appwrite project.
VITE_APPWRITE_URL=YOUR-APPWRITE-API-ENDPOINT
VITE_APPWRITE_PROJ_ID=YOUR-APPWRITE-PROJECT-ID
</code></pre>
<p>You can get the necessary values in your Appwrite console. Click on the settings tab at the bottom of the left sidebar and copy API credentials. </p>
<p>Next, create a utils folder in the <strong>src</strong> folder of the React application. Add a file called <strong>appwrite.ts</strong> within it and paste the following config information:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { Client, Databases } <span class="hljs-keyword">from</span> <span class="hljs-string">"appwrite"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client();

client
    .setEndpoint(<span class="hljs-keyword">import</span>.meta.env.VITE_APPWRITE_URL)
    .setProject(<span class="hljs-keyword">import</span>.meta.env.VITE_APPWRITE_PROJ_ID);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> databases = <span class="hljs-keyword">new</span> Databases(client);

<span class="hljs-keyword">export</span> { ID } <span class="hljs-keyword">from</span> <span class="hljs-string">"appwrite"</span>;
</code></pre>
<p>You are ready to test that the React application is connected to the Appwrite project. Replace everything in the <strong>App.tsx</strong> file within the <strong>src</strong> folder with the following code:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/appwrite"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Appwrite"</span>, client);
    <span class="hljs-keyword">return</span> &lt;div className=<span class="hljs-string">"text-purple-500 text-center font-bold text-      5xl"</span>&gt;App&lt;/div&gt;;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Then open an integrated terminal window and run the following command: <code>npm run dev</code>. This will run your React application at this URL: <a target="_blank" href="http://localhost:5173/">http://localhost:5173/</a>. Open the URL in a browser window and open the browser console. </p>
<p>You should see a large purple text "App" in the center of the screen and the Appwrite client logged in the console like so: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-08-at-16.36.36.png" alt="Image" width="600" height="400" loading="lazy">
<em>The web app running in the browser</em></p>
<p>Now you need to grab the database ID and the collection ID from the Appwrite console. Click on the databases tab on the left sidebar, hover on the database ID value and click to copy it. </p>
<p>Go back to your <strong>.env</strong> file and add an entry like so:</p>
<pre><code class="lang-env">
//replace the right hand side of the equal sign with the correct values from your Appwrite project.
VITE_APPWRITE_URL=YOUR-APPWRITE-API-ENDPOINT
VITE_APPWRITE_PROJ_ID=YOUR-APPWRITE-PROJECT-ID
//new entry below
VITE_APPWRITE_DB_ID=YOUR-APPWRITE-DB-ID
</code></pre>
<p>Lastly, go back to the console and click through the database to get to the collections. Hover and copy collection ID like before, then add it just below the database ID in your env file like so: </p>
<pre><code class="lang-env">
VITE_APPWRITE_DB_ID=YOUR-APPWRITE-DB-ID
VITE_APPWRITE_COLLECTION_ID=YOUR-APPWRITE-COLLECTION-ID
</code></pre>
<p>With that, the set up part of building Taskwrite is complete.</p>
<h2 id="heading-how-to-build-the-task-manager-application">How to Build the Task Manager Application</h2>
<p>To make it easier to work with Typescript, you will need to add interfaces that correspond with the shape of the Appwrite database response. </p>
<p>In your <strong>src</strong> folder, create a folder called <strong>models</strong> and in it, create a file called <strong>interface.ts</strong>. Paste the following in the file:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { Models } <span class="hljs-keyword">from</span> <span class="hljs-string">"appwrite"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> IPayload {
    title: <span class="hljs-built_in">string</span>;
    description: <span class="hljs-built_in">string</span>;
    due_date: <span class="hljs-built_in">Date</span>;
    priority?: <span class="hljs-built_in">string</span>;
    done?: <span class="hljs-built_in">boolean</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> ITask <span class="hljs-keyword">extends</span> Models.Document {
    title: <span class="hljs-built_in">string</span>;
    description: <span class="hljs-built_in">string</span>;
    due_date: <span class="hljs-built_in">Date</span>;
    priority?: <span class="hljs-built_in">string</span>;
    done: <span class="hljs-built_in">boolean</span>;
}
</code></pre>
<p>Here, you are defining an interface called "IPayload" with the same attributes as the task we defined in the Appwrite project. Then you are defining another interface called "ITask" that extends the built in base Model from Appwrite. </p>
<p>This means that ITask has both the attributes of the task we defined before and the built in base attributes that Appwrite collections come with.</p>
<p>Next, in your <strong>utils</strong> folder add a file called <strong>db.ts</strong> and paste the following in it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { ID, databases } <span class="hljs-keyword">from</span> <span class="hljs-string">"./appwrite"</span>;
<span class="hljs-keyword">import</span> { IPayload } <span class="hljs-keyword">from</span> <span class="hljs-string">"../models/interface"</span>;

<span class="hljs-keyword">const</span> dbID: <span class="hljs-built_in">string</span> = <span class="hljs-keyword">import</span>.meta.env.VITE_APPWRITE_DB_ID;
<span class="hljs-keyword">const</span> collectionID: <span class="hljs-built_in">string</span> = <span class="hljs-keyword">import</span>.meta.env.VITE_APPWRITE_COLLECTION_ID;

<span class="hljs-keyword">const</span> createDocument = <span class="hljs-keyword">async</span> (payload: IPayload) =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> databases.createDocument(dbID, collectionID, ID.unique(), {
        ...payload,
    });

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

<span class="hljs-keyword">const</span> readDocuments = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> databases.listDocuments(dbID, collectionID);

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

<span class="hljs-keyword">const</span> updateDocument = <span class="hljs-keyword">async</span> (payload: IPayload, id: <span class="hljs-built_in">string</span>) =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> databases.updateDocument(dbID, collectionID, id, {
        ...payload,
    });

    <span class="hljs-keyword">return</span> res;
};
<span class="hljs-keyword">const</span> deleteDocument = <span class="hljs-keyword">async</span> (id: <span class="hljs-built_in">string</span>) =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> databases.deleteDocument(dbID, collectionID, id);

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

<span class="hljs-keyword">export</span> { createDocument, readDocuments, updateDocument, deleteDocument };
</code></pre>
<p>This file defines four functions corresponding with the CRUD operations. The naming of the functions map to which operation it performs. For all functions, you pass the collection and database IDs so that Appwrite knows which resources to operate on.</p>
<p>To create a task on the Appwrite database, you pass an object with the shape of a task to the function and ask it to create a unique ID for each new task it creates. </p>
<p>To update a task, you pass it a task object similar to create but we also pass it the unique ID of the task to be updated.</p>
<p>To read all tasks from the Appwrite, you call the "listDocuments" function and to delete a task you pass the ID corresponding to the task to be deleted.</p>
<h3 id="heading-how-to-set-up-routing-with-react-router-v6">How to Set Up Routing with React Router V6</h3>
<p>The Taskwrite application will have two routes and a navigation menu to help with that. To add navigation, open an integrated terminal and run the following command to install the React Router library: <code>npm i react-router-dom</code>.</p>
<p>Now, go to the <strong>main.tsx</strong> file in the <strong>src</strong> folder and paste the following in it: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> { BrowserRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.tsx"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;

ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)!).render(
    &lt;React.StrictMode&gt;
        &lt;BrowserRouter&gt;
            &lt;App /&gt;
        &lt;/BrowserRouter&gt;
    &lt;/React.StrictMode&gt;
);
</code></pre>
<p>Then go to the <strong>App.tsx</strong> file in <strong>src</strong> folder and paste the following in it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> { Route, Routes } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> Task <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/Task"</span>;
<span class="hljs-keyword">import</span> Index <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/Index"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar"</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> (
        &lt;&gt;
            &lt;Navbar/&gt;
            &lt;Routes&gt;
                &lt;Route path=<span class="hljs-string">"/"</span> element={&lt;Index /&gt;} /&gt;
                &lt;Route path=<span class="hljs-string">"/tasks"</span> element={&lt;Task /&gt;} /&gt;
            &lt;/Routes&gt;
        &lt;/&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>You now need to create the referenced component above. In the <strong>src</strong> folder, create a folder called <strong>routes</strong> and within it create two files called <strong>Index.tsx</strong> and <strong>Task.tsx</strong>.  </p>
<p>In <strong>Index.tsx</strong>, paste the following: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> Index = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        &lt;main className=<span class="hljs-string">"container mx-auto"</span>&gt;
            &lt;section className=<span class="hljs-string">"max-w-5xl mx-auto m-12 p-16"</span>&gt;
                &lt;h1 className=<span class="hljs-string">"text-4xl md:text-7xl font-bold text-center py-3 mb-16"</span>&gt;
                    AI-enhanced, Voice-enabled, Searchable Task Manager
                &lt;/h1&gt;
            &lt;/section&gt;
        &lt;/main&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Index;
</code></pre>
<p>And in <strong>Task.tsx</strong>, paste the following:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> Task = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        &lt;main className=<span class="hljs-string">"container mx-auto"</span>&gt;
            &lt;section className=<span class="hljs-string">"max-w-5xl mx-auto m-12 p-16"</span>&gt;
                &lt;h1 className=<span class="hljs-string">"text-4xl md:text-7xl font-bold text-center py-3 mb-16"</span>&gt;
                    Your Tasks
                &lt;/h1&gt;
            &lt;/section&gt;
        &lt;/main&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Task;
</code></pre>
<p>Now, create a components folder in the <strong>src</strong> folder and add a file within it called <strong>Navbar.tsx</strong>. Paste the following in that file: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { Link, useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> { PencilIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/24/solid"</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>;

<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> navigate = useNavigate();

    <span class="hljs-keyword">return</span> (
        &lt;nav className=<span class="hljs-string">"py-4 border-b-2 border-container shadow-md shadow-gray-400 w-full fixed top-0 bg-base"</span>&gt;
            &lt;ul className=<span class="hljs-string">"flex items-center justify-between  w-11/12 mx-auto"</span>&gt;
                &lt;Link to=<span class="hljs-string">"/"</span>&gt;
                    &lt;Button
                        content={{
                            text: <span class="hljs-string">"Taskwrite"</span>,
                            icon: PencilIcon,
                        }}
                        textClasses=<span class="hljs-string">"font-semibold text-main"</span>
                        iconClasses=<span class="hljs-string">"text-main"</span>
                    /&gt;
                &lt;/Link&gt;
                &lt;/Link&gt;
                &lt;div className=<span class="hljs-string">"flex items-center justify-between gap-6"</span>&gt;
                    &lt;Link
                        to=<span class="hljs-string">"/tasks"</span>
                        className=<span class="hljs-string">"font-semibold hover:scale-105 transition duration-300 ease-in-out"</span>
                    &gt;
                        View Tasks
                    &lt;/Link&gt;
                &lt;/div&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Navbar;
</code></pre>
<p>This file contains navigation menu that switches between the two pages. You will need to create the <code>Button</code> component referenced above and add the Hero icons package.   </p>
<p>In an integrated terminal, run the following to add Hero icons: <code>npm i @heroicons/react</code> . Next, add a new file called <strong>Button.tsx</strong> in the components folder. Paste the following within that file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ReactNode } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> ButtonProps {
    extraBtnClasses?: <span class="hljs-built_in">string</span>;
    textColor?: <span class="hljs-built_in">string</span>;
    handleClick?: <span class="hljs-function">(<span class="hljs-params">e: React.MouseEvent&lt;HTMLButtonElement&gt;</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
    title?: <span class="hljs-built_in">string</span>;
    disable?: <span class="hljs-built_in">boolean</span>;
    <span class="hljs-keyword">type</span>?: <span class="hljs-string">"button"</span> | <span class="hljs-string">"submit"</span> | <span class="hljs-string">"reset"</span>;
    children: ReactNode;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params">{
    extraBtnClasses,
    textColor,
    handleClick,
    title,
    disable,
    <span class="hljs-keyword">type</span> = "button",
    children,
}: ButtonProps</span>) </span>{
    <span class="hljs-keyword">const</span> handleClickProp = <span class="hljs-keyword">type</span> === <span class="hljs-string">"submit"</span> ? <span class="hljs-literal">undefined</span> : handleClick;

    <span class="hljs-keyword">return</span> (
        &lt;button
            <span class="hljs-keyword">type</span>={<span class="hljs-keyword">type</span>}
            title={title ?? <span class="hljs-string">""</span>}
            onClick={handleClickProp}
            disabled={disable}
            className={<span class="hljs-string">`flex gap-2 items-center text-iconColor <span class="hljs-subst">${extraBtnClasses}</span> <span class="hljs-subst">${
                textColor ?? <span class="hljs-string">""</span>
            }</span> rounded-md px-2 py-1 hover:scale-105 transition duration-300 ease-in-out`</span>}
        &gt;
            {children}
        &lt;/button&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>This file describes a shared button component and defines the props that it will accept.</p>
<p>Go back and fix any import errors and re-run the application by running <code>npm run dev</code>, you should see something like this: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-08-at-18.23.45.png" alt="Image" width="600" height="400" loading="lazy">
<em>app running with npm run dev command</em></p>
<h3 id="heading-how-to-create-the-form-component">How to Create the Form Component</h3>
<p>Add a new file called <strong>AddTask.tsx</strong> in the components folder and paste the following into it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Select <span class="hljs-keyword">from</span> <span class="hljs-string">"./Select"</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>;

<span class="hljs-keyword">const</span> AddTask = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [titleVal, setTitleVal] = useState(<span class="hljs-string">""</span>);
    <span class="hljs-keyword">const</span> [textAreaVal, setTextAreaVal] = useState(<span class="hljs-string">""</span>);
    <span class="hljs-keyword">const</span> [dueDate, setDueDate] = useState(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>());

    <span class="hljs-keyword">const</span> priorityArray = [<span class="hljs-string">"low"</span>, <span class="hljs-string">"medium"</span>, <span class="hljs-string">"high"</span>];

    <span class="hljs-keyword">const</span> [priority, setPriority] = useState(priorityArray[<span class="hljs-number">0</span>]);

    <span class="hljs-keyword">return</span> (
        &lt;form id=<span class="hljs-string">"form"</span> className=<span class="hljs-string">"m-8"</span>&gt;
            &lt;div className=<span class="hljs-string">"flex flex-col mb-6"</span>&gt;
                &lt;label htmlFor=<span class="hljs-string">"title"</span>&gt;Task Title&lt;/label&gt;
                &lt;input
                    <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
                    id=<span class="hljs-string">"title"</span>
                    placeholder=<span class="hljs-string">"Title of your task"</span>
                    value={titleVal}
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setTitleVal(e.target.value)}
                    className=<span class="hljs-string">"bg-inherit border rounded-sm p-2 focus:outline-none focus:ring-1 border-input focus:ring-slate-900"</span>
                /&gt;
            &lt;/div&gt;
            &lt;div className=<span class="hljs-string">"flex flex-col mb-6"</span>&gt;
                &lt;label htmlFor=<span class="hljs-string">"description"</span> className=<span class="hljs-string">"mb-1"</span>&gt;
                    Task Description
                &lt;/label&gt;
                &lt;textarea
                    id=<span class="hljs-string">"description"</span>
                    placeholder=<span class="hljs-string">"Describe your task"</span>
                    maxLength={<span class="hljs-number">200</span>}
                    value={textAreaVal}
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setTextAreaVal(e.target.value)}
                    className=<span class="hljs-string">"bg-inherit border rounded-sm p-2 h-32 resize-none focus:outline-none focus:ring-1 border-input focus:ring-slate-900"</span>
                /&gt;
            &lt;/div&gt;
            &lt;div className=<span class="hljs-string">"flex flex-col mb-6"</span>&gt;
                &lt;label htmlFor=<span class="hljs-string">"description"</span> className=<span class="hljs-string">"mb-1"</span>&gt;
                    Task Priority
                &lt;/label&gt;
                &lt;Select
                    defaultSelectValue={priority}
                    selectOptions={priorityArray}
                    handleSelectChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setPriority(e.target.value)}
                /&gt;
            &lt;/div&gt;
            &lt;div className=<span class="hljs-string">"flex flex-col mb-6"</span>&gt;
                &lt;label htmlFor=<span class="hljs-string">"description"</span> className=<span class="hljs-string">"mb-1"</span>&gt;
                    Task Due <span class="hljs-built_in">Date</span>
                &lt;/label&gt;
                &lt;input
                    <span class="hljs-keyword">type</span>=<span class="hljs-string">"date"</span>
                    id=<span class="hljs-string">"date"</span>
                    value={dueDate!.toISOString().split(<span class="hljs-string">"T"</span>)[<span class="hljs-number">0</span>]}
                    min={<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString().split(<span class="hljs-string">"T"</span>)[<span class="hljs-number">0</span>]}
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setDueDate(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(e.target.value))}
                    className=<span class="hljs-string">"bg-inherit border rounded-sm border-input p-2 focus:outline-none focus:ring-1 focus:ring-slate-900 invalid:focus:ring-red-600"</span>
                /&gt;
            &lt;/div&gt;
            &lt;Button
                <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
                content={{
                    text: <span class="hljs-string">"Add Task"</span>,
                }}
                extraBtnClasses=<span class="hljs-string">"bg-pink-700 justify-center text-white font-semibold px-4 py-2 outline-1 hover:bg-pink-800 focus:ring-1 focus:ring-pink-800 w-full"</span>
            /&gt;
        &lt;/form&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AddTask;
</code></pre>
<p>Now create a new file in components called <strong>Select.tsx</strong>, paste the following in it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> SelectProps {
    defaultSelectValue: <span class="hljs-built_in">string</span>;
    selectOptions: <span class="hljs-built_in">string</span>[];
    handleSelectChange: <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLSelectElement&gt;</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> Select = <span class="hljs-function">(<span class="hljs-params">{
    defaultSelectValue,
    handleSelectChange,
    selectOptions,
}: SelectProps</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [selectVal, setSelectVal] = useState(defaultSelectValue);
    <span class="hljs-keyword">return</span> (
        &lt;select
            value={selectVal}
            onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
                setSelectVal(e.target.value);
                handleSelectChange(e);
            }}
            className=<span class="hljs-string">"bg-inherit border rounded-sm border-input p-2 focus:outline-none focus:ring-1 focus:ring-slate-900 cursor-pointer"</span>
        &gt;
            {selectOptions.map(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span> (
                &lt;option key={option} value={option}&gt;
                    {option.charAt(<span class="hljs-number">0</span>).toUpperCase() + option.slice(<span class="hljs-number">1</span>)}
                &lt;/option&gt;
            ))}
        &lt;/select&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Select;
</code></pre>
<p>This defines a <code>Select</code> component and its props. The <code>Select</code> component props are a function for handling change, an array of options and the default value it should display. </p>
<p>Now, import the <code>AddTask</code> component in the <strong>Index.tsx</strong> file between the <code>h1</code> tags like so: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> AddTask <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/AddTask"</span>;

<span class="hljs-keyword">const</span> Index = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        &lt;main className=<span class="hljs-string">"container mx-auto"</span>&gt;
            &lt;section className=<span class="hljs-string">"max-w-5xl mx-auto m-12 p-16"</span>&gt;
                &lt;h1 className=<span class="hljs-string">"text-4xl md:text-7xl font-bold text-center py-3 mb-16"</span>&gt;
                    AI-enhanced, Voice-enabled, Searchable Task Manager
                &lt;/h1&gt;
                &lt;AddTask /&gt;
            &lt;/section&gt;
        &lt;/main&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Index;
</code></pre>
<p>Your application should now display the form:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-08-at-18.57.25.png" alt="Image" width="600" height="400" loading="lazy">
<em>task app form</em></p>
<h3 id="heading-how-to-set-up-form-to-create-task">How to Set Up Form to Create Task</h3>
<p>To make the form functional, you need to hook it up to a submit function that will call the create function defined in the <strong>db.ts</strong> file. </p>
<p>Additionally, you will need to validate the form to avoid sending bad data and having Appwrite send errors back to the React application.</p>
<p>In the <code>AddTask</code> component file, paste the following code above the <code>return</code> statement and below the <code>setPriority</code> useState:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [priority, setPriority] = useState(priorityArray[<span class="hljs-number">0</span>]);

<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> navigate = useNavigate();

<span class="hljs-keyword">const</span> [isSubmitting, setIsSubmitting] = useState(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">const</span> [titleValidationError, setTitleValidationError] = useState(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">const</span> handleTitleChange = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
        setTitleVal(e.target.value);

        <span class="hljs-keyword">if</span> (e.target.value.trim() !== <span class="hljs-string">""</span>) {
            setTitleValidationError(<span class="hljs-string">""</span>);
        }
    };

    <span class="hljs-keyword">const</span> handleSubmitTask = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
        e.preventDefault();
        setIsSubmitting(<span class="hljs-literal">true</span>);

        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">if</span> (!titleVal) {
                setTitleValidationError(<span class="hljs-string">"Please provide at least a title for the task"</span>);
                <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> setTitleValidationError(<span class="hljs-string">""</span>), <span class="hljs-number">2000</span>);
                setIsSubmitting(<span class="hljs-literal">false</span>);
                <span class="hljs-keyword">return</span>;
            }

            <span class="hljs-keyword">if</span> (titleVal.length &gt; <span class="hljs-number">49</span>) {
                setTitleValidationError(
                    <span class="hljs-string">"Title too long. It can only be 49 characters long"</span>
                );
                <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> setTitleValidationError(<span class="hljs-string">""</span>), <span class="hljs-number">2000</span>);
                setIsSubmitting(<span class="hljs-literal">false</span>);
                <span class="hljs-keyword">return</span>;
            }

            <span class="hljs-keyword">const</span> payload: IPayload = {
                title: titleVal,
                description: textAreaVal,
                due_date: dueDate,
                priority: priority,
            };

            <span class="hljs-keyword">await</span> createDocument(payload);

            <span class="hljs-comment">// reset form</span>
            setTitleVal(<span class="hljs-string">""</span>);
            setTextAreaVal(<span class="hljs-string">""</span>);
            setDueDate(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>());
            setPriority(priorityArray[<span class="hljs-number">0</span>]);
            setTitleValidationError(<span class="hljs-string">""</span>);
            setIsSubmitting(<span class="hljs-literal">false</span>);
            navigate(<span class="hljs-string">"/tasks"</span>);
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error in handleSubmitTask:"</span>, error);
            setIsSubmitting(<span class="hljs-literal">false</span>);
        }
    };

    <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">//rest of the code unchanged below</span>
</code></pre>
<p>Then replace the <code>return</code> statement with the following code:</p>
<pre><code>
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmitTask}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"m-8"</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 mb-6"</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>Task Title<span class="hljs-tag">&lt;/<span class="hljs-name">label</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">id</span>=<span class="hljs-string">"title"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Title of your task"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{titleVal}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleTitleChange}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">bg-inherit</span> <span class="hljs-attr">border</span> <span class="hljs-attr">rounded-sm</span> <span class="hljs-attr">p-2</span> <span class="hljs-attr">focus:outline-none</span>                         <span class="hljs-attr">focus:ring-1</span> ${
                    <span class="hljs-attr">titleValidationError</span>
                    ? "<span class="hljs-attr">border-error</span> <span class="hljs-attr">focus:ring-red-500</span> <span class="hljs-attr">invalid:focus:ring-red-</span>                         <span class="hljs-attr">600</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">border-input</span> <span class="hljs-attr">focus:ring-slate-900</span>"
            }`}
        /&gt;</span>
        {titleValidationError &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-error mt-1"</span>&gt;</span>{titleValidationError}<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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col mb-6"</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-1"</span>&gt;</span>
            Task Description
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Describe your task"</span>
            <span class="hljs-attr">maxLength</span>=<span class="hljs-string">{200}</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{textAreaVal}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTextAreaVal(e.target.value)}
            className={`bg-inherit border rounded-sm p-2 h-32 resize-none                             focus:outline-none focus:ring-1 ${
                        textAreaVal.length &gt; 197
                        ? "border-error focus:ring-red-500 invalid:focus:ring-                             red-600"
                        : "border-input focus:ring-slate-900"
                }`}
        /&gt;
        {textAreaVal.length &gt; 197 &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-error mt-1"</span>&gt;</span>
            Warning description getting too long. Can only be 200 characters
        <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col mb-6"</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-1"</span>&gt;</span>
            Task Priority
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Select</span>
            <span class="hljs-attr">defaultSelectValue</span>=<span class="hljs-string">{priority}</span>
            <span class="hljs-attr">selectOptions</span>=<span class="hljs-string">{priorityArray}</span>
            <span class="hljs-attr">handleSelectChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setPriority(e.target.value)}
        /&gt;
    <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">"flex flex-col mb-6"</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-1"</span>&gt;</span>
            Task Due Date
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"date"</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"date"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{dueDate!.toISOString().split(</span>"<span class="hljs-attr">T</span>")[<span class="hljs-attr">0</span>]}
            <span class="hljs-attr">min</span>=<span class="hljs-string">{new</span> <span class="hljs-attr">Date</span>()<span class="hljs-attr">.toISOString</span>()<span class="hljs-attr">.split</span>("<span class="hljs-attr">T</span>")[<span class="hljs-attr">0</span>]}
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setDueDate(new Date(e.target.value))}
                className="bg-inherit border rounded-sm border-input p-2                                      focus:outline-none focus:ring-1 focus:ring-slate-                               900 invalid:focus:ring-red-600"
        /&gt;
    <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">type</span>=<span class="hljs-string">"submit"</span>
        <span class="hljs-attr">disable</span>=<span class="hljs-string">{isSubmitting}</span>
        <span class="hljs-attr">extraBtnClasses</span>=<span class="hljs-string">"bg-primary justify-center text-white font-semibold px-4 py-2 outline-1 hover:bg-primaryHover focus:ring-1 focus:ring-pink-800 w-full"</span>
    &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
            Add Task
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</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">form</span>&gt;</span></span>
);
</code></pre><p>Fix import errors and your application should now be validating title and description as well as creating the task then sending you to the "/tasks" route. You can check the Appwrite console to confirm that the task has been created. </p>
<h3 id="heading-how-set-up-read-and-delete-tasks">How Set Up Read and Delete Tasks</h3>
<p>Open the <strong>Task.jsx</strong> file in the routes folder within <strong>src</strong> folder, and add the following code above the <code>return</code> like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [tasks, setTasks] = useState&lt;ITask[]&gt;([]);
<span class="hljs-keyword">const</span> [tasksError, setTasksError] = useState(<span class="hljs-string">""</span>);

useEffect(<span class="hljs-function">() =&gt;</span> {
        getTasks()
        .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
        setTasks(res.reverse());
        })
        .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.error(err);
        setTasksError(<span class="hljs-string">"Error fetching tasks, please try again"</span>);
        });
    }, []);

<span class="hljs-keyword">return</span> (
<span class="hljs-comment">//rest of code</span>
</code></pre>
<p>Here, the file is setting some local state using useState to hold the tasks and set any potential task related errors.</p>
<p>Now replace the code in the <code>return</code> with the following code:</p>
<pre><code class="lang-typescript">

&lt;main className=<span class="hljs-string">"container mx-auto"</span>&gt;
    &lt;section className=<span class="hljs-string">"max-w-5xl mx-auto m-12 p-16"</span>&gt;
        &lt;h1 className=<span class="hljs-string">"text-4xl md:text-7xl font-bold text-center py-3 mb-16"</span>&gt;
        Your Tasks
        &lt;/h1&gt;
        {tasksError ? (
            &lt;span className=<span class="hljs-string">"m-8 text-error"</span>&gt;{tasksError}&lt;/span&gt;
        ) : (
            &lt;div className=<span class="hljs-string">"flex flex-col md:flex-row justify-between"</span>&gt;
                &lt;div className=<span class="hljs-string">"flex-1"</span>&gt;
                    &lt;h3 className=<span class="hljs-string">"text-2xl font-bold m-8"</span>&gt;Pending Tasks&lt;/h3&gt;
                    &lt;div&gt;
                         {tasks
                             .filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> !task.done)
                             .map(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> (
                                &lt;TaskItem key={task.$id} task={task} /&gt;
                         ))}
                    &lt;/div&gt;
                &lt;/div&gt;
                &lt;div className=<span class="hljs-string">"flex-1"</span>&gt;
                    &lt;h3 className=<span class="hljs-string">"text-2xl font-bold m-8"</span>&gt;Completed Tasks&lt;/h3&gt;
                    &lt;div&gt;
                        {tasks
                            .filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> task.done)
                            .map(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> (
                                &lt;TaskItem key={task.$id} task={task} /&gt;
                        ))}
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        )}
    &lt;/section&gt;
&lt;/main&gt;
</code></pre>
<p>You now need to create the <code>getTasks()</code> function and the <code>TaskItem</code> component. In the components folder, create a file called <strong>TaskItem.tsx</strong> and paste the following code in it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> TaskItemProps {
    task: ITask;
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TaskItem</span>(<span class="hljs-params">{ task }: TaskItemProps</span>) </span>{
    <span class="hljs-keyword">return</span> (
    &lt;&gt;
        &lt;div className=<span class="hljs-string">"m-8 cursor-pointer border border-container rounded-md p-4 hover:shadow-lg transition duration-300 ease-in-out max-h-96"</span>&gt;
            &lt;section
            key={task.$id}
            className=<span class="hljs-string">"flex flex-col justify-between gap-2 my-4 h-full"</span>
            &gt;
            &lt;section className=<span class="hljs-string">"flex gap-4 items-center justify-between flex-wrap"</span>&gt;
                {task.priority &amp;&amp; (
                &lt;span&gt;
                    &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;Priority: &lt;/span&gt;
                        &lt;span
                            className={<span class="hljs-string">`<span class="hljs-subst">${
                            task.priority === <span class="hljs-string">"low"</span>
                            ? <span class="hljs-string">"bg-lowPriority text-iconColor"</span>
                            : task.priority === <span class="hljs-string">"medium"</span>
                            ? <span class="hljs-string">"bg-mediumPriority text-iconColor"</span>
                            : <span class="hljs-string">"bg-highPriority text-iconColor"</span>
                            }</span> py-1 px-2 rounded-md`</span>}
                        &gt;
                            {task.priority}
                        &lt;/span&gt;
                &lt;/span&gt;
                )}
                &lt;div className=<span class="hljs-string">"flex gap-2 py-1 ml-auto"</span>&gt;
                    &lt;Button
                        handleClick={<span class="hljs-function">() =&gt;</span> handleEdit(task)}
                        extraBtnClasses=<span class="hljs-string">"bg-ok"</span>
                    &gt;
                        &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;Edit&lt;/span&gt;
                        &lt;PencilSquareIcon height={<span class="hljs-number">25</span>} className=<span class="hljs-string">"hidden lg:flex"</span> /&gt;
                    &lt;/Button&gt;
                    &lt;Button
                        handleClick={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> handleDelete(e, task.$id)}
                        extraBtnClasses=<span class="hljs-string">"bg-highPriority"</span>
                    &gt;
                        &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;Delete&lt;/span&gt;
                        &lt;TrashIcon height={<span class="hljs-number">25</span>} className=<span class="hljs-string">"hidden lg:flex"</span> /&gt;
                    &lt;/Button&gt;
                &lt;/div&gt;
            &lt;/section&gt;
            &lt;section className=<span class="hljs-string">""</span>&gt;
                &lt;h2 className=<span class="hljs-string">"text-xl font-medium py-2 break-words"</span>&gt;
                    {task.title}
                &lt;/h2&gt;
                &lt;p className=<span class="hljs-string">"py-1 mb-4 min-h-16 break-words"</span>&gt;
                    {task.description.length &gt; <span class="hljs-number">70</span>
                        ? task.description.substring(<span class="hljs-number">0</span>, <span class="hljs-number">70</span>) + <span class="hljs-string">"..."</span>
                        : task.description}
                &lt;/p&gt;
                &lt;span className=<span class="hljs-string">"font-extralight mt-2"</span>&gt;
                    &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;Due on: &lt;/span&gt;
                        &lt;span className=<span class="hljs-string">"underline"</span>&gt;{<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(
                            task.due_date
                        ).toLocaleDateString()}</span>`</span>}
                    &lt;/span&gt;
                &lt;/span&gt;
                &lt;/section&gt;
                &lt;section className=<span class="hljs-string">"flex justify-between"</span>&gt;
                    {task.done ? (
                        &lt;span className=<span class="hljs-string">"items-center text-ok font-bol ml-auto"</span>&gt;
                            Completed
                        &lt;/span&gt;
                    ) : (
                    &lt;div className=<span class="hljs-string">"flex items-center ml-auto hover:scale-105 transition duration-300 ease-in-out"</span>&gt;
                        &lt;label htmlFor=<span class="hljs-string">"done"</span> className=<span class="hljs-string">"mr-2 font-light"</span>&gt;
                            Mark <span class="hljs-keyword">as</span> complete
                        &lt;/label&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"checkbox"</span>
                            checked={isDone}
                            onClick={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> e.stopPropagation()}
                            onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
                            setIsDone(e.target.checked);
                            handleCheckbox(task, task.$id, e);
                        }}
                            className=<span class="hljs-string">"size-5 accent-pink-600 rounded-sm"</span>
                    /&gt;
                &lt;/div&gt;
                )}
                &lt;/section&gt;
            &lt;/section&gt;
        &lt;/div&gt;
    &lt;/&gt;
    );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TaskItem;
</code></pre>
<p>This gives the file some markup to display. It divides the page into two columns, one for the pending tasks and one for the completed tasks, and it handles responsiveness of the page.</p>
<p>In order to get rid of the errors, paste the following code just before the <code>return</code> statement like so: </p>
<pre><code class="lang-typescript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TaskItem</span>(<span class="hljs-params">{ task }: TaskItemProps</span>) </span>{

<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> [isDone, setIsDone] = useState(<span class="hljs-literal">false</span>);

<span class="hljs-keyword">const</span> handleDelete = <span class="hljs-keyword">async</span> (
        currentTaskId: <span class="hljs-built_in">string</span>
    ) =&gt; {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">await</span> deleteDocument(currentTaskId);
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(error);
        }
};

<span class="hljs-keyword">const</span> handleCheckbox = <span class="hljs-keyword">async</span> (
        currentTask: IPayload,
        id: <span class="hljs-built_in">string</span>,
        checkedVal: <span class="hljs-built_in">boolean</span>
    ) =&gt; {
        <span class="hljs-keyword">if</span> (!checkedVal) <span class="hljs-keyword">return</span>;

        <span class="hljs-keyword">const</span> payload: IPayload = {
        title: currentTask.title,
        description: currentTask.description,
        due_date: currentTask.due_date,
        priority: currentTask.priority,
        done: checkedVal,
        };

        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">await</span> updateDocument(payload, id);
        } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.error(error);
        }
};

<span class="hljs-comment">//rest of code below untouched</span>
<span class="hljs-keyword">return</span> (
......
</code></pre>
<p>This adds the ability to delete a task item and the ability to mark it as complete. </p>
<p>Create a new file in the <strong>utils</strong> folder and call it <strong>shared.ts</strong>. This file will house any function that will be called in more than two places in the application. </p>
<p>The <code>getTasks</code> function is one such repetitive function, so it will be placed in the <strong>shared.ts</strong> file. Paste the following code into it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { readDocuments } <span class="hljs-keyword">from</span> <span class="hljs-string">"./db"</span>;
<span class="hljs-keyword">import</span> { ITask } <span class="hljs-keyword">from</span> <span class="hljs-string">"../models/interface"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getTasks = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { documents } = <span class="hljs-keyword">await</span> readDocuments();

    <span class="hljs-keyword">return</span> documents <span class="hljs-keyword">as</span> ITask[];
};
</code></pre>
<p>This defines the function and returns an array of <code>ITasks</code>. Go back to the <strong>Task.tsx</strong> file and fix any import errors. </p>
<p>Run the application and you should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-09-at-00.10.56.png" alt="Image" width="600" height="400" loading="lazy">
<em>your pending and completed tasks displayed in the browser</em></p>
<p>The task can be deleted or marked as complete but you won't see an update on the UI until the page is refreshed. To fix that, go back to the <strong>TaskItem</strong> file and paste the following code below the <code>isDone</code> useState and above the <code>handleDelete</code> function:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [isDone, setIsDone] = useState(<span class="hljs-literal">false</span>);

<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> updateTasks = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> allTasks = <span class="hljs-keyword">await</span> getTasks();
        <span class="hljs-keyword">if</span> (setTasks) setTasks(allTasks.reverse());
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
    }
};

<span class="hljs-comment">//rest of code below remains as is</span>
<span class="hljs-keyword">const</span> handleDelete = <span class="hljs-keyword">async</span> (
</code></pre>
<p>Update <code>TaskItem</code> props interface and the <code>TaskItem</code> function like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> TaskItemProps {
    task: ITask;
    setTasks?: <span class="hljs-function">(<span class="hljs-params">tasks: ITask[]</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TaskItem</span>(<span class="hljs-params">{ task, setTasks }: TaskItemProps</span>) </span>{
<span class="hljs-comment">//rest of code below</span>
    <span class="hljs-keyword">const</span> [isDone, setIsDone] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<p>This gives a setter function that resets the tasks array as a prop to the <code>TaskItem</code> component. </p>
<p>Adjust the <code>handleDelete</code> and <code>handleCheckbox</code> functions in the <code>TaskItem</code> component to include the <code>updateTasks</code> function you added above. it should read like this:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> handleDelete = <span class="hljs-keyword">async</span> (
    e: React.MouseEvent&lt;HTMLButtonElement&gt;,
    currentTaskId: <span class="hljs-built_in">string</span>
) =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> deleteDocument(currentTaskId);
        updateTasks();
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
    }
};

<span class="hljs-keyword">const</span> handleCheckbox = <span class="hljs-keyword">async</span> (
    currentTask: IPayload,
    id: <span class="hljs-built_in">string</span>,
    e: React.ChangeEvent&lt;HTMLInputElement&gt;
    ) =&gt; {

    <span class="hljs-keyword">const</span> payload: IPayload = {
        title: currentTask.title,
        description: currentTask.description,
        due_date: currentTask.due_date,
        priority: currentTask.priority,
        done: e.target.checked,
    };

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> updateDocument(payload, id);
        updateTasks();
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
    }
};
</code></pre>
<p>Go back to the <strong>Task.tsx</strong> file and pass <code>setTasks</code> to the <code>TaskItem</code> component like so: <code>&lt;TaskItem key={task.$id} task={task} setTasks={setTasks} /&gt;</code>. Now, the UI updates without needing to manually refresh the page.</p>
<h3 id="heading-how-to-make-the-tasks-editable">How to Make the Tasks Editable</h3>
<p>In order to edit a task, you will need to pass a function to the edit button in the <code>TaskItem</code> component. </p>
<p>Paste the following code in the <strong>TaskItem.tsx</strong> file between the <code>updateTasks</code> and <code>handleDelete</code> functions like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">//paste below updateTasks</span>
<span class="hljs-keyword">const</span> handleEdit = <span class="hljs-keyword">async</span> (
    currentTask: ITask
) =&gt; {
    navigate(<span class="hljs-string">"/"</span>, { state: { task: currentTask } });
};

<span class="hljs-comment">//rest of code untouched below</span>
</code></pre>
<p>Add this line right above the <code>isDone</code> useState: <code>const navigate = useNavigate();</code>. </p>
<p>In the same file, find the edit button and pass the <code>handleEdit</code> function to it. Also, wrap it in a condition that checks if the task is done such that the button is only displayed in the case that the task is not marked as complete. Like so:</p>
<pre><code class="lang-typescript">{!task.done &amp;&amp; (
    &lt;Button
        handleClick={<span class="hljs-function">() =&gt;</span> handleEdit(task)}
        extraBtnClasses=<span class="hljs-string">"bg-ok"</span>
    &gt;
        &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;Edit&lt;/span&gt;
        &lt;PencilSquareIcon height={<span class="hljs-number">25</span>} className=<span class="hljs-string">"hidden lg:flex"</span> /&gt;
    &lt;/Button&gt;
)
</code></pre>
<p>The <code>AddTask</code> component has to be adjusted to handle editing a task and the <strong>Index.tsx</strong> file needs to be updated to handle the task to be edited being passed to it via the navigate <code>handleEdit</code> function.</p>
<p>First, go to the <strong>AddTask</strong> file and add some prop definitions directly below the import statements, then pass the new props to the component like so:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span>....

<span class="hljs-comment">// pass a task and an isEdit boolean</span>
<span class="hljs-comment">// if isEdit is true, then the form will be populated with the task's data</span>
<span class="hljs-keyword">interface</span> ITaskFormProps {
    task: ITask | <span class="hljs-literal">null</span>;
    isEdit?: <span class="hljs-built_in">boolean</span>;
    setTasks?: <span class="hljs-function">(<span class="hljs-params">tasks: ITask[]</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-comment">//pass component props</span>
<span class="hljs-keyword">const</span> AddTask = <span class="hljs-function">(<span class="hljs-params">{ task, isEdit, setTasks }: ITaskFormProps</span>) =&gt;</span> {
<span class="hljs-comment">//code untouched below</span>
</code></pre>
<p>Adjust the due date and priority useStates to read like below:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [dueDate, setDueDate] = useState(
    isEdit &amp;&amp; task?.due_date ? <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(task.due_date) : <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
);

<span class="hljs-keyword">const</span> [priority, setPriority] = useState(
    isEdit &amp;&amp; task?.priority ? task?.priority : priorityArray[<span class="hljs-number">0</span>]
);
</code></pre>
<p>Add a useEffect below the useStates like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [titleValidationError, setTitleValidationError] = useState(<span class="hljs-string">""</span>);

<span class="hljs-comment">//paste below useState statements</span>
useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (isEdit &amp;&amp; task) {
        setTitleVal(task.title);
        setTextAreaVal(task.description);
    } <span class="hljs-keyword">else</span> {
        setTitleVal(<span class="hljs-string">""</span>);
    }
}, [isEdit, task]);
</code></pre>
<p>In the <code>handleSubmit</code> function within the same <strong>AddTask</strong> file, delete this line: <code>await createDocument(payload);</code> and replace with the below:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">if</span> (isEdit &amp;&amp; task) {
    <span class="hljs-keyword">await</span> updateDocument(payload, task!.$id);
    <span class="hljs-keyword">const</span> allTasks = <span class="hljs-keyword">await</span> getTasks();
    <span class="hljs-keyword">if</span> (setTasks) <span class="hljs-keyword">return</span> setTasks(allTasks.reverse());
} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">await</span> createDocument(payload);
}
</code></pre>
<p>Now replace the <code>Button</code> component at the bottom of the file just above the form closing tag with this:</p>
<pre><code class="lang-typescript">
&lt;Button
    <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
    disable={isSubmitting}
    extraBtnClasses=<span class="hljs-string">"bg-primary justify-center text-white font-semibold px-4 py-2 outline-1 hover:bg-primaryHover focus:ring-1 focus:ring-pink-800 w-full"</span>
&gt;
    &lt;span&gt;
        {isSubmitting ? <span class="hljs-string">"Submitting..."</span> : task ? <span class="hljs-string">"Edit Task"</span> : <span class="hljs-string">"Add Task"</span>}
    &lt;/span&gt;
&lt;/Button&gt;
<span class="hljs-comment">//unchanged code below</span>
&lt;/form&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AddTask;
</code></pre>
<p>This sets the text on the button depending on whether the form is submitting, creating a new task or updating an exisiting task.</p>
<p>Go to the <strong>Index.tsx</strong> file in the <strong>routes</strong> folder and paste the following above the <code>return</code> statement:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> Index = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">//paste here</span>
    <span class="hljs-keyword">const</span> location = useLocation();
    <span class="hljs-keyword">const</span> navigate = useNavigate();

    <span class="hljs-keyword">const</span> taskFromState: ITask = location.state?.task;

    <span class="hljs-keyword">const</span> [taskToEdit] = useState&lt;ITask | <span class="hljs-literal">null</span>&gt;(taskFromState ?? <span class="hljs-literal">null</span>);

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (taskFromState) {
            navigate(location.pathname, {});
        }
    }, [taskFromState, location.pathname, navigate]);

    <span class="hljs-comment">//below code remains unchanged</span>
    <span class="hljs-keyword">return</span> (....
</code></pre>
<p>Here, the file gets the task passed to it from the "/tasks" route and sets it to local state. Then the useEffect nullies the passed task so that the form is reset on refresh.</p>
<p>In the <strong>Index.tsx</strong> file replace the <code>AddTask</code> component with this line: <code>&lt;AddTask task={taskToEdit} isEdit={taskToEdit ? true : false} /&gt;</code>.</p>
<p>Run your application and you should be able to click on the edit button, be navigated back to the "/" route, have the form pre-filled with the task details, be able to edit some of the fields and be redirected back to "/tasks" once you click on the "Edit Task" button. </p>
<h3 id="heading-how-to-enable-viewing-of-tasks">How to Enable Viewing of Tasks</h3>
<p>The application now creates, reads, updates and deletes tasks. All that is left on that is the ability to view a particular task. </p>
<p>Go to the <strong>TaskItem</strong> file, add the following to the interface <code>TaskItemProps</code>: <code>isViewTask: boolean;    handleViewTask?: (e: React.MouseEvent&lt;HTMLDivElement&gt;) =&gt; void;</code>.</p>
<p>Add them as props to the <code>TaskItem</code> component and set <code>isViewTask</code> to a default of <code>false</code> like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TaskItem</span>(<span class="hljs-params">{
    task,
    setTasks,
    isViewTask = <span class="hljs-literal">false</span>,
    handleViewTask,
}: TaskItemProps</span>) </span>{    
    <span class="hljs-comment">//rest of code below unchanged</span>
    <span class="hljs-keyword">const</span> navigate = useNavigate();
</code></pre>
<p>Replace the paragraph tag that displays the task description within the <code>return</code> of the component with this markup:</p>
<pre><code class="lang-typescript">
&lt;p className=<span class="hljs-string">"py-1 mb-4 min-h-16 break-words"</span>&gt;
    {task.description.length &gt; <span class="hljs-number">70</span> &amp;&amp; !isViewTask
        ? task.description.substring(<span class="hljs-number">0</span>, <span class="hljs-number">70</span>) + <span class="hljs-string">"..."</span>
        : task.description
    }
&lt;/p&gt;
</code></pre>
<p>The change introduced will ensure that the full description is visible if <code>isViewTask</code> is set to <code>true</code>.</p>
<p>On the <code>div</code> tag just below the <code>return</code> in the same component, add an <code>onClick</code> handler like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">return</span> (
&lt;&gt;
    &lt;div
        className=<span class="hljs-string">"m-8 cursor-pointer border border-container rounded-md p-4                        hover:shadow-lg transition duration-300 ease-in-out max-h-                       96"</span>
        onClick={handleViewTask}
    &gt;
<span class="hljs-comment">//rest unchanged code</span>
...
</code></pre>
<p>Back in the <strong>Task.tsx</strong> file, paste the following just above the useEffect:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> handleViewTask = <span class="hljs-function">(<span class="hljs-params">
    e: React.MouseEvent&lt;HTMLDivElement&gt;,
    activeTask: ITask
</span>) =&gt;</span> {
    setIsViewTask(<span class="hljs-literal">true</span>);
    setSelectedTask(activeTask);
};

<span class="hljs-comment">//unchanged code below</span>
useEffect(...
</code></pre>
<p>Add the following useState functions above the <code>handleViewTask</code> function, below the other useStates:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [tasksError, setTasksError] = useState(<span class="hljs-string">""</span>);
<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> [isViewTask, setIsViewTask] = useState(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">const</span> [selectedTask, setSelectedTask] = useState&lt;ITask&gt;();

<span class="hljs-comment">//code below unchanged</span>
<span class="hljs-keyword">const</span> handleViewTask = (...
</code></pre>
<p>Now paste the following code within the <code>return</code> statement in the same file, just above the <code>h1</code> tag displaying "Your Tasks" text:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">return</span> (
&lt;main className=<span class="hljs-string">"container mx-auto"</span>&gt;
    &lt;section className=<span class="hljs-string">"max-w-5xl mx-auto m-12 p-16"</span>&gt;
        <span class="hljs-comment">//paste here</span>
        {isViewTask &amp;&amp; selectedTask &amp;&amp; (
            &lt;Dialog key={selectedTask.$id} setIsViewTask={setIsViewTask}&gt;
                &lt;TaskItem
                    task={selectedTask}
                    handleViewTask={<span class="hljs-function">() =&gt;</span> handleViewTask(selectedTask!)}
                    isViewTask={isViewTask}
                /&gt;
            &lt;/Dialog&gt;
        )}
        &lt;h1 className=<span class="hljs-string">"text-4xl md:text-7xl font-bold text-center py-3 mb-16"</span>&gt;
        Your Tasks
        &lt;/h1&gt;
        <span class="hljs-comment">//rest of code below remains unchanged</span>
</code></pre>
<p>You will need to create the Dialog component. Create a new file in the components folder and call it <strong>Dialog.tsx</strong>, then paste the following in it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { XMarkIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/24/solid"</span>;
<span class="hljs-keyword">import</span> { ReactNode, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { ITask } <span class="hljs-keyword">from</span> <span class="hljs-string">"../models/interface"</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>;

<span class="hljs-keyword">interface</span> DialogProps {
    setIsViewTask?: <span class="hljs-function">(<span class="hljs-params">isViewTask: <span class="hljs-built_in">boolean</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
    children: ReactNode;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dialog</span>(<span class="hljs-params">{ setIsViewTask, children }: DialogProps</span>) </span>{
    <span class="hljs-keyword">const</span> [isOpen, setIsOpen] = useState(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (setIsViewTask) setIsViewTask(<span class="hljs-literal">false</span>);
        setIsOpen(<span class="hljs-literal">false</span>);
    };
    <span class="hljs-keyword">return</span> (
        &lt;dialog
            open={isOpen}
            id=<span class="hljs-string">"modal"</span>
            style={{
            backgroundColor: <span class="hljs-string">"var(--base-bg)"</span>,
            color: <span class="hljs-string">"var(--text-main)"</span>,
            }}
            className={<span class="hljs-string">`<span class="hljs-subst">${
                isOpen ? <span class="hljs-string">"opacity-100"</span> : <span class="hljs-string">"opacity-0 pointer-events-none"</span>
                }</span> transition-opacity duration-300 ease-in-out fixed inset-0                      backdrop-filter backdrop-blur-md backdrop-brightness-50 w-4/6                      border border-container rounded-md max-h-[80vh] overflow-y-auto                 text-main`</span>}
        &gt;
        &lt;Button
            handleClick={closeModal}
            content={{ text: <span class="hljs-string">"Close"</span>, icon: XMarkIcon }}
            extraBtnClasses=<span class="hljs-string">"ml-auto text-main font-medium hover:text-error"</span>
        /&gt;
        &lt;div className=<span class="hljs-string">"max-h-[80vh] overflow-y-auto"</span>&gt;{children}&lt;/div&gt;
        &lt;/dialog&gt;
    );
    }

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Dialog;
</code></pre>
<p>Here, the file defined a <code>Dialog</code> component that takes in some props, displays a button and the children it receives from props. </p>
<p>Finally, replace the two <code>TaskItem</code> components in the <code>tasks.filter...</code> function within the return statement of the <strong>Task.tsx</strong> file with the following:</p>
<pre><code class="lang-typescript">
{tasks
    .filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> !task.done)
    .map(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> (
        &lt;TaskItem
            key={task.$id}
            task={task}
            setTasks={setTasks}
            handleViewTask={<span class="hljs-function">() =&gt;</span> handleViewTask(task)}
            isViewTask={isViewTask}
        /&gt;
))}
</code></pre>
<p>You should be able to click on the task items and have the dialog pop up with the details of the task. </p>
<p>However, if you try to delete the item you will notice that it opens the dialog while deleting it. To fix that, adjust the <code>handleDelete</code> function in the <strong>TaskItem.tsx</strong> file to read like this: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> handleDelete = <span class="hljs-keyword">async</span> (
    e: React.MouseEvent&lt;HTMLButtonElement&gt;,
    currentTaskId: <span class="hljs-built_in">string</span>
) =&gt; {
    e.stopPropagation();
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> deleteDocument(currentTaskId);
        <span class="hljs-keyword">if</span> (isViewTask) {
            navigate(<span class="hljs-number">0</span>);
        } <span class="hljs-keyword">else</span> {
            updateTasks();
        }
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
    }
};
</code></pre>
<p>You've added <code>e.stopPropagation()</code> to stop the event from bubbling up to the parent and potentially interfering with the click to open the dialog. </p>
<p>You've also added a check after deleting the task to see if the task is being viewed, in which case we refresh the page via the <code>navigate(0)</code> to force the UI to update to the proper state. If not, it proceeds to call <code>updateTasks()</code> to refresh the state.</p>
<p>You will notice the same issue when you try to mark the task as complete in which the dialog pops up. To fix this, adjust the checkbox input to include this line: <code>onClick={(e) =&gt; e.stopPropagation()}</code>. </p>
<p>The new line stops the event from bubbling up to the parent <code>div</code>. It is added to the <code>onClick</code> instead of the <code>onChange</code> because the event it is trying to intercept is of type <code>onClick</code>. The input should read like this:</p>
<pre><code class="lang-typescript">
&lt;label htmlFor=<span class="hljs-string">"done"</span> className=<span class="hljs-string">"mr-2 font-light"</span>&gt;
    Mark <span class="hljs-keyword">as</span> complete
&lt;/label&gt;
&lt;input
    <span class="hljs-keyword">type</span>=<span class="hljs-string">"checkbox"</span>
    checked={isDone}
    onClick={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> e.stopPropagation()}
    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    setIsDone(e.target.checked);
    handleCheckbox(task, task.$id, e);
    }}
    className=<span class="hljs-string">"size-5 accent-pink-600 rounded-sm"</span>
/&gt;
</code></pre>
<p>At this point, the React application is responsive, can CRUD the Appwrite database and the user can view individual tasks. </p>
<h3 id="heading-how-to-auto-generate-descriptions-with-vercels-ai-sdk">How to Auto Generate Descriptions with Vercel's AI SDK</h3>
<p>To enhance the application and its' user experience, you can add the ability to auto generate descriptions for the tasks using AI. </p>
<p>To get started, open an integrated terminal and run the following command: <code>npm i ai</code>. This adds the <a target="_blank" href="https://vercel.com/blog/introducing-the-vercel-ai-sdk">Vercel AI SDK</a> to the React application. </p>
<p>Next, run this command in the terminal: <code>npm i @huggingface/inference</code> to add Hugging Face support. The application will use <a target="_blank" href="https://sdk.vercel.ai/docs/guides/providers/huggingface">Hugging Face</a> because you need to pay to get programmatic access to OpenAI.</p>
<p>Create a new file in the utils folder, call it <strong>ai.ts</strong> and paste the following in it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { HfInference } <span class="hljs-keyword">from</span> <span class="hljs-string">"@huggingface/inference"</span>;
<span class="hljs-keyword">import</span> { HuggingFaceStream, StreamingTextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"ai"</span>;

<span class="hljs-comment">// Create a new HuggingFace Inference instance</span>
<span class="hljs-keyword">const</span> Hf = <span class="hljs-keyword">new</span> HfInference(<span class="hljs-keyword">import</span>.meta.env.VITE_HUGGINGFACE_KEY);

<span class="hljs-comment">// IMPORTANT! Set the runtime to edge</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> runtime = <span class="hljs-string">"edge"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> callAI = <span class="hljs-keyword">async</span> (prompt: <span class="hljs-built_in">string</span>) =&gt; {
    <span class="hljs-keyword">const</span> response = Hf.textGenerationStream({
        model: <span class="hljs-string">"OpenAssistant/oasst-sft-4-pythia-12b-epoch-3.5"</span>,
        inputs: <span class="hljs-string">`&lt;|prompter|&gt;<span class="hljs-subst">${prompt}</span>&lt;|endoftext|&gt;&lt;|assistant|&gt;`</span>,
        parameters: {
            max_new_tokens: <span class="hljs-number">150</span>,
            <span class="hljs-comment">// @ts-ignore</span>
            typical_p: <span class="hljs-number">0.2</span>,
            repetition_penalty: <span class="hljs-number">1</span>,
            truncate: <span class="hljs-number">1000</span>,
            return_full_text: <span class="hljs-literal">false</span>,
        },
    });

    <span class="hljs-comment">// Convert the response into a friendly text-stream</span>
    <span class="hljs-keyword">const</span> stream = HuggingFaceStream(response);

    <span class="hljs-comment">// Respond with the stream</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> StreamingTextResponse(stream);
};
</code></pre>
<p>This boilerplate creates an instance of Hugging Face, creates a function that takes in a prompt, passes that prompt to the <code>textGenerationStream</code> function. Then it converts the response to a stream and to a text stream. </p>
<p>You will need to add a Hugging Face access token. You can genrate one at this <a target="_blank" href="https://huggingface.co/settings/tokens">address</a>. You will need an account before you can access it. </p>
<p>Once you have token, open the env file and add to it the following line</p>
<pre><code class="lang-env">
//replace with your actual token
VITE_HUGGINGFACE_KEY=YOUR-HF-ACCESS-TOKEN
</code></pre>
<p>Open the <strong>AddTask.tsx</strong> file and paste the following button just above the closing div tag of the <code>div</code> containing the textarea input:</p>
<pre><code class="lang-typescript">
{textAreaVal.length &gt; <span class="hljs-number">197</span> &amp;&amp; (
    &lt;span className=<span class="hljs-string">"text-error mt-1"</span>&gt;
        Warning description getting too long. Can only be <span class="hljs-number">200</span> characters
    &lt;/span&gt;
)}
<span class="hljs-comment">//paste here</span>
&lt;Button
    handleClick={generateDesc}
    disable={isGenerating}
    extraBtnClasses=<span class="hljs-string">"bg-light mt-2 w-fit ml-auto"</span>
&gt;
    &lt;span&gt;Generate description&lt;/span&gt;
    &lt;SparklesIcon height={<span class="hljs-number">20</span>} /&gt;
&lt;/Button&gt;
<span class="hljs-comment">//rest of below code unchanged</span>
&lt;/div&gt;
</code></pre>
<p>Define <code>generateDesc</code> function just above the <code>return</code> statement in the <strong>AddTask</strong> file like so:</p>
<pre><code>
<span class="hljs-keyword">const</span> generateDesc = <span class="hljs-keyword">async</span> () =&gt; {
    setTextAreaVal(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">if</span> (!titleVal) {
    alert(<span class="hljs-string">"Please provide a title for the task"</span>);
    <span class="hljs-keyword">return</span>;
    }

    setIsGenerating(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> prompt = <span class="hljs-string">`Provide a description for this task: <span class="hljs-subst">${titleVal}</span>. Keep the description to a maximum of 30 words`</span>;

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> callAI(prompt);
        <span class="hljs-keyword">const</span> responseText = <span class="hljs-keyword">await</span> res.text();

        setIsGenerating(<span class="hljs-literal">false</span>);

        <span class="hljs-comment">//create a typing effect</span>
        responseText.split(<span class="hljs-string">""</span>).forEach(<span class="hljs-function">(<span class="hljs-params">char, index</span>) =&gt;</span> {
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setTextAreaVal(<span class="hljs-function">(<span class="hljs-params">prevText</span>) =&gt;</span> prevText + char);
        }, index * <span class="hljs-number">32</span>);
        });
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"ERROR HUGGING FACE API: "</span> + error);
    }
};
</code></pre><p>The function checks that the title is not empty and uses the title to create a prompt to pass on the Hugging Face helper. The response from the call is saved to local state. A simple typing effect is created as the textarea is populated with the response. </p>
<p>Next, add this useState: <code>const [isGenerating, setIsGenerating] = useState(false);</code> to the other useStates in the <code>AddTask</code> component.</p>
<p>Replace the textarea input in the same component with the following:</p>
<pre><code class="lang-typescript">
&lt;textarea
    id=<span class="hljs-string">"description"</span>
    placeholder=<span class="hljs-string">"Describe your task"</span>
    maxLength={<span class="hljs-number">200</span>}
    value={isGenerating ? <span class="hljs-string">"generating..."</span> : textAreaVal}
    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setTextAreaVal(e.target.value)}
    className={<span class="hljs-string">`bg-inherit border rounded-sm p-2 h-32 resize-none          focus:outline-none focus:ring-1 <span class="hljs-subst">${
    textAreaVal.length &gt; <span class="hljs-number">197</span>
    ? <span class="hljs-string">"border-error focus:ring-red-500 invalid:focus:ring-red-600"</span>
    : <span class="hljs-string">"border-input focus:ring-slate-900"</span>
    }</span>`</span>}
/&gt;
</code></pre>
<p>On checking the application, you should see the button and be able to generate a description for the title of a task as below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-09-at-04.57.05.png" alt="Image" width="600" height="400" loading="lazy">
<em>ai-enhanced task description</em></p>
<h3 id="heading-voice-enable-the-application-with-the-react-speech-recognition-package">Voice-enable the Application with the React Speech Recognition Package</h3>
<p>First, you need to add the dependency and its Typescript helper by running the following commands in an integrated terminal window: <code>npm i react-speech-recognition</code> and <code>npm i @types/react-speech-recognition</code>. </p>
<p>Additionally, run the following command to get the dependency to work properly: <code>npm i regenerator-runtime</code>.</p>
<p>Create a <strong>hooks</strong> folder within the <strong>src</strong> folder. Create a file within it called <strong>useSpeechToTextHelper.ts</strong> and paste the following in it:</p>
<pre><code class="lang-typeacript">
import "regenerator-runtime/runtime";
import { useState } from "react";
import { useSpeechRecognition } from "react-speech-recognition";

export function useSpeechToTextHelper() {
    const [error, setError] = useState("");

    const {
        transcript,
        listening,
        resetTranscript,
        browserSupportsSpeechRecognition,
    } = useSpeechRecognition();

    if (!browserSupportsSpeechRecognition) {
        setError("Browser doesn't support speech recognition.");
    }

    return {
        error,
        listening,
        transcript,
        resetTranscript,
    };
}
</code></pre>
<p>This hook exposes some built in helper functions from React Speech Recognition, handles the case that the browser does not support the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition">relevant web APIs</a> and returns some of that data.</p>
<p>Create a new file in the components folder called <strong>Speaker.tsx</strong>. Paste the following code into it:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> { useSpeechToTextHelper } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useSpeechToTextHelper"</span>;
<span class="hljs-keyword">import</span> { MicrophoneIcon, XCircleIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/24/solid"</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>;
<span class="hljs-keyword">import</span> SpeechRecognition <span class="hljs-keyword">from</span> <span class="hljs-string">"react-speech-recognition"</span>;

<span class="hljs-keyword">interface</span> SpeakerProps {
handleClear: <span class="hljs-function">(<span class="hljs-params">e: React.MouseEvent&lt;HTMLButtonElement&gt;</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Speaker</span>(<span class="hljs-params">{ handleClear }: SpeakerProps</span>) </span>{
    <span class="hljs-keyword">const</span> { listening, error } = useSpeechToTextHelper();

    <span class="hljs-keyword">const</span> handleSpeech = <span class="hljs-function">() =&gt;</span> {
        SpeechRecognition.startListening();
    };

    <span class="hljs-keyword">return</span> (
    &lt;div&gt;
        {error &amp;&amp; &lt;div&gt;{error}&lt;/div&gt;}
        &lt;div className=<span class="hljs-string">"flex gap-2 py-1 items-center text-center justify-center"</span>&gt;
            &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;{listening ? <span class="hljs-string">"Mic on"</span> : <span class="hljs-string">"Mic off"</span>}&lt;/span&gt;
            &lt;Button
                handleClick={handleSpeech}
                extraBtnClasses=<span class="hljs-string">"bg-lightOk"</span>
                title=<span class="hljs-string">"Start"</span>
            &gt;
                &lt;MicrophoneIcon height={<span class="hljs-number">25</span>} /&gt;
            &lt;/Button&gt;
            &lt;Button
                handleClick={handleClear}
                extraBtnClasses=<span class="hljs-string">"bg-light"</span>
                <span class="hljs-keyword">type</span>=<span class="hljs-string">"reset"</span>
                title=<span class="hljs-string">"Reset"</span>
            &gt;
                &lt;XCircleIcon height={<span class="hljs-number">25</span>} /&gt;
            &lt;/Button&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Speaker;
</code></pre>
<p>This components accepts a function to clear the voice input as a prop, uses the helper hook, defines a function to handle the actual speech, handles potential error state and displays a button for handling the speech and another for clearing the voice transcript.</p>
<p>In the <strong>AddTask</strong> file, delete the label for title and replace it with the following markup:</p>
<pre><code class="lang-typescript">
&lt;div className=<span class="hljs-string">"flex flex-row justify-between items-center"</span>&gt;
    &lt;label htmlFor=<span class="hljs-string">"title"</span>&gt;Task Title&lt;/label&gt;
    &lt;Speaker handleClear={clearTranscript} /&gt;
&lt;/div&gt;
</code></pre>
<p>This adds the <code>Speaker</code> component and wraps both the label and the <code>Speaker</code> component in a representational <code>div</code> in order to maintain the form layout. </p>
<p>Add the <code>clearTranscript</code> function just above the <code>handleSubmit</code> function like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> handleTitleChange = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    setTitleVal(e.target.value);

    <span class="hljs-keyword">if</span> (e.target.value.trim() !== <span class="hljs-string">""</span>) {
        setTitleValidationError(<span class="hljs-string">""</span>);
    }
};

<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> clearTranscript = <span class="hljs-function">() =&gt;</span> {
    resetTranscript();
};
<span class="hljs-comment">//below code is unchanged</span>
<span class="hljs-keyword">const</span> handleSubmitTask = asyc....
</code></pre>
<p>Next in the same <code>AddTask</code> component, add the following:</p>
<pre><code>
<span class="hljs-keyword">const</span> AddTask = <span class="hljs-function">(<span class="hljs-params">{ task, isEdit, setTasks }: ITaskFormProps</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> navigate = useNavigate();
    <span class="hljs-comment">//paste here</span>
    <span class="hljs-keyword">const</span> { transcript, resetTranscript } = useSpeechToTextHelper();
    <span class="hljs-comment">//rest remains unchanged</span>
</code></pre><p>Replace the useEffect in the file with this new one:</p>
<pre><code class="lang-typescript">
useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (isEdit &amp;&amp; task &amp;&amp; !transcript) {
        setTitleVal(task.title);
        setTextAreaVal(task.description);
    } <span class="hljs-keyword">else</span> {
        setTitleVal(transcript || <span class="hljs-string">""</span>);
    }
}, [isEdit, task, transcript]);
</code></pre>
<p>Your application should now support creating titles for the tasks via voice inputs. And should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-09-at-05.36.51.png" alt="Image" width="600" height="400" loading="lazy">
<em>voice input functionality added to the form</em></p>
<h3 id="heading-how-to-add-search-functionality-to-the-application">How to Add Search Functionality to the Application</h3>
<p>To increase ease of use, it is useful to have some search functionality in the application. </p>
<p>To start, open up the Appwrite console. Click into your collections, click on Indexes tab then click on the "Create index" button. </p>
<p>Leave the index key as is, select FullText in the index type dropdown. Add title attribute and create the index. Repeat the process for the description attribute.</p>
<p>In your application, open <strong>db.ts</strong> file in <strong>utils</strong> folder and paste the following function just above the <code>export</code> keyword, then add it to the list of exports:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> searchTasks = <span class="hljs-keyword">async</span> (searchTerm: <span class="hljs-built_in">string</span>) =&gt; {
    <span class="hljs-keyword">const</span> resTitle = <span class="hljs-keyword">await</span> databases.listDocuments(dbID, collectionID, [
                        Query.search(<span class="hljs-string">"title"</span>, searchTerm),
                    ]);
    <span class="hljs-keyword">const</span> resDesc = <span class="hljs-keyword">await</span> databases.listDocuments(dbID, collectionID, [
                        Query.search(<span class="hljs-string">"description"</span>, searchTerm),
                     ]);
    <span class="hljs-keyword">const</span> res = [...resTitle.documents, ...resDesc.documents];

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

<span class="hljs-keyword">export</span> {
    createDocument,
    readDocuments,
    updateDocument,
    deleteDocument,
    searchTasks,
};
</code></pre>
<p>Create a new file in the components folder, calll it <strong>Search.tsx</strong>. Paste the following into it:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FormEvent, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { ITask } <span class="hljs-keyword">from</span> <span class="hljs-string">"../models/interface"</span>;
<span class="hljs-keyword">import</span> Dialog <span class="hljs-keyword">from</span> <span class="hljs-string">"./Dialog"</span>;
<span class="hljs-keyword">import</span> TaskItem <span class="hljs-keyword">from</span> <span class="hljs-string">"./TaskItem"</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>;
<span class="hljs-keyword">import</span> { searchTasks } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils/db"</span>;

<span class="hljs-keyword">const</span> Search = <span class="hljs-function">() =&gt;</span> {
<span class="hljs-keyword">const</span> [searchTerm, setSearchTerm] = useState(<span class="hljs-string">""</span>);
<span class="hljs-keyword">const</span> [isSearching, setIsSearching] = useState(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">const</span> [searchedTasks, setSearchedTasks] = useState&lt;ITask[]&gt;([]);
<span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"flex flex-col w-full md:w-1/2"</span>&gt;
            &lt;form
                className=<span class="hljs-string">"flex flex-col md:flex-row items-start md:items-center gap-2"</span>
                onSubmit={handleSubmit}
            &gt;
                {searchedTasks.length &gt; <span class="hljs-number">0</span> &amp;&amp; (
                    &lt;Dialog setSearchedTasks={setSearchedTasks}&gt;
                        {searchedTasks.map(<span class="hljs-function">(<span class="hljs-params">task: ITask</span>) =&gt;</span> (
                            &lt;TaskItem key={task.$id} task={task} isViewTask={<span class="hljs-literal">true</span>} /&gt;
                        ))}
                    &lt;/Dialog&gt;
                )}
            &lt;input
                aria-roledescription=<span class="hljs-string">"search"</span>
                <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
                id=<span class="hljs-string">"search"</span>
                placeholder=<span class="hljs-string">"search your tasks..."</span>
                value={searchTerm}
                onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setSearchTerm(e.target.value)}
                className={<span class="hljs-string">`bg-inherit w-5/6 border rounded-md p-2 focus:outline-none focus:ring-1 <span class="hljs-subst">${
                error
                ? <span class="hljs-string">"border-error focus:ring-red-500 invalid:focus:ring-red-600"</span>
                : <span class="hljs-string">"border-input focus:ring-slate-900"</span>
                }</span>`</span>}
            /&gt;
            &lt;Button
            <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
            extraBtnClasses=<span class="hljs-string">"bg-primary text-white hover:bg-primaryHover font-medium text-main py-2"</span>
            &gt;
                &lt;span&gt;{isSearching ? <span class="hljs-string">"Searching..."</span> : <span class="hljs-string">"Search"</span>}&lt;/span&gt;
            &lt;/Button&gt;
            &lt;/form&gt;
            &lt;span className=<span class="hljs-string">"text-error font-medium mt-1"</span>&gt;{error}&lt;/span&gt;
        &lt;/div&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Search;
</code></pre>
<p>The new <code>Search</code> component creates some local state and returns a form with an input and search button. It also opens the dialog when it has search results.</p>
<p>Add this <code>handleSubmit</code> function above the <code>return</code> statement like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> (e: FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">if</span> (!searchTerm) {
        setError(<span class="hljs-string">"No search term entered"</span>);
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setError(<span class="hljs-string">""</span>);
        }, <span class="hljs-number">3000</span>);
        <span class="hljs-keyword">return</span>;
    }

    setIsSearching(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> searchTasks(searchTerm);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"res search: "</span>, res);
    <span class="hljs-keyword">if</span> (res.length === <span class="hljs-number">0</span>) {
        setIsSearching(<span class="hljs-literal">false</span>);
        setError(<span class="hljs-string">"No task found"</span>);
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setSearchTerm(<span class="hljs-string">""</span>);
        setError(<span class="hljs-string">""</span>);
        }, <span class="hljs-number">3000</span>);
        <span class="hljs-keyword">return</span>;
    }
    setIsSearching(<span class="hljs-literal">false</span>);
    setSearchedTasks(res <span class="hljs-keyword">as</span> ITask[]);
};
</code></pre>
<p>This function sets an error if no search term is received, then it attempts to call the database search function passing it the search term. If successful it sets the tasks to local state and if not, it catches the error.</p>
<p>Click into the dialog component and replace its props with the following, then pass the <code>setSearchedTasks</code> to it like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> DialogProps {
    setIsViewTask?: <span class="hljs-function">(<span class="hljs-params">isViewTask: <span class="hljs-built_in">boolean</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
    setSearchedTasks?: <span class="hljs-function">(<span class="hljs-params">tasks: ITask[]</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
    children: ReactNode;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dialog</span>(<span class="hljs-params">{ setIsViewTask, setSearchedTasks, children }: DialogProps</span>) </span>{...
</code></pre>
<p>Replace the <code>closeModal</code> function in the dialog component with this snippet:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (setIsViewTask) setIsViewTask(<span class="hljs-literal">false</span>);
    <span class="hljs-comment">//this is the new line</span>
    <span class="hljs-keyword">if</span> (setSearchedTasks) setSearchedTasks([]);
    setIsOpen(<span class="hljs-literal">false</span>);
};
</code></pre>
<p>Go back into the <strong>Task.tsx</strong> file and paste this below the <code>h1</code> tag that displays the "Your Tasks" text:</p>
<pre><code class="lang-typescript">
&lt;h1 className=<span class="hljs-string">"text-4xl md:text-7xl font-bold text-center py-3 mb-16"</span>&gt;
Your Tasks
&lt;/h1&gt;
<span class="hljs-comment">//paste here</span>
&lt;div className=<span class="hljs-string">"m-8 flex flex-col-reverse md:flex-row gap-8 items-start                     md:items-center md:justify-between"</span>&gt;
    &lt;Search /&gt;
    &lt;Button
        handleClick={<span class="hljs-function">() =&gt;</span> navigate(<span class="hljs-string">"/"</span>)}
        extraBtnClasses=<span class="hljs-string">"bg-primary text-white font-medium py-2 hover:bg-    primaryHover ml-auto"</span>
    &gt;
        &lt;span&gt;Add Task&lt;/span&gt;
        &lt;PlusIcon height={<span class="hljs-number">25</span>} className=<span class="hljs-string">"hidden md:flex"</span> /&gt;
    &lt;/Button&gt;
&lt;/div&gt;
<span class="hljs-comment">//rest of code stays unchanged</span>
</code></pre>
<p>This adds the search component and a button that takes you back to the Index page when clicked.</p>
<p>Add the following within the Task component just below the useStates:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> [selectedTask, setSelectedTask] = useState&lt;ITask&gt;();
<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> navigate = useNavigate();
<span class="hljs-comment">//all below remain unchanged</span>
<span class="hljs-keyword">const</span> handleSelectChange = (e: React.ChangeEvent&lt;HTMLSelectElement&gt;)
</code></pre>
<p>You can now test your search functionality. It works but has one bug: if the search term is present in the both the title and the description we get back two search results. </p>
<p>To fix that, modify the <code>searchTasks</code> function in <strong>db.ts</strong> to filter out duplicate tasks by IDs like so: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> searchTasks = <span class="hljs-keyword">async</span> (searchTerm: <span class="hljs-built_in">string</span>) =&gt; {
    <span class="hljs-keyword">const</span> resTitle = <span class="hljs-keyword">await</span> databases.listDocuments(dbID, collectionID, [
        Query.search(<span class="hljs-string">"title"</span>, searchTerm),
    ]);
    <span class="hljs-keyword">const</span> resDesc = <span class="hljs-keyword">await</span> databases.listDocuments(dbID, collectionID, [
        Query.search(<span class="hljs-string">"description"</span>, searchTerm),
    ]);

    <span class="hljs-keyword">const</span> res = [...resTitle.documents, ...resDesc.documents];

    <span class="hljs-comment">// remove duplicate tasks</span>
    <span class="hljs-keyword">const</span> uniqueRes = res.filter(
        <span class="hljs-function">(<span class="hljs-params">task, index, self</span>) =&gt;</span> index === self.findIndex(<span class="hljs-function">(<span class="hljs-params">t</span>) =&gt;</span> t.$id ===                  task.$id)
    );

    <span class="hljs-keyword">return</span> uniqueRes;
};
</code></pre>
<p>Now your search should work as expected and should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-09-at-08.00.40.png" alt="Image" width="600" height="400" loading="lazy">
<em>your pending and completed tasks displayed in the browser</em></p>
<h3 id="heading-how-to-add-ability-to-sort-tasks-via-due-date-and-priority">How to Add Ability to Sort Tasks via Due Date and Priority</h3>
<p>The application will only sort the pending tasks as it makes the most sense. It will sort by due date from the earliest date to the latest and vice versa. It will also sort by priority from lowest to highest and vice versa.</p>
<p>To get started, paste the following in the <strong>Tasks.tsx</strong> file right below the <code>h3</code> tag with the "Pending Tasks" text like so:</p>
<pre><code class="lang-typescript">
&lt;h3 className=<span class="hljs-string">"text-2xl font-bold m-8"</span>&gt;Pending Tasks&lt;/h3&gt;
<span class="hljs-comment">//paste here</span>
&lt;div className=<span class="hljs-string">"m-8 flex items-start lg:items-center gap-1 justify-between flex-col lg:flex-row"</span>&gt;
    &lt;span className=<span class="hljs-string">"font-medium"</span>&gt;Sort Tasks by: &lt;/span&gt;
    &lt;Select
        defaultSelectValue={selectArray[<span class="hljs-number">0</span>]}
        handleSelectChange={handleSelectChange}
        selectOptions={selectArray}
    /&gt;
&lt;/div&gt;
</code></pre>
<p>Then paste the following array that will contain the options for select component above. Paste it right above <code>handleViewTask</code> function like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> navigate = useNavigate();
<span class="hljs-comment">//paste here</span>
<span class="hljs-keyword">const</span> selectArray = [
    <span class="hljs-string">"priority - (low - high)"</span>,
    <span class="hljs-string">"priority - (high - low)"</span>,
    <span class="hljs-string">"due date - (earliest - latest)"</span>,
    <span class="hljs-string">"due date - (latest - earliest)"</span>,
];
<span class="hljs-comment">//rest remains unchanged</span>
<span class="hljs-keyword">const</span> handleViewTask = (...
</code></pre>
<p>Add the <code>handleSelectChange</code> and the sort functions above the <code>selectArray</code>, like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> sortByPriority = (tasksList: ITask[], isAsc: <span class="hljs-built_in">boolean</span>): ITask[] =&gt; {
    <span class="hljs-keyword">const</span> priorityOrder: { [key: <span class="hljs-built_in">string</span>]: <span class="hljs-built_in">number</span> } = {
    low: <span class="hljs-number">1</span>,
    medium: <span class="hljs-number">2</span>,
    high: <span class="hljs-number">3</span>,
    };

    <span class="hljs-keyword">return</span> [...tasksList].sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> priorityA = priorityOrder[a.priority!.toLowerCase()];
    <span class="hljs-keyword">const</span> priorityB = priorityOrder[b.priority!.toLowerCase()];
    <span class="hljs-keyword">return</span> isAsc ? priorityA - priorityB : priorityB - priorityA;
    });
};

<span class="hljs-keyword">const</span> handleSelectChange = <span class="hljs-keyword">async</span> (
    e: React.ChangeEvent&lt;HTMLSelectElement&gt;
) =&gt; {
    <span class="hljs-keyword">const</span> selectedOption = e.target.value;
    <span class="hljs-keyword">const</span> doneTasks = tasks.filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span> task.done);

    <span class="hljs-keyword">switch</span> (selectedOption) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">"priority - (low - high)"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"priority - (high - low)"</span>: {
            <span class="hljs-keyword">const</span> isAsc = selectedOption === <span class="hljs-string">"priority - (low - high)"</span>;
            <span class="hljs-keyword">const</span> sortedTasks = sortByPriority(tasks, isAsc);
            setTasks([...doneTasks, ...sortedTasks.filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span>                           !task.done)]);
            <span class="hljs-keyword">break</span>;
        }
        <span class="hljs-keyword">case</span> <span class="hljs-string">"due date - (earliest - latest)"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"due date - (latest - earliest)"</span>: {
            <span class="hljs-keyword">const</span> isEarliestToLatest =
            selectedOption === <span class="hljs-string">"due date - (earliest - latest)"</span>;
            <span class="hljs-keyword">const</span> dueDateResult = <span class="hljs-keyword">await</span> sortByDueDate(isEarliestToLatest);
            <span class="hljs-keyword">const</span> sortedTasks = dueDateResult.documents <span class="hljs-keyword">as</span> ITask[];
            setTasks([...doneTasks, ...sortedTasks.filter(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span>                            !task.done)]);
            <span class="hljs-keyword">break</span>;
        }
        <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">break</span>;
        }
};

<span class="hljs-comment">//below remains unchanged</span>
<span class="hljs-keyword">const</span> selectArray = .....
</code></pre>
<p>The sortByPriority function creates an object whose keys map to the priority array and gives them numerical values. This makes it easier to sort as it is hard to tell which string is higher priority without that.</p>
<p>The <code>handleSelectChange</code> function picks out the selected option and filters the tasks to get the completed ones. It does matching logic in the switch statements, calling <code>sortByPriority</code> for the cases where the user is trying to do that and it calls <code>sortByDueDate</code> for the rest of the cases. </p>
<p><code>sortByDueDate</code> is defined in the <strong>db.ts</strong> file. Open it and paste the following at the bottom of the file above the exports. Then add it to the exports list like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> sortByDueDate = <span class="hljs-keyword">async</span> (isEarliestToLatest: <span class="hljs-built_in">boolean</span>) =&gt; {
    <span class="hljs-keyword">const</span> orderQuery = isEarliestToLatest
        ? Query.orderAsc(<span class="hljs-string">"due_date"</span>)
        : Query.orderDesc(<span class="hljs-string">"due_date"</span>);
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> databases.listDocuments(dbID, collectionID,              [orderQuery]);
    <span class="hljs-keyword">return</span> res;
};

<span class="hljs-keyword">export</span> {
    createDocument,
    readDocuments,
    updateDocument,
    deleteDocument,
    searchTasks,
    sortByDueDate,
};
</code></pre>
<p>This function leverages Appwrites' Query methods to sort the date string according to the Boolean that is passed to it.</p>
<p>Going back to your application, run it to test the sorting functionality. The application should be sorted and the sorting should only apply to the pending tasks.</p>
<h3 id="heading-bonus-how-to-add-dark-mode-support">Bonus: How to Add Dark Mode Support</h3>
<p>The last thing left is to add Dark Mode support that respects the users' systems setting. </p>
<p>For this, open the <strong>tailwind.config.ts</strong> file and replace its contents with the following:</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">/** @type {import('tailwindcss').Config} */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    content: [<span class="hljs-string">"./index.html"</span>, <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>],
    darkMode: <span class="hljs-string">"selector"</span>,
    theme: {
        extend: {
            textColor: {
                error: <span class="hljs-string">"var(--text-error)"</span>,
                ok: <span class="hljs-string">"var(--text-ok)"</span>,
                main: <span class="hljs-string">"var(--text-main)"</span>,
                iconColor: <span class="hljs-string">"var(--btn-icon-main)"</span>,
            },
            backgroundColor: {
                base: <span class="hljs-string">"var(--base-bg)"</span>,
                primary: <span class="hljs-string">"var(--btn-bg-primary)"</span>,
                primaryHover: <span class="hljs-string">"var(--btn-bg-primary-hover)"</span>,
                ok: <span class="hljs-string">"var(--btn-bg-ok)"</span>,
                lightOk: <span class="hljs-string">"var(--btn-bg-light-ok)"</span>,
                light: <span class="hljs-string">"var(--btn-bg-light)"</span>,
                lowPriority: <span class="hljs-string">"var(--low-priority)"</span>,
                mediumPriority: <span class="hljs-string">"var(--medium-priority)"</span>,
                highPriority: <span class="hljs-string">"var(--high-priority)"</span>,
            },
            borderColor: {
                container: <span class="hljs-string">"var(--border-container)"</span>,
                input: <span class="hljs-string">"var(--border-input)"</span>,
                error: <span class="hljs-string">"var(--border-error)"</span>,
            },
        },
    },
    plugins: [],
};
</code></pre>
<p>This extends the tailwind preset colors and ties the CSS variables that were set in the <strong>index.css</strong> file to the Tailwind config. </p>
<p>In the <strong>index.css</strong> file, add this dark class below the date class like so:</p>
<pre><code class="lang-css">
<span class="hljs-selector-id">#date</span><span class="hljs-selector-pseudo">::-webkit-calendar-picker-indicator</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--btn-bg-light); 
}
//<span class="hljs-selector-tag">paste</span> <span class="hljs-selector-tag">here</span>
<span class="hljs-selector-class">.dark</span>{
    <span class="hljs-attribute">--base-bg</span>: <span class="hljs-number">#262626</span>;
    <span class="hljs-attribute">--text-main</span>: <span class="hljs-number">#ffffff</span>;
    <span class="hljs-attribute">--text-error</span>: <span class="hljs-number">#fca5a5</span>;
    <span class="hljs-attribute">--text-ok</span>: <span class="hljs-number">#86efac</span>;
    <span class="hljs-attribute">--border-input</span>: <span class="hljs-number">#e2e8f0</span>;
    <span class="hljs-attribute">--border-error</span>: <span class="hljs-number">#fca5a5</span>;
}
</code></pre>
<p>This changes some of the CSS variables values when the dark class is applied.</p>
<p>Now, open the navbar file in the components folder and replace its contents with the following:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> navigate = useNavigate();

    <span class="hljs-keyword">const</span> themeArray = [<span class="hljs-string">"light"</span>, <span class="hljs-string">"dark"</span>, <span class="hljs-string">"system"</span>];
    <span class="hljs-keyword">const</span> [theme, setTheme] = useState(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"theme"</span>) || themeArray[<span class="hljs-number">2</span>];
    });

    <span class="hljs-keyword">const</span> applyTheme = <span class="hljs-function">(<span class="hljs-params">selectedTheme: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> isDarkModePreferred = <span class="hljs-built_in">window</span>.matchMedia(
            <span class="hljs-string">"(prefers-color-scheme: dark)"</span>
            ).matches;

        <span class="hljs-built_in">document</span>.documentElement.classList.remove(<span class="hljs-string">"light"</span>, <span class="hljs-string">"dark"</span>);
        <span class="hljs-built_in">document</span>.documentElement.classList.add(selectedTheme);

        <span class="hljs-keyword">if</span> (selectedTheme === <span class="hljs-string">"system"</span>) {
        <span class="hljs-built_in">document</span>.documentElement.classList.toggle(<span class="hljs-string">"dark"</span>, isDarkModePreferred);
        <span class="hljs-built_in">document</span>.documentElement.classList.toggle(<span class="hljs-string">"light"</span>,                  !isDarkModePreferred);
        }
    };

    <span class="hljs-keyword">const</span> handleSelectTheme = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLSelectElement&gt;</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> selectedTheme = e.target.value;
        setTheme(selectedTheme);

        <span class="hljs-comment">// Store the selected theme in localStorage</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"theme"</span>, selectedTheme);
    };

    useEffect(<span class="hljs-function">() =&gt;</span> {
        applyTheme(theme);
    }, [theme]);

    <span class="hljs-keyword">return</span> (
        &lt;nav className=<span class="hljs-string">"py-4 border-b-2 border-container shadow-md shadow-gray-400 w-full fixed top-0 bg-base"</span>&gt;
            &lt;ul className=<span class="hljs-string">"flex items-center justify-between  w-11/12 mx-auto"</span>&gt;
                &lt;Link to=<span class="hljs-string">"/"</span>&gt;
                    &lt;Button&gt;
                        &lt;span className=<span class="hljs-string">"font-semibold text-main"</span>&gt;Taskwrite&lt;/span&gt;
                        &lt;PencilIcon height={<span class="hljs-number">20</span>} className=<span class="hljs-string">"text-main"</span> /&gt;
                    &lt;/Button&gt;
                &lt;/Link&gt;
                &lt;div className=<span class="hljs-string">"flex items-center justify-between gap-6"</span>&gt;
                &lt;Link
                    to=<span class="hljs-string">"/tasks"</span>
                    className=<span class="hljs-string">"font-semibold hover:scale-105 transition duration-300 ease-in-out"</span>
                &gt;
                    View Tasks
                &lt;/Link&gt;
                &lt;div className=<span class="hljs-string">"flex gap-2 items-center"</span>&gt;
                    &lt;span className=<span class="hljs-string">"font-semibold"</span>&gt; Theme: &lt;/span&gt;
                    &lt;Select
                        defaultSelectValue={theme}
                        selectOptions={themeArray}
                        handleSelectChange={handleSelectTheme}
                    /&gt;
                &lt;/div&gt;
                &lt;/div&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Navbar;
</code></pre>
<p>Your application should now have a select in the navigation menu that successfully toggles between dark and light themes while defaulting to the system preferences when set to "System". </p>
<p>It should look like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-09-at-13.21.38.png" alt="Image" width="600" height="400" loading="lazy">
<em>Taskwrite complete interface and functionalities</em></p>
<p>And Taskwrite is complete! You have successfully built a task manager application that is AI-enhanced, voice-enabled, searchable and sortable using React and Appwrite.</p>
<h2 id="heading-notes">Notes</h2>
<p>Appwrite recently announced some new features that would greatly simplify the search functionality above but, at the time of writing, these changes were not rolled out to their cloud offering. </p>
<p>The application could be further simplified by using state management solutions and this will be added to it in subsequent articles.</p>
<p>The application is live <a target="_blank" href="https://taskwrite.netlify.app/">here</a>.</p>
<h2 id="heading-limitations">Limitations</h2>
<p>The following are some known limitations and issues with this application:</p>
<ul>
<li>The navigation menu is not responsive</li>
<li>The application has no tests written</li>
<li>The permissions set for Appwrite are permissive and not recommended for production environments</li>
<li>The application could leverage <a target="_blank" href="https://appwrite.io/docs/apis/realtime">Appwrites' Realtime</a> capabilites for a smoother experience</li>
<li>The application could do with push notifications to remind the user when the task due date is coming up</li>
</ul>
<p>That said, the application is going to continue being improved and worked on. You can follow along with that on <a target="_blank" href="https://github.com/FatumaA/taskwrite">GitHub</a>. All contributions and improvements on the codebase are welcome. Please star the repository while you are at it.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Java Project – How to Build a Simple Parking Lot Management System ]]>
                </title>
                <description>
                    <![CDATA[ Hello everyone! It's good to be back with another tutorial – this time on Java, one of the most popular programming languages out there today. Java is used by developers and enterprises to build powerful applications that serve many different purpose... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/java-project-parking-lot-management-system/</link>
                <guid isPermaLink="false">66d8515a7211ea6be29e1b81</guid>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kunal Nalawade ]]>
                </dc:creator>
                <pubDate>Wed, 07 Feb 2024 00:25:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/java-article-image.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hello everyone! It's good to be back with another tutorial – this time on Java, one of the most popular programming languages out there today.</p>
<p>Java is used by developers and enterprises to build powerful applications that serve many different purposes. The object-oriented nature of Java makes writing clean, modular, reusable code very easy.</p>
<p>If you are not comfortable with Java yet, there are many great resources online that can help you get started. You can check out <a target="_blank" href="https://www.freecodecamp.org/news/learn-java-free-java-courses-for-beginners/">this collection of resources</a> put together by one of our freeCodeCamp staff. <a target="_blank" href="https://www.freecodecamp.org/news/learn-the-basics-of-java-programming/">Here's a beginner-friendly Java course</a>. And <a target="_blank" href="https://www.javatpoint.com/java-tutorial">here's another helpful tutorial</a> to help you work on the basics. It's never too late!</p>
<p>In this tutorial, we are going to build a simple command-line application for a parking lot system. I'll start with the requirements first, then list down the classes needed and explain the workflow of the application. After this, I will show you how to implement each functionality.</p>
<h2 id="heading-heres-what-well-cover">Here's What We'll Cover:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-project">How to Set Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-requirements">Project Requirements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-java-classes">Java Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-application-workflow">Application Workflow</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-application">How to Build the Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-the-app">How to Test the Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-future-scope">Future Scope</a></p>
</li>
</ol>
<h2 id="heading-how-to-set-up-the-project">How to Set Up the Project</h2>
<p>You need to have Java 8 installed in your system. If you don't, then download it from <a target="_blank" href="https://www.oracle.com/java/technologies/downloads/">here</a> and follow <a target="_blank" href="https://phoenixnap.com/kb/install-java-windows">this</a> installation guide. Also, install <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> and add the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=redhat.java">Language Support for Java</a> extension on VS Code.</p>
<p>There's no need to set up a database connection, an external server, or a framework. We'll use an in-memory data structure to store the data. Also, we are not going to create a user interface, as the focus is only on the business logic.</p>
<p>With that out of the way, we are ready to start.</p>
<h2 id="heading-project-requirements">Project Requirements</h2>
<p>Our goal is to create an application for a parking lot system. As everyone visualizes projects differently, there may be many features that come into your mind.</p>
<p>But before jumping into the implementation, we first need to lay down specific requirements. It's always important to start with the basic requirements first, so that's what we'll do here.</p>
<p>Here is a list of all the requirements:</p>
<ul>
<li><p>A parking lot should have a parking lot ID, number of floors, and number of slots on each floor.</p>
</li>
<li><p>Each slot in the parking lot has a type of vehicle that can park there. Valid types are <em>car, bike,</em> and <em>truck.</em> You can include any vehicle type as per your needs. You can also decide which slots should go to each type. Here, I am assigning the first slot on each floor to a truck, the next two for bikes, and rest for cars.</p>
</li>
<li><p>When a vehicle pulls into the parking lot, the application takes the vehicle's type, registration number, and its color. You can also have additional details like the vehicle's name, the driver's name, and so on.</p>
</li>
<li><p>After that, a slot is assigned to the vehicle. The selection strategy could be anything you want. Here, we will choose the lowest floor and earliest available slot.</p>
</li>
<li><p>When a vehicle is assigned a slot, the app should return a ticket. The ticket should be a string of the form . For example, the vehicle parked on floor 2, slot 5 would be <em>PR123_2_5.</em></p>
</li>
<li><p>To unpark the vehicle, the user should present the valid ticket. After that, the vehicle is removed from the slot.</p>
</li>
<li><p>The app should be able to display the number of available slots and a list of available and unavailable slots for a specific vehicle type.</p>
</li>
</ul>
<h2 id="heading-java-classes">Java Classes</h2>
<p>Let's understand how we are going to design the application. Since it is a command line application, we are only going to write the business logic of a parking lot and display outputs on a console.</p>
<p>We will have separate classes representing the entities of our application: <code>Vehicle</code> and <code>Slot</code>. Then, we'll create a <code>ParkingLot</code> class that contains the core business logic of our application. Our main driver class will interact with this class for all the functionalities.</p>
<p>Now, let's define our classes. Each class will have a constructor to initialize their fields.</p>
<p><strong>Vehicle class:</strong></p>
<ul>
<li><code>type</code>, <code>registration</code>, <code>color</code> (All are of string type)</li>
</ul>
<p><strong>Slot class:</strong></p>
<ul>
<li><p><code>type</code> (string)</p>
</li>
<li><p><code>vehicle</code> (Vehicle): type of vehicle parked in the slot</p>
</li>
<li><p><code>ticketId</code> (string): ticket id assigned to the vehicle parked in this slot, initially null.</p>
</li>
</ul>
<p><strong>ParkingLot class:</strong></p>
<p><strong>Fields</strong>:</p>
<ul>
<li><p><code>parkingLotId</code> (string)</p>
</li>
<li><p><code>slots</code> (List&lt;List&gt;): This is a list of all the slots in the parking lot. The list of lists represents slots on multiple floors. The floors and slots are numbered according to the list index.</p>
</li>
</ul>
<p><strong>Constructor</strong>: <code>ParkingLot(parkingLotId, nfloors, noOfSlotsPerFlr)</code></p>
<p><strong>Methods</strong>:</p>
<ul>
<li><p><code>parkVehicle(type, regNo, color)</code>: takes all the parameters of a vehicle, assigns a slot, and returns the ticket</p>
</li>
<li><p><code>unPark(ticketId)</code>: takes the ticket id and removes the vehicle from the slot</p>
</li>
<li><p><code>getNoOfOpenSlots(type)</code>: returns the number of slots for vehicle type</p>
</li>
<li><p><code>displayOpenSlots(type)</code>: displays all open slots for vehicle type</p>
</li>
<li><p><code>displayOccupiedSlots(type)</code>: displays all occupied slots for vehicle type</p>
</li>
</ul>
<p>This will contain our core business logic including the methods that will be exposed to the client i.e. the main method in our case. The initialization logic inside the constructor works differently as compared to the <code>Vehicle</code> and <code>Slot</code> class.</p>
<p>You will learn more about these classes in the implementation section.</p>
<h2 id="heading-application-workflow">Application Workflow</h2>
<p>Now that we have our classes defined, let's understand the workflow of our application.</p>
<p>When a vehicle pulls into the parking lot, the system takes in the vehicle details and looks for an available slot. If it finds an open slot, it assigns that slot to the vehicle and returns a ticket.</p>
<p>This is handled by the <code>parkVehicle()</code> method. If a slot is not available, then the method prints an error message.</p>
<p>Now, if the vehicle wants to unpark, it has to show the ticket. The system parses the ticket, finds out which slot the vehicle is parked in and frees up the slot. The <code>unPark()</code> method takes in the ticket and frees up the corresponding slot.</p>
<p>Let's dive into the code now.</p>
<h2 id="heading-how-to-build-the-application">How to Build the Application</h2>
<p>Let's first write our <code>Vehicle</code> and <code>Slot</code> classes with the fields mentioned previously:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Vehicle</span> </span>{
    String type;
    String registration;
    String color;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Vehicle</span><span class="hljs-params">(String type, String registration, String color)</span> </span>{
        <span class="hljs-keyword">this</span>.type = type;
        <span class="hljs-keyword">this</span>.registration = registration;
        <span class="hljs-keyword">this</span>.color = color;
    }
}
</code></pre>
<pre><code class="lang-python">public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Slot</span> {
    <span class="hljs-title">String</span> <span class="hljs-title">type</span>;
    <span class="hljs-title">Vehicle</span> <span class="hljs-title">vehicle</span>;
    <span class="hljs-title">String</span> <span class="hljs-title">ticketId</span>;
    <span class="hljs-title">public</span> <span class="hljs-title">Slot</span>(<span class="hljs-params">String type</span>) {
        <span class="hljs-title">this</span>.<span class="hljs-title">type</span> = <span class="hljs-title">type</span>;
        <span class="hljs-title">this</span>.<span class="hljs-title">vehicle</span>=<span class="hljs-title">null</span>;
        <span class="hljs-title">this</span>.<span class="hljs-title">ticketId</span>=<span class="hljs-title">null</span>;
    }
}</span>
</code></pre>
<p>Now, let's implement the <code>ParkingLot</code> class. We have discussed the fields and methods the class will contain. Let's write the fields first.</p>
<pre><code class="lang-python">public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ParkingLot</span> {
    <span class="hljs-title">String</span> <span class="hljs-title">parkingLotId</span>;
    <span class="hljs-title">List</span>&lt;<span class="hljs-title">List</span>&lt;<span class="hljs-title">Slot</span>&gt;&gt; <span class="hljs-title">slots</span>;

    ... <span class="hljs-title">constructor</span> <span class="hljs-title">and</span> <span class="hljs-title">methods</span> ...
 }</span>
</code></pre>
<p>I'll show you, step by step, how to implement each functionality.</p>
<h3 id="heading-initialize-the-class">Initialize the Class</h3>
<p>Initialize the <code>ParkingLot</code> class through the constructor. This is where we build our parking lot. As mentioned before, the parking lot will have a given number of floors and number of slots per floor.</p>
<pre><code class="lang-java">ParkingLot(String parkingLotId, <span class="hljs-keyword">int</span> nfloors, <span class="hljs-keyword">int</span> noOfSlotsPerFlr) {
        ... code here ...
    }
</code></pre>
<p>Start by setting the parking lot id.</p>
<pre><code class="lang-java"><span class="hljs-keyword">this</span>.parkingLotId=parkingLotId;
</code></pre>
<p>Then, initialize all the slots. On each floor, the first slot goes to truck, the next two go to bike, and the rest are for cars.</p>
<pre><code class="lang-java">slots = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i&lt;nfloors;i++){
    slots.add(<span class="hljs-keyword">new</span> ArrayList&lt;&gt;());
    List&lt;Slot&gt; floorSlots = slots.get(i);
    floorSlots.add(<span class="hljs-keyword">new</span> Slot(<span class="hljs-string">"truck"</span>));
    floorSlots.add(<span class="hljs-keyword">new</span> Slot(<span class="hljs-string">"bike"</span>));
    floorSlots. add(<span class="hljs-keyword">new</span> Slot(<span class="hljs-string">"bike"</span>));

    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> j=<span class="hljs-number">3</span>;j&lt;noOfSlotsPerFlr;j++){
        slots.get(i).add(<span class="hljs-keyword">new</span> Slot(<span class="hljs-string">"car"</span>));
    }
}
</code></pre>
<p>Here, we have done simple initialization for each <code>Slot</code> object in the list.</p>
<h3 id="heading-park-the-vehicle">Park the Vehicle</h3>
<p>Let's implement the <code>parkVehicle()</code> method now.</p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">parkVehicle</span><span class="hljs-params">(String type, String regNo, String color)</span> </span>{
        ... Implementation here ...
    }
</code></pre>
<p>First, create a <code>Vehicle</code> object with the given details.</p>
<pre><code class="lang-java">Vehicle vehicle = <span class="hljs-keyword">new</span> Vehicle(type, regNo, color);
</code></pre>
<p>Loop over the entire slots list and find the first empty slot. To determine whether a slot is empty or not, check if the slot can take the given vehicle type and if the slot's vehicle field is null (meaning it is empty).</p>
<pre><code class="lang-java"><span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i&lt;slots.size();i++){
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> j=<span class="hljs-number">0</span>;j&lt;slots.get(i).size(); j++){
                Slot slot = slots.get(i).get(j);
                <span class="hljs-keyword">if</span>(slot.type == type &amp;&amp; slot.vehicle == <span class="hljs-keyword">null</span>) {
                    ... assign the slot here ...
                }
            }
        }
</code></pre>
<p>To assign this slot to the vehicle, just set the <code>vehicle</code> field to the <code>Vehicle</code> object we created above. Then, generate and return a ticket using the floor and slot numbers.</p>
<pre><code class="lang-java">slot.vehicle=vehicle;
slot.ticketId=generateTicketId(i+<span class="hljs-number">1</span>, j+<span class="hljs-number">1</span>);
<span class="hljs-keyword">return</span> slot.ticketId;
</code></pre>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">generateTicketId</span><span class="hljs-params">(<span class="hljs-keyword">int</span> flr, <span class="hljs-keyword">int</span> slno)</span></span>{
        <span class="hljs-keyword">return</span> parkingLotId + <span class="hljs-string">"_"</span> + flr + <span class="hljs-string">"_"</span> + slno;
    }
</code></pre>
<p>This method does not need to be exposed outside the class, so it's private.</p>
<p>If a slot is not available, return null or throw an exception. Do this after the for loop has ended.</p>
<pre><code class="lang-java">System.out.println(<span class="hljs-string">"NO slot available for given type"</span>);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
</code></pre>
<h3 id="heading-unpark-the-vehicle">Unpark the Vehicle</h3>
<p>To unpark the vehicle, parse the ticket id to get the floor and slot number where the car is parked.</p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unPark</span><span class="hljs-params">(String ticketId)</span></span>{
        String[] extract = ticketId.split(<span class="hljs-string">"_"</span>);
        <span class="hljs-keyword">int</span> flr_idx=Integer.parseInt(extract[<span class="hljs-number">1</span>])-<span class="hljs-number">1</span>;
        <span class="hljs-keyword">int</span> slot_idx=Integer.parseInt(extract[<span class="hljs-number">2</span>])-<span class="hljs-number">1</span>;

         ...
    }
</code></pre>
<p>Surround the parsing logic with a try-catch in case the user passes an invalid ticket id.</p>
<p>With the floor and slot number, find the slot where the vehicle is parked and un-assign the vehicle.</p>
<pre><code class="lang-python"><span class="hljs-keyword">for</span>(int i=<span class="hljs-number">0</span>; i&lt;slots.size();i++){
            <span class="hljs-keyword">for</span>(int j=<span class="hljs-number">0</span>;j&lt;slots.get(i).size(); j++){
                <span class="hljs-keyword">if</span>(i==flr_idx &amp;&amp; j==slot_idx) {
                    Slot slot = slots.get(i).get(j);
                    slot.vehicle=null;
                    slot.ticketId=null;
                    System.out.println(<span class="hljs-string">"Unparked vehicle"</span>);
                }
            }
        }
</code></pre>
<h3 id="heading-display-parking-lot-information">Display Parking Lot Information</h3>
<p>We have defined three methods to display parking lot information in different forms.</p>
<p><strong>Return the number of open slots for a vehicle type.</strong></p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">getNoOfOpenSlots</span><span class="hljs-params">(String type)</span></span>{
        <span class="hljs-keyword">int</span> count=<span class="hljs-number">0</span>;
        <span class="hljs-keyword">for</span>(List&lt;Slot&gt; floor: slots){
            <span class="hljs-keyword">for</span>(Slot slot: floor){
                <span class="hljs-keyword">if</span>(slot.vehicle == <span class="hljs-keyword">null</span> &amp;&amp; slot.type.equals(type)) count++;
            }
        }

        <span class="hljs-keyword">return</span> count;
    }
</code></pre>
<p>Here, we loop over all the slots and check two conditions. First, we check if the <code>vehicle</code> field is null indicating an empty slot. And then we check if the type of the slot matches with the vehicle type requested by the client.</p>
<p>We then simply return the number of empty slots for the give type.</p>
<p><strong>Display all the open slots for vehicle type:</strong></p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">displayOpenSlots</span><span class="hljs-params">(String type)</span></span>{
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i&lt;slots.size();i++){
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> j=<span class="hljs-number">0</span>;j&lt;slots.get(i).size();j++){
                Slot slot=slots.get(i).get(j);
                <span class="hljs-keyword">if</span>(slot.vehicle == <span class="hljs-keyword">null</span> &amp;&amp; slot.type.equals(type)) 
                    System.out.println(<span class="hljs-string">"Floor "</span> + (i+<span class="hljs-number">1</span>) + <span class="hljs-string">" slot "</span> + (j+<span class="hljs-number">1</span>));
            }
        }   
    }
</code></pre>
<p>This is an enhancement over the previous function. Instead of the count, we are returning the actual empty slots with floor and slot no.</p>
<p><strong>Display all occupied slots for vehicle type:</strong></p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">displayOccupiedSlots</span><span class="hljs-params">(String type)</span></span>{
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i&lt;slots.size();i++){
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> j=<span class="hljs-number">0</span>;j&lt;slots.get(i).size();j++){
                Slot slot=slots.get(i).get(j);
                <span class="hljs-keyword">if</span>(slot.vehicle != <span class="hljs-keyword">null</span> &amp;&amp; slot.type.equals(type)) 
                    System.out.println(<span class="hljs-string">"Floor "</span> + (i+<span class="hljs-number">1</span>) + <span class="hljs-string">" slot "</span> + (j+<span class="hljs-number">1</span>));
            }
        }   
    }
</code></pre>
<p>Here, we check the opposite condition for the <code>vehicle</code> field. If it is not null, then that slot is occupied by a vehicle. We then check if the vehicle type matches with the requested type and then display the floor and slot no.</p>
<p>With this, we have our basic implementation of a parking lot. It is time to test out each functionality.</p>
<h2 id="heading-how-to-test-the-app">How to Test the App</h2>
<p>Since we do not have a UI or a testing framework, we are going to test each functionality manually from the driver code. Just like any Java application, ours will also start from the main method.</p>
<p>First, let's create a <code>ParkingLot</code> object. Each functionality of the parking lot will be accessed through this object.</p>
<pre><code class="lang-java"><span class="hljs-keyword">int</span> nFloors=<span class="hljs-number">4</span>;
<span class="hljs-keyword">int</span> nSlotsPerFloor=<span class="hljs-number">6</span>;
ParkingLot parkingLot = <span class="hljs-keyword">new</span> ParkingLot(<span class="hljs-string">"PR1234"</span>, nFloors, nSlotsPerFloor);
</code></pre>
<p>This creates a parking lot with 4 floors and 6 slots per floor, for a total of 24 slots.</p>
<pre><code class="lang-java">parkingLot.getNoOfOpenSlots(<span class="hljs-string">"car"</span>)

String ticket1 = parkingLot.parkVehicle(<span class="hljs-string">"car"</span>, <span class="hljs-string">"MH-03"</span>, <span class="hljs-string">"red"</span>);
String ticket2 = parkingLot.parkVehicle(<span class="hljs-string">"car"</span>, <span class="hljs-string">"MH-04"</span>, <span class="hljs-string">"purple"</span>);

parkingLot.displayOccupiedSlots(<span class="hljs-string">"car"</span>);
</code></pre>
<p>First, we display the number of open slots available for a car. Then, we park two cars and get the tickets. After parking, we display the occupied slots.</p>
<p>To run the program, click the run button in the top right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/blg1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>VS Code Editor</em></p>
<p>This gives the following output.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-34.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Output for the above test cases</em></p>
<p>Here, you can see the first available slots were assigned to the car.</p>
<p>Now, unpark the second vehicle and display the occupied slots.</p>
<pre><code class="lang-java">parkingLot.unPark(ticket2);
parkingLot.displayOccupiedSlots(<span class="hljs-string">"car"</span>);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>After Unparking</em></p>
<p>Let's try parking a truck now. Display available slots for a truck, park the truck, and display occupied slots.</p>
<pre><code class="lang-java">parkingLot.displayOpenSlots(<span class="hljs-string">"truck"</span>);
parkingLot.parkVehicle(<span class="hljs-string">"truck"</span>, <span class="hljs-string">"MH-01"</span>, <span class="hljs-string">"black"</span>);
parkingLot.displayOccupiedSlots(<span class="hljs-string">"truck"</span>);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-36.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Output after parking a truck</em></p>
<p>I'll park three more trucks and then try parking another one. Since there are no more truck slots available, it shows the following message:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-38.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Output if the parking lot is full</em></p>
<h2 id="heading-future-scope">Future Scope</h2>
<p>In this article, we have created a very basic command-line application for a parking lot. The application only does one thing – assigning and un-assigning a vehicle from a parking slot. But there are many other things you can add to this basic app:</p>
<ul>
<li><p>Use encapsulation wherever possible. Technically, this should not come under future scope. I have not used encapsulation here as I only wanted to focus on the design and logical part. But while working on actual applications, remember to encapsulate all the fields and access them through getters and setters.</p>
</li>
<li><p>Implement proper exception handling for scenarios where a slot is not available, a vehicle type is not allowed, or the ticket is invalid.</p>
</li>
<li><p>You can add more types of vehicles to the parking lot and use a different strategy to assign slot types.</p>
</li>
<li><p>You can use a different strategy to find out the first available slot.</p>
</li>
<li><p>You can expose all the logic we have discussed here as a backend service and create a UI for your parking lot with a database as well.</p>
</li>
</ul>
<p>You can find the code on GitHub <a target="_blank" href="https://github.com/KunalN25/parking-lot-app">here</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, we created a simple command-line application with Java from the requirements. From the list of requirements, we determined the design of the application and defined the classes needed. Then, we understood the workflow of our application.</p>
<p>After finalizing the design and the workflow, we jumped into the implementation. This simple application was an example of how you outline the requirements and based on those requirements, figure out the design and workflow of your application.</p>
<p>Before writing the code, you should know what classes you are going to use and the purpose of each class. This is true no matter how small or big an application you want to make. I hope this helps in your future projects.</p>
<p>If you are unable to understand the content or find the explanation unsatisfactory, comment your thoughts below. New ideas are always appreciated! Feel free to connect with me on Twitter. Until then, goodbye!</p>
<h3 id="heading-helpful-resources">Helpful resources</h3>
<ul>
<li><p><a target="_blank" href="https://workat.tech/machine-coding/practice/design-parking-lot-qm6hwq4wkhp8">workat.tech</a> has abundant resources on cracking interviews at top product-based companies. This post is inspired by one such resource.</p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/the-java-handbook/">Here's a Java handbook for beginners</a> that'll teach you a lot of foundational concepts.</p>
</li>
<li><p>And <a target="_blank" href="https://www.freecodecamp.org/news/object-oriented-programming-in-java/">here's a more advanced Java handbook</a> that covers more advanced object-oriented programming concepts.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a React App in 2024 ]]>
                </title>
                <description>
                    <![CDATA[ In 2024, there are more ways than ever to get your React projects started. But which one should you choose? In this article, I'm going to break down all of the major ways you can use to create a React app and which one you should pick based off of ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-react-app-in-2024/</link>
                <guid isPermaLink="false">66d03779dcd3a41034854bbe</guid>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Reed ]]>
                </dc:creator>
                <pubDate>Fri, 26 Jan 2024 13:44:45 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/build-react-app-2024.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In 2024, there are more ways than ever to get your React projects started. But which one should you choose?</p>
<p>In this article, I'm going to break down all of the major ways you can use to create a React app and which one you should pick based off of your project's needs.</p>
<p>I'll also include a quick guide at the end to show you exactly how to choose between each according to the type of project you're building.</p>
<p>Let's get started!</p>
<h2 id="heading-why-you-shouldnt-use-create-react-app">❌ Why You Shouldn't Use Create React App</h2>
<p>In 2023, the Create React App tool was deprecated, which means that it was no longer being maintained. Create React App has been the go-to way to make a new React project, but it's been dethroned by a number of different alternatives.</p>
<p>For that reason, Create React App is not an option I would recommend for creating a new React project in 2024.</p>
<h2 id="heading-how-to-create-a-react-app-with-vite">💨 How to Create a React App with Vite</h2>
<p>You may be asking, "What's a good replacement for Create React App?" That option is Vite.</p>
<p>Vite is ideal for making client-rendered React projects that run exclusively in the browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/vite.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vite docs home page</em></p>
<p>To spin up a new React project with Vite, you just have to run a single command:</p>
<pre><code class="lang-bash">npm create vite@latest my-react-app -- --template react
</code></pre>
<p>The great thing about Vite is, as its name indicates, it's much faster than virtually every alternative. Where Vite really shines is how quickly it runs in development.</p>
<p>Vite is unopinionated, however, which means you will likely need to install a suite of third-party libraries for basic functionality, like routing and data fetching.</p>
<p>If your application doesn't have a server or you are using an external API and it doesn't need server-side rendering, Vite is a perfect choice.</p>
<p>Additionally, Vite comes with its own config file, <strong>vite.config.js</strong>, which might require reading the documentation in order to configure things such as environment variables, build options, and image options.</p>
<h2 id="heading-how-to-create-a-react-app-with-nextjs">🥞 How to Create a React App with Next.js</h2>
<p>If you're looking for a way to build a React app that gives you a single-page app (SPA) experience but with server-side rendering and server components, Next.js is your choice.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/nextjs.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next.js docs home page</em></p>
<p>Next.js is the only option at the moment that comes with server components, which allows you to mark a component as <code>async</code> and <code>await</code> in some operation on the server.</p>
<pre><code class="lang-tsx">async function getData() {
  const res = await fetch('https://api.example.com/...');
  return res.json();
}

export default async function Page() {
  const data = await getData();

  return &lt;main&gt;{data}&lt;/main&gt;;
}
</code></pre>
<p>The benefit of server components is that you don't have to show a loading spinner in your app before fetching data. Server components allow you to ship far less JavaScript to the client, which leads to faster load times for your users.</p>
<p>Server components do require that you have a server, however, this means that it can't be deployed as simply as a client-rendered React app made with Vite.</p>
<p>Next.js is powerful because it comes with a ton of built-in features, such as a dedicated file-based routing, image optimization, and font loading, to name a few.</p>
<p>Next.js also allows you to tap into server actions, which is a new React feature that allows you to run server code by calling a function.</p>
<pre><code class="lang-tsx">// Server Component
export default function Page() {
  // Server Action
  async function create() {
    'use server'

    // ...
  }

  return (
    // ...
  )
}
</code></pre>
<p>Next.js also has route handlers, which lets you to make HTTP requests to an API endpoint. This is something that client-rendered React apps can't do, because there's no server.</p>
<p>With all of Next.js' benefits, it comes with a steeper learning curve. There are a number of Next.js-specific concepts that may seem to go against some React concepts you've already learned.</p>
<p>For example, within server components, you can't use React Hooks. This means that you have to rely on patterns such as storing state in the URL.</p>
<p>Despite the learning curve, Next.js is the most popular React framework and is relied upon to build impressive React apps that power small startups to Fortune 500 companies.</p>
<p>If you want to make something impressive with React, you can certainly do it with Next.js.</p>
<h2 id="heading-how-to-create-a-react-app-with-remix">💿 How to Create a React App with Remix</h2>
<p>Remix, like Next.js, is a React-based framework that has a different focus on web standards to deliver a better user experience. Remix allows you to also write server and client React code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/remix.png" alt="Image" width="600" height="400" loading="lazy">
<em>Remix docs home page</em></p>
<p>Remix prides itself on serving static and dynamic content faster than Next.js. This means it's equally good at building full-stack applications as well as static websites.</p>
<p>Instead of server components and server actions, Remix simply has actions. Actions allows you to handle data mutations on your server, which is anything that isn't a GET request. Actions are just simple functions with the name action.</p>
<p>To get data, you use simple functions called loaders. React Remix uses React Router under the hood. So if you're comfortable with React Router, you'll likely feel at home with Remix as well.</p>
<pre><code class="lang-tsx">export async function loader() {
  return json(await fakeGetTodos());
}

export default function Todos() {
  const data = useLoaderData&lt;typeof loader&gt;();
  return (
    &lt;div&gt;
      &lt;TodoList todos={data} /&gt;
      &lt;Form method="post"&gt;
        &lt;input type="text" name="title" /&gt;
        &lt;button type="submit"&gt;Create Todo&lt;/button&gt;
      &lt;/Form&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Remix is a bit more stable than Next.js and has a less steep learning curve, still allowing you to build just as impressive applications, with the only downside being that it's not nearly as popular as Next.js. So it doesn't have the same community support and libraries around it.</p>
<p>But if you do want something with server-side rendering and client-side rendering, Remix still remains a great option to build your next React project.</p>
<h2 id="heading-how-to-create-a-react-app-with-astro">🚀 How to Create a React App with Astro</h2>
<p>The newest way to build a React project is definitely the most performant. React apps can also be built using a framework called Astro.  </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/astro.png" alt="Image" width="600" height="400" loading="lazy">
<em>Astro docs home page</em></p>
<p>Astro's goal is deliver content to users quickly through something called "island architecture".</p>
<p>In short, this means that JavaScript is loaded only when the user needs them, making for a much more optimal user experience. If you want the fastest website possible, I'd highly recommend looking at Astro.</p>
<p>Astro supports server-side rendering and is great for SEO-focused websites that are largely static. However, what's neat about Astro is that it can also run code on the server if you choose to. It's not as popular to build fully dynamic, full-stack applications with Astro, but it is possible to do so.</p>
<p>Astro is also very flexible as it's not even tied to React. You don't need to use React whatsoever to build an Astro app, and you can use React alongside other frameworks such as Vue and Svelte within the same app.</p>
<p>If you're building a website that has posts or content that uses markdown or MDX, Astro should be your top choice. It uses a feature called "collections" to describe all the data within your markdown files so that you know exactly what content is going to be rendered in your React components.</p>
<p>Astro is gaining rapidly in popularity, and it's probably the best choice out there if you are interested in making a static website that doesn't need a database or authentication, or a website that is largely static.</p>
<h2 id="heading-so-what-should-i-choose">🤔 So What Should I Choose?</h2>
<p>If you've read up to this point and you're still trying to figure out which framework would be best for you to build a React project in 2024, here's the rundown:</p>
<ul>
<li>If you're just starting out and learning the React basics but want to build a simple or medium-sized project, stick with Vite.</li>
<li>If you want a full-stack framework with all the bells and whistles, like server components, and you don't mind spending time learning additional concepts, check out Next.js.</li>
<li>If you've tried Next.js and find some of its concepts difficult to understand, but still want to build a full-stack React application, definitely look into Remix.</li>
<li>Finally, if you have a static or content-driven website, and you don't really need a database or authentication, I would highly recommend using Astro.</li>
</ul>
<h2 id="heading-become-a-professional-react-developer">🏆 Become a Professional React Developer</h2>
<p>Looking for the ultimate resource to learn React from start to finish?</p>
<p>✨ <strong><a target="_blank" href="https://www.thereactbootcamp.com">Introducing: The React Bootcamp</a></strong></p>
<p>The bootcamp features every resource to help you succeed with React:</p>
<ul>
<li>🎬 200+ in-depth videos</li>
<li>🕹️ 100+ hands-on React challenges</li>
<li>🛠️ 5+ impressive portfolio projects</li>
<li>📄 10+ essential React cheat sheets</li>
<li>🥾 A complete Next.js bootcamp</li>
<li>🖼️ A complete series of animated videos</li>
</ul>
<p>Click below to try the React Bootcamp for yourself.</p>
<p><a target="_blank" href="https://www.thereactbootcamp.com"><img src="https://reedbarger.nyc3.digitaloceanspaces.com/reactbootcamp/react-bootcamp-cta-alt.png" alt="Click to join the React Bootcamp" width="600" height="400" loading="lazy"></a><br><em>Click to get started</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 7 React Projects to Build in 2024 ]]>
                </title>
                <description>
                    <![CDATA[ To be confident with using React, you need to build real-world projects. But what projects are worth building in 2024? I have put together a list of seven projects that I think are ideal for becoming a confident React developer, from simple to comple... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-projects-to-build-in-2024/</link>
                <guid isPermaLink="false">66d037d5871ae63f179f6bc3</guid>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Reed ]]>
                </dc:creator>
                <pubDate>Tue, 16 Jan 2024 12:07:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/mugshotbot.com_customize_color-orange-mode-light-pattern-bubbles-theme-two_up-url-https___gifcoins.io--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>To be confident with using React, you need to build real-world projects. But what projects are worth building in 2024?</p>
<p>I have put together a list of seven projects that I think are ideal for becoming a confident React developer, from simple to complex applications. This will give you some inspiration for what apps to build.</p>
<p>I'll also walk you through the entire tech stack I would use to build each project, as well as a summary of how to build each one step by step.</p>
<p>Let's get started!</p>
<h2 id="heading-chatgpt-ai-app">🤖 ChatGPT AI App</h2>
<p>As ChatGPT becomes more powerful, you can build impressive applications using the ChatGPT API.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/chatgpt.png" alt="Image" width="600" height="400" loading="lazy">
<em>ChatGPT AI app: Draw-a-UI</em></p>
<p>This is a great starter app because, for most applications, all you need to do is send text or an image to the ChatGPT API, give it some instructions, and it'll send a response back to you.</p>
<p>You could use the ChatGPT API to build a text summarizer, a translation app, an app that explains what a code snippet does. The possibilities are really endless.</p>
<p>One simple ChatGPT-powered AI app that I've built is "Draw a UI", where you can draw a quick mockup of a user interface, send it to ChatGPT, and it'll actually send you back the HTML that was generated according to your screenshot!</p>
<p>I would build this application using Next.js as well as the <code>tldraw</code> npm package, which allows you to draw pictures in your React app.</p>
<p>Next, send that screenshot to a Next.js route handler on the back end that uses the <strong>openai</strong> npm package to talk with ChatGPT, and then sends you back the HTML code.</p>
<h2 id="heading-personal-website">👨‍💻 Personal Website</h2>
<p>If you're not ready to build something very complex yet, a personal website is a great starting point. You can use it as a way to get comfortable working with JSX and CSS in React.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/portfolio.png" alt="Image" width="600" height="400" loading="lazy">
<em>A personal website made with Next.js and TailwindCSS</em></p>
<p>Learn how to create basic components, add images, links to your social media profiles, and so on. When you start building more projects, you can show them off on your website.</p>
<p>The great thing about a personal website is that you can extend it as much as you want. You can add your own integrated blog, or you can talk about things that you've learned or what you're working on at the moment.</p>
<p>In order to build your personal website, I would recommend using Next.js because it makes it so easy to build individual pages that are statically rendered, which is good for SEO.</p>
<p>For the images, you can use the integrated <code>next/image</code> library. And for making a blog, I'd highly recommend using the ContentLayer package , which you can use to write all of your blog posts as markdown or MDX.</p>
<p>ContentLayer is great because it makes your markdown content type safe so you know what data each blog post includes. This is also a great way to get started with TypeScript in React, although it may seem intimidating at first.</p>
<h2 id="heading-chat-app">💬 Chat App</h2>
<p>A truly dynamic web application would be a chat app, something that you likely use every day. It's good to build applications that you're familiar with because it gives you a good idea of what parts it's composed of.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/chat.png" alt="Image" width="600" height="400" loading="lazy">
<em>Chat App made with React, similar to WhatsApp</em></p>
<p>A chat app is simple in terms of its components. You only need a messages area, an input to type new messages, and a list of people to chat with.</p>
<p>It's a great project because it can be as simple or as complex as you like. To build something like this, I would use Vite to create the React project and power the backend with Supabase.</p>
<p>You don't need any server-side code here with Supabase, and it also gives you real-time chat functionality, entirely free. You can add authentication to identify users (using Supabase Auth), and put all the created users in a sidebar to chat with.</p>
<p>Then you can create a table for messages and send them off to Supabase whenever someone types in some text. To extend it further, you could make it possible for them to add images and video with Supabase Storage.</p>
<p>Finally, you can display messages in real-time using subscriptions depending on to the user you're chatting with.</p>
<h2 id="heading-e-commerce-app">💳 E-Commerce App</h2>
<p>The next type of app we'll talk about is an e-commerce app. </p>
<p>An e-commerce app can be used for selling physical or digital products with a one-time purchase feature.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/ecommerce.png" alt="Image" width="600" height="400" loading="lazy">
<em>An E-Commerce app with physical goods, made with Next.js</em></p>
<p>After the purchase, you need to deliver it to the customer. The e-commerce app could be very complex, but it doesn't have to be at the start. You just need to make a basic storefront with your products.</p>
<p>Give them an associated image with a description, as well as a buy button. You don't even need to add authentication. To build this, I would use Next.js integrated with Stripe to handle purchases.</p>
<p>The inventory system doesn't have to be very complex if you're selling a physical product. It could be as simple as having a number in a database that can be updated whenever stock is added and decreased whenever someone makes a purchase.</p>
<h2 id="heading-online-marketplace">🛒 Online Marketplace</h2>
<p>The online marketplace is an extension of the e-commerce app. It's a bit more complex because you're adding more products. </p>
<p>You might also consider adding extra features such as reviews, which are essential to online shopping.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/marketplace.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next.js store with an integrated shopping cart and reviews</em></p>
<p>The challenge in this case is adding a shopping cart. To make for a good user experience on a website with many products, you want to allow customers to add multiple products to their cart.</p>
<p>To add a shopping cart, I would use the same stack as before, Next.js and Stripe, to handle managing and purchasing the products. Fortunately, there's a great package called use-shopping-cart, which integrates perfectly with Stripe checkout. </p>
<p>You can use it to make a complete shopping cart with the ability to add and remove items, as well as clear the cart right out of the box.</p>
<p>For reviews, you could add a database layer like Supabase, or you could outsource reviews to a third-party service that allows you to integrate reviews, like Trustpilot, for example.</p>
<h2 id="heading-saas-app-software-as-a-service">🚚 SaaS App (Software as a Service)</h2>
<p>The final evolution in doing sales online with React is a SaaS application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/saas.png" alt="Image" width="600" height="400" loading="lazy">
<em>Gumroad inspired SaaS app made with Next.js</em></p>
<p>In this app, you provide customers access to a certain software service that you've made, usually for a monthly or yearly subscription fee.</p>
<p>You can create a SaaS application as a paid version of an app that you've already built, such as the AI app or the chat app.</p>
<p>In short, if you can build an application that users would pay for, either to be more productive, for entertainment, or to educate them, then all you need to do to make a SaaS app is to charge those customers a fee for using it.</p>
<p>A SaaS app could charge users based on usage or over a set period of time, such as a month or a year. </p>
<p>This can be done with the help of Stripe or a merchant of records such as Paddle, which makes taxes easier. Both of them can handle subscriptions.</p>
<p>I would recommend using Stripe Checkout to allow customers to manage their subscription and cancel it if necessary.</p>
<h2 id="heading-real-world-app-clone">📱Real-World App Clone</h2>
<p>Finally, the most ambitious project would be to build a clone of an app that you really like or that you use every day.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/clone.png" alt="Image" width="600" height="400" loading="lazy">
<em>A clone of the YouTube web app with React</em></p>
<p>An app clone is very difficult because you're usually cloning something that a big company is based off of. However, doing so is a great approach to leveling up your skills as a React developer because you have to think through about how a service is made.</p>
<p>If you were to clone something like YouTube, for example, you're not only going to build out the user interface and have it look like YouTube's, but you'll also need the functionalities that YouTube has, such as menus and drawers and notifications, and the ability to add and view videos, comments, likes, and so on.</p>
<p>If I were to build a YouTube clone, I would use either Supabase or a MySQL database like PlanetScale, along with Next.js, and authentication with Supabase or NextAuth.</p>
<p>I would build the user interface with TailwindCSS and Radix UI. Radix is a library that provides simple (primitive) components that can be easily styled, but are fully functional, which saves you a bunch of time.</p>
<p>To upload videos and media, I would use a platform dedicated to streaming videos such as Mux, which provides a great developer API.</p>
<p>For storing images and all other media and attachments, I would use Supabase Storage.</p>
<p>How far you take it really depends on your ambition and whether you want it to be a business of your own or a great portfolio project to show off to potential employers.</p>
<h2 id="heading-want-to-build-all-the-projects">🛠️ Want to Build All the Projects?</h2>
<p>You can learn exactly how to build each project within this list inside the all-new React Bootcamp:</p>
<p>✨ <strong><a target="_blank" href="https://www.thereactbootcamp.com">Introducing: The React Bootcamp</a></strong></p>
<p>You'll learn how to build every project in this list through hours of step-by-step videos, plus the complete source code to make them your own.</p>
<p>The bootcamp features every resource to help you succeed with React:</p>
<ul>
<li>🎬 200+ in-depth videos</li>
<li>🕹️ 100+ hands-on React challenges</li>
<li>🛠️ 5+ impressive portfolio projects</li>
<li>📄 10+ essential React cheat sheets</li>
<li>🥾 A complete Next.js bootcamp</li>
<li>🖼️ A complete series of animated videos</li>
</ul>
<p>Click below to try the React Bootcamp for yourself.</p>
<p><a target="_blank" href="https://www.thereactbootcamp.com"><img src="https://reedbarger.nyc3.digitaloceanspaces.com/reactbootcamp/react-bootcamp-cta-alt.png" alt="Click to join the React Bootcamp" width="600" height="400" loading="lazy"></a>  </p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
