<?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" version="2.0"><channel><title><![CDATA[Israel Mitolu's Blog]]></title><description><![CDATA[Self-taught Frontend Developer with a flair for engaging and dynamic User Experience.
I write articles about Frontend Technologies to share what I've learned an]]></description><link>https://blog.mitolu.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 21:41:01 GMT</lastBuildDate><atom:link href="https://blog.mitolu.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[2024: Beyond Grateful]]></title><description><![CDATA[If 2023 was a rollercoaster, 2024 felt like I was riding it with my hands in the air — terrified, thrilled, and somehow still holding onto snacks. It was a year of crazy highs, challenging lows, and a lot of unexpected twists. But before we dive into...]]></description><link>https://blog.mitolu.dev/2024-beyond-grateful</link><guid isPermaLink="true">https://blog.mitolu.dev/2024-beyond-grateful</guid><category><![CDATA[yearinreview]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Career]]></category><category><![CDATA[2024]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 31 Jan 2025 14:20:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736611007309/56d8994e-7ee0-4979-a743-03ed2db2e2c3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If 2023 was a rollercoaster, 2024 felt like I was riding it with my hands in the air — terrified, thrilled, and somehow still holding onto snacks. It was a year of crazy highs, challenging lows, and a lot of unexpected twists. But before we dive into all that, let me just say:</p>
<p>Happy New Year! <strong>🎉🎆</strong></p>
<p>If I’m being honest, I didn’t journal as much in 2024, so this is me trusting my memory to do its thing. Hopefully, it doesn’t fail me. 😂</p>
<p>Let’s get into it!</p>
<p>I ended my previous <a target="_blank" href="https://israelmitolu.hashnode.dev/2023-the-rollercoaster-ride">Year in Review</a>, by quoting Bo Peabody:</p>
<blockquote>
<p><em>“The best way to ensure that lucky</em> <em>things happen</em> <em>is to make sure a lot of things happen.”</em></p>
</blockquote>
<p>Well, I took that to heart and did <em>a lot</em> — some of it intentional, some of it just trying to survive my final year of university.</p>
<h2 id="heading-trying-times">Trying Times</h2>
<p>After the storms of 2023 — losing my job, getting scammed, and everything that followed — I picked up the lessons, shared my experiences, and moved forward, believing firmly in Romans 8:28:</p>
<blockquote>
<p><em>“…all things work together for good to those who love God, to those who are called according to His purpose.”</em></p>
</blockquote>
<p>But I wasn’t prepared for everything that came in the first quarter. I don’t think I’d ever felt as exhausted or helpless as I did then.</p>
<p>I was broke — so broke that I went days without a proper meal. Just three months earlier, I had invested everything into a new laptop, and whatever little I had left went into buying data and aggressively applying for jobs.</p>
<p>I sent out applications daily — on LinkedIn, WhatsApp groups, Twitter — anywhere I could find an opportunity. I revamped <a target="_blank" href="https://docs.google.com/document/d/1cMJqgvbjr5_Eq7rIU3-i0QfpJR8I3Sn3DpliYiZyPh8/edit?usp=sharing">my resume</a>, optimized my <a target="_blank" href="https://mitolu.dev/">portfolio</a>, and landed a few interviews, but I kept falling short of the final stages. At some point, I considered selling my laptop and giving up on tech altogether. That’s how bad it got.</p>
<p>Meanwhile, my first-semester exams were ongoing, but I was barely prepared. Survival was m<a target="_blank" href="https://docs.google.com/document/d/1cMJqgvbjr5_Eq7rIU3-i0QfpJR8I3Sn3DpliYiZyPh8/edit?usp=sharing">y prio</a>rity.</p>
<p>Desperate for income, I started taking on guitar recording sessions and small graphics design gigs within the university — designing FYB (Final Year Brethren) banners, dinner invitations, and other event materials. The pay wasn’t great (₦5K jobs weren’t exactly life-changing), but they helped me stay afloat.</p>
<p>By March, my partner pointed out that I was having frequent mood swings 😂. Not having money hits hard, bruh. In April, a few people even commented that I was losing weight — I knew exactly why.</p>
<h2 id="heading-to-focus-on-school-or-tech">To Focus on School or Tech?</h2>
<p>Then, in May, things took a turn. Gigs started rolling in, one after another, as if God had strategically placed me in people’s minds. Or maybe it was some form of <strong>divine strategic positioning</strong> — a concept I’ll revisit later.</p>
<p>After months of rejection and struggle, I finally started getting well-paying gigs! But just as things were looking up, a new challenge arose: balancing tech with school.</p>
<p>I had neglected my final-year project for too long, and my supervisor wasn’t having it. In my mind, I thought, <em>I’ll just rush everything and be fine.</em> Oh, how wrong I was!</p>
<p>By June, mid-semester tests were here, and I was barely prepared. My grades reflected that. Honestly, I didn’t even spend much time in my department throughout the year. Sure, I attended most classes, but I’d leave immediately afterward to focus on work.</p>
<h2 id="heading-lock-in-season">Lock in Season</h2>
<p>From June to September, I worked like <strong>a</strong> machine running on sheer willpower. I was juggling three contract jobs at once, and it was <em>a lot</em>. Cherie called it “Extreme Sports” 😆</p>
<p>Then, in the first week of June, something incredible happened. I got a DM on LinkedIn from a recruiter at <a target="_blank" href="https://gitroll.io/">Gitroll</a>. An employer was interested in my profile!</p>
<p>The interview went great — one of my favourite parts was explaining my thought process behind animations and micro-interactions in my projects (something I’m passionate about). A week later, I received an offer letter. Two weeks later, I officially started the job.</p>
<p>God did! 🙌</p>
<p>It’s been quite interesting, it feels like the job was curated for me, and am thankful for that. I get to provide great value while enjoying my work. But in the first few months, impostor syndrome hit hard. After months of struggling to land opportunities, it felt unreal to finally be in a position I hadn’t even applied for. <em>“Do I really deserve this?”</em> I often wondered.</p>
<p>Looking back, though, I realize it wasn’t just luck. The quality of my projects spoke for me, and my involvement in open-source projects helped position me as a strong candidate. But beyond strategy and effort, the way things aligned felt undeniably <strong>divine</strong>. God was really intentional.</p>
<p>Meanwhile, school became an unbearable weight. My final-year project hit setbacks, and at one point, it seemed like I wouldn’t graduate. But people stood by me — especially my parents 🤍. With their support, I overcame the challenges and resolved everything just in time.</p>
<h2 id="heading-life-after-school"><strong>Life After School</strong></h2>
<p>Fast forward to October — after my exams and final sign-out — I moved into my own apartment and started building my dream workspace!</p>
<p>I had always wanted to live alone, and now I finally could. 😄 But I quickly realized that post-university life can feel a little isolating. Thankfully, I enjoy my own company and loved ones visit often, so it’s not too bad.</p>
<h2 id="heading-convocation-and-baecation">Convocation and Baecation 🌹</h2>
<p>Finally, in December, I graduated! 🥳 After eight years, the journey had finally come to an end.</p>
<p>This chapter of my life was filled with incredible friends, unforgettable experiences, challenges, and pivotal decisions — like switching departments, joining tech communities, and getting involved in church and music.</p>
<p>The last quarter of the year was the best. Plans I had envisioned for years finally became reality, and it all felt surreal.</p>
<p>I had promised myself last year that I would travel more, but I only left my state once — right at the end of 2024. But that one trip? Absolutely worth it. I spent an amazing time with my Cherie, and I look forward to more adventures ahead.</p>
<hr />
<h2 id="heading-2024-in-highlights"><strong>2024 in Highlights</strong></h2>
<h2 id="heading-wins"><strong>Wins 🎉</strong></h2>
<ul>
<li><p>Landed a full-time job</p>
</li>
<li><p>Graduated from university</p>
</li>
<li><p>Moved into my own apartment</p>
</li>
<li><p>Worked on exciting projects that went live</p>
</li>
<li><p>Got myself a couple of gadgets and things that had been on my “To Buy” list for years</p>
</li>
<li><p>Joined an amazing team and worked with incredible people</p>
</li>
<li><p>Started building my dream workspace</p>
</li>
<li><p>Achieved a 10x increase from my previous annual income</p>
</li>
<li><p>Supported family &amp; friends when they needed me most</p>
</li>
<li><p>Explored a new city</p>
</li>
<li><p>Expanded my library with new books</p>
</li>
<li><p>Proved to myself that I could thrive with the resources I had at any given time</p>
</li>
<li><p>Almost had a carryover in my final year but got it resolved.</p>
</li>
<li><p>Finally got swag from Hashnode! 😄</p>
</li>
</ul>
<h2 id="heading-setbacks"><strong>Setbacks 🚧</strong></h2>
<ul>
<li><p>Didn’t upskill as much as planned (API Documentation, React Native, Tech Writing.</p>
</li>
<li><p>Had to withdraw from a few gigs, disappointing some clients</p>
</li>
<li><p>Faced multiple rejections from job applications which caused doubt in my abilities for a little while.</p>
</li>
<li><p>Didn’t invest as much as I wanted to</p>
</li>
<li><p>Didn’t read as many books as I had hoped</p>
</li>
<li><p>Spent <em>too much</em> money eating out after moving — could’ve cooked more</p>
</li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>2024 was a year of resilience, breakthroughs, and divine alignment. The struggles tested me, but the victories reassured me.</p>
<p>I’ve laid the foundation for 2025, and I believe it will be a year of massive growth — personally, professionally, and spiritually.</p>
<p>Here’s to a new year of building, thriving, and becoming! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[2023: The Rollercoaster Ride]]></title><description><![CDATA[Last year, I wrote my first year-in-review where I spoke about my journey from being completely broke to landing my first gigs as a technical writer, and how I'm on a trajectory towards the moon. The positive feedback and connections I received from ...]]></description><link>https://blog.mitolu.dev/2023-the-rollercoaster-ride</link><guid isPermaLink="true">https://blog.mitolu.dev/2023-the-rollercoaster-ride</guid><category><![CDATA[yearinreview]]></category><category><![CDATA[Year in a Review]]></category><category><![CDATA[2023]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 26 Jan 2024 13:02:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704464993378/d492d943-0ccd-48fc-a743-f7332570c8c2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last year, I wrote my <a target="_blank" href="https://israelmitolu.hashnode.dev/my-2022-year-in-review-to-the-moon">first year-in-review</a> where I spoke about my journey from being completely broke to landing my first gigs as a technical writer, and how I'm on a trajectory towards <em>the moon</em>. The positive feedback and connections I received from the tech community encouraged me to continue sharing my experiences through these articles.</p>
<p>However, I approached writing this year's review with a sense of hesitation and scepticism, given the challenges I faced in 2023, which is why it's arriving later than anticipated. Nevertheless, I'm committed to being transparent and accountable, both for my growth and for the opportunity to reflect on my journey in the future. So, here it goes.</p>
<p>While last year was marked by a mindset of a straight upward trajectory, as inferred from the title <em>"To the Moon"</em>. Life, as I discovered, rarely follows a straightforward path to success.</p>
<p>This past year was a very "special" one because it was essentially a crazy rollercoaster ride, which is a metaphor that means it had its ups and downs. Mostly scary but yet exciting. 2023 taught me a lot of lessons about myself and people in general, and I will share some of those lessons and my experiences in this post. I do hope that you learn some valuable lessons.</p>
<h2 id="heading-a-promising-start">A Promising Start</h2>
<p>This year started with exams. I started writing exams on the 28th of December, and they ran all through January, so I was more focused on school and making sure that I got good grades. I had not studied for most of the semester (I like to crashread🤫)</p>
<p>I also quit a job I was working at in February because I wasn't growing and felt like I was wasting time there. Also, I was actively writing articles which sorted the bills, so I wasn't bothered.</p>
<p>Overall, the first quarter of 2023 was amazing. I was super energized and enthusiastic about what I'd accomplish. My work, finances, academics, and social life were all good.</p>
<p>Then it came...</p>
<h2 id="heading-the-first-dip">The First Dip</h2>
<p>My primary goal for the year was to buy a MacBook, specifically the 2020 M1 MacBook Pro, so I started working hard and saving towards it. By May, I had saved up $1k (~₦700k at the time) for it - a sum that filled me with pride and excitement as I anticipated treating myself. It was a significant amount, one that I had never had at my disposal before, and I relished the sense of <em>accomplishment</em>.</p>
<p>However, my excitement was short-lived. Shortly after, I fell victim to an elaborate scam, losing all the money I had painstakingly saved. The setback was sudden and devastating.</p>
<blockquote>
<p>"Progress happens too slowly to notice, but setbacks happen too quickly to ignore." — Morgan Housel</p>
</blockquote>
<p>Initially, I found it difficult to accept the reality of the situation and chose to ignore it, focusing instead on my mid-semester tests and approaching exams in June. Despite applying for writing gigs to supplement my income during this period, I encountered a notable lull in opportunities. It was the first time in my writing career that I went an entire month without securing any gigs. Fortunately, I had some savings stored elsewhere, providing a temporary buffer.</p>
<p>July came, and I still hadn't gotten any gigs, so I decided to learn MongoDB and work on some <a target="_blank" href="https://github.com/israelmitolu">projects</a>. I had been too focused on writing for months and didn't code as much as I wanted.</p>
<blockquote>
<p><em>Shege</em> is a native Yoruba term used to describe hardships, and damn, I saw shege this year.</p>
</blockquote>
<p>I was in denial for a long time, thinking <em>"People can't be that heartless. Right?"</em>. At some point in July, I started to get low on cash and came to the fact that I'd been scammed hard, I opened up my goal tracker in Notion and added only one thing: "Survive".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695018848884/aa1d0c75-1f96-4856-8862-162f9f58e1c0.png" alt class="image--center mx-auto" /></p>
<p>This was very different from the preceding months where the monthly goals and achievements were filled up. At this point, I had little motivation and was trying to pick myself back up. I was tired and didn't want to do anything anymore.</p>
<h2 id="heading-more-shege">More Shege</h2>
<p>During this challenging time, I confided in a few close friends about how the last quarter went, and their support and encouragement helped me get through my "Anger" phase. They also helped by sending relevant job opportunities.</p>
<p>Towards the end of July, I landed a contract job that initially seemed promising. Not just because I needed a job urgently, but because of how I got it. The client stumbled upon my <a target="_blank" href="https://israelmitolu.netlify.app">portfolio</a> and said she wanted features in one of my projects implemented on her project, so I accepted the offer and got on board.</p>
<p>What followed was a whirlwind of intense labour over the next four months. My schedule was consumed by work, leaving little to no time for socializing, engaging on social media, or taking care of my health. Why you ask? The workload was too much, and it didn't help that my laptop was slow. I was also trying to make up for the money I lost, so I kept on pushing and grinding.</p>
<p>It got to a point in August, I would just cry for no particular reason, which is extremely rare for me. I would keep saying "<em>I am just trying my best</em>". I'd be honest with you. Tech is hard, and harder for a Nigerian student without financial support, data resources or a good laptop, trying to hustle way through tech. In the end, the pay wasn't quite worth the stress and time spent.</p>
<h2 id="heading-healing">Healing</h2>
<p>So, I decided to quit and terminate the contract because I was losing my mind. After that, I travelled to Ogun for the RCCG International Youth Conference in October to clear my mind, see my friends, and fellowship with other young believers. The four days spent there were a balm to my soul. In those moments, I felt the reassuring presence of God, affirming that brighter days were indeed on the horizon.</p>
<p>Upon my return, I decided to take some days off to relax and wind off. Went through my goals for the year and decided to take on some of them. So, I bought a new laptop, started reading books more frequently, started weight training and became more intentional about prayer.</p>
<p>I also joined a couple of friends in building <a target="_blank" href="https://concealed.vercel.app">Concealed</a>, and it's been cool so far. You can check it out, as it has a lot of <a target="_blank" href="https://concealed.hashnode.dev/">cool features</a> you can explore.</p>
<p>In all aspects of my life, I have been the recipient of God's grace and provision. Towards the end of the year, I received a few gigs through referrals that allowed me not to end the year completely broke 😂. I also received offers to work at two startups, however, the offered salaries didn't align with my values and worth. Recognizing the importance of maintaining my motivation and well-being, I chose to decline those offers. It was a decision rooted in self-respect and a commitment to prioritize my mental and emotional health over monetary gain.</p>
<h2 id="heading-lessons-learned">Lessons Learned</h2>
<ul>
<li><p><strong>Just because you can purchase it doesn't mean you can afford it</strong>. While I was recovering from the loss, I was able to secure some other gigs, which paid a decent amount of money, that could allow me to buy some items on my wishlist for the year. But I learned that's just a way back to the trenches 😬 I learned this from Morgan Housel's Psychology of Money.</p>
</li>
<li><p><strong>Speak up for yourself</strong>. If you are highly agreeable or a people-pleaser, people will take advantage of you and do it over and over again with no remorse. So, please learn how to communicate your wants, and put yourself first, rather than making people treat you anyhow.</p>
</li>
<li><p><strong>Save for the rainy days</strong>. This is something everyone says, but you really should take it seriously. Sometimes, your savings might just be the one to save you, to be honest. One solid piece of advice I learned is to have at least 6 months' worth of savings such that even if you were to lose your job, you'd still be fine.</p>
</li>
<li><p><em>"<strong><strong>Getting money requires taking risks, being optimistic, and putting yourself out there. But keeping money requires the opposite of taking risks. It requires humility, and fear that what you’ve made can be taken away from you just as fast.</strong></strong>"</em> — <em>Morgan Housel</em></p>
</li>
<li><p><strong>If a deal sounds too good to be true, it probably is</strong>. Of course, if you get a good job offer, I'm not saying it's untrue, but be very smart and rational before you agree to do any business. Always, peruse contracts well before signing.</p>
</li>
<li><p><strong>As a freelancer, collect some payment upfront before starting any project</strong>. Some folks are just out there to waste your time.</p>
</li>
<li><p>Invest in self-improvement by reading books, honing your existing skills, and acquiring new ones. Save and invest wisely, making decisions that will benefit your future self. Engage in activities that bring you joy and fulfilment, ensuring that your actions today contribute to a brighter tomorrow.</p>
</li>
</ul>
<h2 id="heading-the-bright-side-wins">The Bright Side: Wins</h2>
<ul>
<li><p>I grew as a Frontend Engineer. Had more experiential knowledge of integrating APIs, learning the intricacies of business logic and improving my efficiency in crafting pixel-perfect designs. Worked almost exclusively with React, NextJS and TypeScript.</p>
</li>
<li><p>I spent a lot of quality time with my loved ones and had a lot of fun too.</p>
</li>
<li><p>Started learning React Native, and it's interesting so far. Can't wait to build cool apps soon.</p>
</li>
<li><p>Wrote 18 paid articles and 5 articles on my <a target="_blank" href="https://israelmitolu.hashnode.dev/">blog</a>.</p>
</li>
<li><p>I finally started building my library! I bought 10 books on Christian faith, Psychology, and Finance.</p>
</li>
<li><p>Although I planned to read at least one book in a month, I ended up reading just 5 in total 😅. Yes, it's a win for me 😌</p>
</li>
<li><p>I bought a sleek laptop with all the specs I need...for now 🙃</p>
</li>
<li><p>I had a set of dumbbells made for me, which helped me work out more. I'll confess, I'm tempted to work out my arms more.</p>
</li>
<li><p>I got myself a brand new guitar and posted a <a target="_blank" href="https://www.instagram.com/reel/CtNA5wSoAWG/?utm_source=ig_web_copy_link&amp;igsh=ODhhZWM5NmIwOQ==">video</a> on IG(after so long 😂)</p>
</li>
<li><p>The experience of the year helped to build my resilience.</p>
</li>
<li><p>Lastly, I'm grateful that I am still alive. When there's life, there's hope.</p>
</li>
</ul>
<h2 id="heading-setbacks">Setbacks</h2>
<ul>
<li><p>I let laziness and procrastination get the best of me.</p>
</li>
<li><p>Aside from all the <em>shege</em> I mentioned earlier, I also made a loss of ~1k CAD from a client who just wanted to get work done without paying 🤦🏾‍♂️, but that's a story for another day.</p>
</li>
<li><p>I didn't build some of the cool project ideas I wanted to.</p>
</li>
<li><p>Even though I had so many article ideas, I didn't write as much on my blog and Freecodecamp. Those ideas are still in my drafts, so I'd find time to work on them.</p>
</li>
<li><p>I applied to a lot of jobs, and consequently received tons of rejection emails.</p>
</li>
</ul>
<h2 id="heading-plans-for-2024">Plans for 2024</h2>
<p>In 2024, I'll approach my goals with a sense of flexibility rather than rigid absolutes, acknowledging that not every goal from the previous year was achieved. With that in mind, here are some aspirations for the new year:</p>
<ul>
<li><p>Securing a full-time job in either <a target="_blank" href="https://linktr.ee/israelmitolu">Frontend roles or Technical writing</a>, and I welcome any referrals! 😃</p>
</li>
<li><p>Explore new places and travel more.</p>
</li>
<li><p>Collaborate with amazing people, devs and writers.</p>
</li>
<li><p>Delve into API documentation to enhance my technical skills.</p>
</li>
<li><p>Explore more on mobile development.</p>
</li>
<li><p>Try my hands on some Backend technologies(Rust, Go)</p>
</li>
<li><p>Advocate for my achievements and skills more assertively.</p>
</li>
<li><p>Work on being consistent, despite the demands of my final year in uni and other responsibilities.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I extend my deepest gratitude to the following individuals who stood by me during the toughest of times: my girlfriend, my parents, Vee, Peace, Promise, Anu, Israel, Cess, and Vicky. Your unwavering support and love mean the world to me 💖</p>
<p>To those who have spoken on my behalf and offered encouragement throughout my tech journey, I am sincerely grateful for your guidance and belief in me.</p>
<p>I am sincerely hopeful for the future. I believe that 2024 will be amazing. I'd leave this quote with you, which I aim to imbibe this year.</p>
<blockquote>
<p>"The best way to ensure that lucky things happen is to make sure a lot of things happen." — Bo Peabody</p>
</blockquote>
<p>Here's to a year filled with abundance and success. Cheers to making great things happen! 🥂</p>
<p>Thank you for reading! If you find this post insightful, kindly like, drop a comment and share it with someone who might need it.</p>
<p>Happy New Year! 🎉🎉</p>
]]></content:encoded></item><item><title><![CDATA[Getting Started With Web Scraping in Playwright]]></title><description><![CDATA[As the internet continues to expand exponentially, so does the wealth of information it holds. For businesses, researchers, and data enthusiasts, harnessing this information is crucial for gaining a competitive edge.
Web scraping has emerged as a pow...]]></description><link>https://blog.mitolu.dev/getting-started-with-web-scraping-in-playwright</link><guid isPermaLink="true">https://blog.mitolu.dev/getting-started-with-web-scraping-in-playwright</guid><category><![CDATA[web scraping]]></category><category><![CDATA[playwright]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 27 Oct 2023 12:06:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407573684/6d47c6c2-e276-4c2b-8c93-a41d23534185.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As the internet continues to expand exponentially, so does the wealth of information it holds. For businesses, researchers, and data enthusiasts, harnessing this information is crucial for gaining a competitive edge.</p>
<p>Web scraping has emerged as a powerful solution. With its ability to extract data from websites, web scraping has become an indispensable skill for data-driven decision-making.</p>
<p>Among the myriad of web-scraping tools available, Playwright, an automation library, shines as an exceptional choice. Its versatility and ease of use have made it a favorite among developers looking to harness the potential of web scraping efficiently.</p>
<p>In this post, I'll walk you through the process of getting started with web scraping using Playwright. We'll cover setting up the environment, learn the basics of Playwright, and demonstrate how to perform common web-scraping tasks with practical examples.</p>
<h2 id="heading-what-is-playwright"><strong>What Is Playwright?</strong></h2>
<p>Playwright is an open-source Node.js library designed to automate and interact with web browsers. It was created by Microsoft's web platform team and is particularly well-suited for <a target="_blank" href="https://www.proxyrack.com/blog/what-is-headless-browser-is-headless-browser-is-good-for-web-scraping/">headless browser</a> automation and web-scraping tasks. It provides a high-level API that makes it easier for developers to navigate, interact, and extract data from web pages.</p>
<h3 id="heading-key-features-of-playwright"><strong>Key Features of Playwright</strong></h3>
<ul>
<li><p><strong>Cross-browser support</strong>—Playwright supports all modern browsers, including Google Chrome, Firefox, Microsoft Edge, and Safari with WebKit.</p>
</li>
<li><p><strong>Headless and headed modes</strong>—Playwright can operate in headless mode (without a visible browser window) or headed mode (with a visible browser window). This flexibility enables debugging and monitoring when necessary.</p>
</li>
<li><p><strong>Powerful element selection</strong>—Playwright's advanced selectors make it easy to locate and interact with elements on a webpage using various strategies like accessibility attributes, CSS selectors, and text matching.</p>
</li>
<li><p><strong>Auto-wait APIs</strong>—Playwright offers intelligent waiting mechanisms that ensure the webpage has loaded completely before proceeding with interactions.</p>
</li>
<li><p><strong>Device emulation</strong>—With Playwright, you can emulate various devices, such as smartphones or tablets, to scrape mobile-specific data or test website responsiveness.</p>
</li>
<li><p><strong>Scalability</strong>—Playwright can handle multiple browser instances concurrently, making it a scalable solution for large-scale scraping tasks.</p>
</li>
<li><p><strong>Cross-language</strong>—Playwright is available in multiple languages such as JavaScript, Typescript, Python, and .NET, making it accessible to a wide range of developers.</p>
</li>
<li><p><strong>Cross-platform</strong>—With Playwright, you can test and automate web applications on Windows, macOS, and Linux operating systems locally or on CI.</p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To get started with Playwright, you need to have the following installed on your local machine:</p>
<ul>
<li><p><strong>Node.js</strong>—Playwright is a Node.js library, so you'll need <a target="_blank" href="https://nodejs.org/en/download">Node.js</a> and <a target="_blank" href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">npm</a> (Node Package Manager) installed. You can download the latest version of Node.js from the official website and follow the installation instructions for your operating system.</p>
</li>
<li><p><strong>Code editor</strong>—You'll need a text editor to write your JavaScript code. Popular options include Visual Studio Code, Sublime Text, or Atom. I recommend using <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a>.</p>
</li>
</ul>
<h2 id="heading-setting-up-the-environment"><strong>Setting Up the Environment</strong></h2>
<p>Once you have the prerequisites installed, open your terminal and run the following command to create a new directory for your project. Navigate to this directory using the cd command:</p>
<pre><code class="lang-bash">mkdir playwright-web-scraping
</code></pre>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> playwright-web-scraping
</code></pre>
<p>Next, initialize a new Node.js project by running</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>This will create a new <strong>package.json</strong> file in your project directory.</p>
<h3 id="heading-installing-playwright"><strong>Installing Playwright</strong></h3>
<p>Now you can install the Playwright package by running the following command:</p>
<pre><code class="lang-bash">npm install playwright
</code></pre>
<p>With Playwright and the required dependencies installed, we're ready to start scraping the web!</p>
<h2 id="heading-basic-web-scraping-with-playwright"><strong>Basic Web Scraping with Playwright</strong></h2>
<p>To start off, let's briefly see how to use Playwright for browser automation. This will help you understand the basics of interacting with web pages programmatically.</p>
<p>Create a new JavaScript file, <strong>index.js</strong>, in your project directory. Add the following code to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { chromium } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'playwright'</span>);
(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> chromium.launch({ <span class="hljs-attr">headless</span>: <span class="hljs-literal">false</span> });
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">'https://google.com'</span>);
  <span class="hljs-keyword">await</span> page.waitForTimeout(<span class="hljs-number">5000</span>);
  <span class="hljs-keyword">await</span> page.screenshot({ <span class="hljs-attr">path</span>: <span class="hljs-string">'google-home.png'</span> });
  <span class="hljs-keyword">await</span> browser.close();
})();
</code></pre>
<p>In this example, we import the <strong>chromium</strong> object from Playwright, which allows us to control the Chromium browser. We then use an asynchronous <a target="_blank" href="https://www.google.com/url?q=https://developer.mozilla.org/en-US/docs/Glossary/IIFE&amp;sa=D&amp;source=editors&amp;ust=1691476742017362&amp;usg=AOvVaw1V9UUoVyBI9cw0-G3B5laE">IIFE</a> (immediately invoked function expression) to define our scraping script.</p>
<p>To run the script, execute the following command in your terminal:</p>
<pre><code class="lang-javascript">node index.js
</code></pre>
<p>If everything goes well, you'll see that the script performs the following actions:</p>
<ol>
<li><p>It'll open a new Chromium browser window. Since we set the headless option to false, a new browser window will pop up when the script is executed. Setting it to the default value of <strong>true</strong> will run the script in headless mode, i.e., without a visible browser window.</p>
</li>
<li><p>The browser will navigate to the Google homepage.</p>
</li>
<li><p>It'll wait for five seconds using the <strong>waitForTimeout()</strong> method. This gives the website some time to load and display content before taking a screenshot.</p>
</li>
<li><p>After waiting, it'll take a screenshot of the current page and save it as <strong>google-home.png</strong> in the project directory.</p>
</li>
<li><p>Finally, it closes the browser.</p>
</li>
</ol>
<p>You should see a new browser window briefly appear. Navigate to <a target="_blank" href="https://google.com">https://google.com</a>, take a screenshot, and then close the window. The screenshot <strong>google-home.png</strong> will be saved in your project directory</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407229800/dc0003da-e8e2-4ab1-b9da-730d289aa7e4.webp" alt class="image--center mx-auto" /></p>
<p>This simple example demonstrates the basic workflow of using Playwright to automate browser actions.</p>
<p>Now, let's move on to web-scraping tasks!</p>
<h2 id="heading-scraping-text-from-a-webpage"><strong>Scraping Text From a Webpage</strong></h2>
<p>Let's start with a simple example of extracting text from a webpage. For this example, we'll extract the main heading on <a target="_blank" href="https://www.google.com/url?q=https://www.proxyrack.com/&amp;sa=D&amp;source=editors&amp;ust=1691476742018889&amp;usg=AOvVaw18B3D7ZaBXzaxOBXXsnNHE">Proxyrack's homepage</a>.</p>
<p>Add the following code to your index.js file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { chromium } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'playwright'</span>);
(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> chromium.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">'https://www.proxyrack.com'</span>);
  <span class="hljs-keyword">const</span> headingText = <span class="hljs-keyword">await</span> page.$eval(<span class="hljs-string">'h1'</span>, <span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> el.textContent);
  <span class="hljs-built_in">console</span>.log(headingText);
  <span class="hljs-keyword">await</span> browser.close();
})();
</code></pre>
<p>Playwright provides a number of <a target="_blank" href="https://playwright.dev/docs/locators">locators</a> to find elements on a webpage. In this example, we used the <strong>page.$eval()</strong> method to locate and extract the text content of the first <strong>h1</strong> element on the page. The method takes two arguments: a selector identifier and a function. The function receives the element as an argument and returns the result of the function—in this case, the text content of the element.</p>
<p>Upon running the script, you should see the following output in your terminal:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407302980/43024912-0081-4a2d-83ce-7a60a26bb0d3.webp" alt class="image--center mx-auto" /></p>
<p>Playwright also lets us use CSS selectors to locate elements. Let's see how to do so using the element's class.</p>
<p>Navigate to the page in your browser and right-click on the heading. Select "Inspect" from the context menu to open the browser's developer tools.</p>
<p>Alternatively, you can use the keyboard shortcut <strong>Ctrl + Shift + I</strong> (Windows/Linux) or <strong>Cmd + Opt + I</strong> (Mac).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407340141/1be3ea91-a074-4b67-8344-8cfeba914cec.webp" alt class="image--center mx-auto" /></p>
<p>You'll notice that the heading has three classes: <strong>c-PJLV</strong>, <strong>c-PJLV-dWIXpI-variant-huge</strong>, and <strong>c-PJLV-hyvuql-weight-bold</strong>.</p>
<p>Let's go with the second one, as it's more specific. It's important to choose a selector that is unique to the element you want to scrape. If you use the first class, you'd be selecting other elements on the page, as it's a shared class.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { chromium } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'playwright'</span>);
(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> chromium.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">'https://www.proxyrack.com'</span>);
  <span class="hljs-keyword">const</span> headingText = <span class="hljs-keyword">await</span> page.$eval(<span class="hljs-string">'h1'</span>, <span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> el.textContent);
  <span class="hljs-built_in">console</span>.log(headingText);
  <span class="hljs-keyword">await</span> browser.close();
})();
</code></pre>
<h2 id="heading-scraping-text-from-multiple-elements"><strong>Scraping Text from Multiple Elements</strong></h2>
<p>When scraping websites, it's common that you'd want to get multiple elements. For example, you might want to extract the titles of all the articles on a blog or the names of all the products on an e-commerce website.</p>
<p>In this example, we'll scrape the details of the repositories on the NodeJS topic on GitHub.</p>
<p>Navigate to the <a target="_blank" href="https://github.com/topics/nodejs">NodeJS topic page</a> and open the browser's developer tools. Inspect the first repository on the page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407374224/49abeb35-cbef-4886-ad3e-a98cbe79ca6a.webp" alt class="image--center mx-auto" /></p>
<p>Here, you'll notice that each repository is contained in an <strong>article</strong> element with the five classes. We'll use one of them, the <strong>border</strong>, to locate all the repositories on the page.</p>
<p>To find the selector for the repository owner and the other data we want to extract, we need to move down the DOM tree. The repo owner is located within the <strong>h3 a</strong>. Follow the same process to find the selectors for the other data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407396365/4d6af63e-3881-4e1e-80d0-ee2a58ec3b03.webp" alt class="image--center mx-auto" /></p>
<p>Using the selectors, let's extract the data from the page and store it in an array.</p>
<p>Create a new file called <strong>github.js</strong> and initialize it with the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { chromium } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'playwright'</span>);
(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> chromium.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">'https://www.proxyrack.com'</span>);
  <span class="hljs-keyword">const</span> headingText = <span class="hljs-keyword">await</span> page.$eval(
    <span class="hljs-string">'.c-PJLV-dWIXpI-variant-huge'</span>,
    <span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> el.innerText
  );
  <span class="hljs-built_in">console</span>.log(headingText);
  <span class="hljs-keyword">await</span> browser.close();
})();
</code></pre>
<p>To extract the data from the page, we'll use the <strong>page.$$eval()</strong> method, which enables us to collect data from multiple elements that share a selector.</p>
<p>Copy the following code into the <strong>github.js</strong> file to extract the data from the page:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> repositoryList = <span class="hljs-keyword">await</span> page.$$eval(<span class="hljs-string">'article.border'</span>, <span class="hljs-function">(<span class="hljs-params">repoCards</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> data = [];
  repoCards.map(<span class="hljs-function">(<span class="hljs-params">card</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [user, repo] = card.querySelectorAll(<span class="hljs-string">'h3 a'</span>);
    <span class="hljs-keyword">const</span> stars = card
      .querySelector(<span class="hljs-string">'#repo-stars-counter-star'</span>)
      .getAttribute(<span class="hljs-string">'title'</span>);
    <span class="hljs-keyword">const</span> description = card.querySelector(<span class="hljs-string">'.px-3 &gt; p'</span>);
    <span class="hljs-keyword">const</span> topics = card.querySelectorAll(<span class="hljs-string">'a.topic-tag'</span>);
    <span class="hljs-keyword">const</span> toText = <span class="hljs-function">(<span class="hljs-params">element</span>) =&gt;</span> element &amp;&amp; element.innerText.trim();
    <span class="hljs-keyword">const</span> parseNumber = <span class="hljs-function">(<span class="hljs-params">text</span>) =&gt;</span> <span class="hljs-built_in">Number</span>(text.replace(<span class="hljs-regexp">/,/g</span>, <span class="hljs-string">''</span>));
    data.push({
      <span class="hljs-attr">user</span>: toText(user),
      <span class="hljs-attr">repo</span>: toText(repo),
      <span class="hljs-attr">url</span>: repo.href,
      <span class="hljs-attr">stars</span>: parseNumber(stars),
      <span class="hljs-attr">description</span>: toText(description),
    });
  });
  <span class="hljs-keyword">return</span> data;
});
</code></pre>
<p>The code block above selects all the repositories on the page using the <strong>article.border</strong> selector.</p>
<p>Next, it iterates over the repositories and selects the user and repo name, the number of stars, the description, and the topics. It then pushes the data into an array and returns it.</p>
<p>We also defined two helper functions to format the elements. The <strong>toText()</strong> function returns the text content of an element and removes any whitespace. The <strong>parseNumber()</strong>  function removes the commas from the number of stars and converts it to a number.</p>
<p>Finally, we log the array to the console.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(repositoryList)
</code></pre>
<p>The full code should look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { chromium } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'playwright'</span>);
(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> chromium.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">'https://github.com/topics/nodejs'</span>);
  <span class="hljs-keyword">const</span> repositoryList = <span class="hljs-keyword">await</span> page.$$eval(<span class="hljs-string">'article.border'</span>, <span class="hljs-function">(<span class="hljs-params">repoCards</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> data = [];
    repoCards.map(<span class="hljs-function">(<span class="hljs-params">card</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> [user, repo] = card.querySelectorAll(<span class="hljs-string">'h3 a'</span>);
      <span class="hljs-keyword">const</span> stars = card
        .querySelector(<span class="hljs-string">'#repo-stars-counter-star'</span>)
        .getAttribute(<span class="hljs-string">'title'</span>);
      <span class="hljs-keyword">const</span> description = card.querySelector(<span class="hljs-string">'.px-3 &gt; p'</span>);
      <span class="hljs-keyword">const</span> topics = card.querySelectorAll(<span class="hljs-string">'a.topic-tag'</span>);
      <span class="hljs-keyword">const</span> toText = <span class="hljs-function">(<span class="hljs-params">element</span>) =&gt;</span> element &amp;&amp; element.innerText.trim();
      <span class="hljs-keyword">const</span> parseNumber = <span class="hljs-function">(<span class="hljs-params">text</span>) =&gt;</span> <span class="hljs-built_in">Number</span>(text.replace(<span class="hljs-regexp">/,/g</span>, <span class="hljs-string">''</span>));
      data.push({
        <span class="hljs-attr">user</span>: toText(user),
        <span class="hljs-attr">repo</span>: toText(repo),
        <span class="hljs-attr">url</span>: repo.href,
        <span class="hljs-attr">stars</span>: parseNumber(stars),
        <span class="hljs-attr">description</span>: toText(description),
        <span class="hljs-attr">topics</span>: <span class="hljs-built_in">Array</span>.from(topics).map(<span class="hljs-function">(<span class="hljs-params">t</span>) =&gt;</span> toText(t)),
      });
    });
    <span class="hljs-keyword">return</span> data;
  });
  <span class="hljs-built_in">console</span>.log(repositoryList);
  <span class="hljs-keyword">await</span> browser.close();
})();
</code></pre>
<p>Run the script by using the following command:</p>
<pre><code class="lang-bash">node github.js
</code></pre>
<p>Then, you should see this output in your terminal:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698407435054/2184b5c3-9bfc-40ad-9b40-eb74de7e8c27.webp" alt class="image--center mx-auto" /></p>
<h2 id="heading-interacting-with-web-elements"><strong>Interacting With Web Elements</strong></h2>
<h3 id="heading-clicking-buttons-and-links"><strong>Clicking Buttons and Links</strong></h3>
<p>Web scraping often involves interacting with elements like buttons and links to access more data or navigate to other pages. Playwright allows you to simulate clicks on such elements:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> page.click(<span class="hljs-string">"button#submit-button"</span>);
</code></pre>
<h3 id="heading-filling-forms"><strong>Filling Forms</strong></h3>
<p>Sometimes when scraping, you might need to fill in a form, especially when accessing restricted content or interacting with search functionalities. Playwright offers simple yet effective methods to populate form fields and submit data.</p>
<p>To fill in a text input field, you can use the page.fill() method:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> page.fill(<span class="hljs-string">'input#username'</span>, <span class="hljs-string">'your_username'</span>);
<span class="hljs-keyword">await</span> page.fill(<span class="hljs-string">'input#password'</span>, <span class="hljs-string">'your_password'</span>);
<span class="hljs-comment">// Submit the form</span>
<span class="hljs-keyword">await</span> page.click(<span class="hljs-string">'button#submit-button'</span>);
</code></pre>
<h3 id="heading-hovering-and-right-clicking"><strong>Hovering and Right-Clicking</strong></h3>
<p>Playwright enables you to simulate mouse actions, such as hovering over an element or right-clicking on it. These interactions can be useful when scraping websites with interactive elements:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> targetElement = <span class="hljs-keyword">await</span> page.$(<span class="hljs-string">'#target-element'</span>);
<span class="hljs-keyword">await</span> targetElement.hover();
<span class="hljs-keyword">await</span> targetElement.click({ <span class="hljs-attr">button</span>: <span class="hljs-string">'right'</span> });
</code></pre>
<h2 id="heading-taking-screenshots"><strong>Taking Screenshots</strong></h2>
<p>Playwright provides the <strong>page.screenshot()</strong> method to take screenshots of webpages, as we saw in an earlier example. You can use this method to take screenshots of the entire page or a specific element.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Fullpage screenshot</span>
<span class="hljs-keyword">await</span> page.screenshot({ <span class="hljs-attr">path</span>: <span class="hljs-string">'screenshot.png'</span> });
<span class="hljs-comment">// Screenshot a single element</span>
<span class="hljs-keyword">await</span> page.locator(<span class="hljs-string">'h1'</span>).screenshot({ <span class="hljs-attr">path</span>: <span class="hljs-string">'header.png'</span> });
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Web scraping with Playwright provides a powerful and flexible way to extract data from websites. In this post, we've covered the basics of getting started with Playwright and demonstrated how to perform common web-scraping tasks using practical examples.</p>
<p>Remember that when scraping websites, it's important to be respectful of the website's terms of service and robots.txt file. Some websites may have restrictions on web-scraping activities, so make sure to review and adhere to their policies.</p>
<p>Implementing rate limiting and using rotating proxies can help you maintain a low profile while scraping and avoiding potential IP blocks.</p>
<p>For enhanced anonymity and IP rotation, consider trying <a target="_blank" href="https://www.proxyrack.com">ProxyRack</a>, a reliable proxy service that offers a vast pool of residential proxies to mask your real IP address effectively. ProxyRack provides an additional layer of protection and ensures that your web-scraping activities remain discreet and undetectable.</p>
<p>Happy scraping!</p>
<p><em>This post was originally published on</em> <a target="_blank" href="https://www.proxyrack.com/blog/playwright-web-scraping/">Proxyrack's blog</a></p>
]]></content:encoded></item><item><title><![CDATA[Creating Visual Interest: How to Use MouseMove Hover to Reveal Background Images]]></title><description><![CDATA[Canva celebrated 15 billion designs on its platform on February 15, 2023, marking a huge milestone for the company. To celebrate the event, the company created a special background image featuring designs made on the platform. When you move your mous...]]></description><link>https://blog.mitolu.dev/creating-visual-interest-how-to-use-mousemove-hover-to-reveal-background-images</link><guid isPermaLink="true">https://blog.mitolu.dev/creating-visual-interest-how-to-use-mousemove-hover-to-reveal-background-images</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[CSS]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 28 Apr 2023 14:02:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682690498684/4ba22fcd-f8c5-488d-a5f2-cadb68ce12ae.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Canva celebrated <a target="_blank" href="https://www.canva.com/newsroom/news/15-billion-designs/">15 billion designs</a> on its platform on February 15, 2023, marking a huge milestone for the company. To celebrate the event, the company created a special background image featuring designs made on the platform. When you move your mouse around the page, the image gets revealed, creating a cool effect. I found it interesting and decided to recreate it using HTML, CSS, and JavaScript. This article will show you how to recreate the effect step by step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677444574615/b4aae5bc-82fe-4edc-b102-850b720a42b2.gif" alt="Canva homepage celebrating 15 billion designs" class="image--center mx-auto" /></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes you have a basic knowledge of HTML, CSS and JavaScript. You'll also need a code editor and a web browser. I'll be using VS Code and Chrome, but you can use whatever you like.</p>
<p>To follow along, clone or download the starter files <a target="_blank" href="https://github.com/israelmitolu/Reveal-Background-Image-Mousemove">here</a></p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>First, create the HTML markup for the container and background image. The container is where the background image will be revealed on hover.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Thanks for <span class="hljs-tag">&lt;<span class="hljs-name">u</span>&gt;</span>15 billion designs!<span class="hljs-tag">&lt;/<span class="hljs-name">u</span>&gt;</span><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">"text"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search your content or Canva's"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hover"</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>
</code></pre>
<p>The container is where all the content will be. The <code>hover</code> div is empty for now, but we will add the background image using CSS.</p>
<h2 id="heading-styling-the-container">Styling the Container</h2>
<p>Next, copy the following CSS into the <code>styles.css</code> file. This will style the container and the other elements in the app.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">position</span>: relative;
  <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-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">margin</span>: auto;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">25rem</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(
    <span class="hljs-number">119.42deg</span>,
    #cc00c4 <span class="hljs-number">8.94%</span>,
    #<span class="hljs-number">7</span>d2ae8 <span class="hljs-number">54.02%</span>,
    #<span class="hljs-number">00</span>c4cc <span class="hljs-number">95.49%</span>
  );
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">overflow</span>: hidden;
}

<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3rem</span>;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">10</span>;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">9px</span> <span class="hljs-number">4px</span> <span class="hljs-number">9px</span> <span class="hljs-number">40px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#2b3b4a4d</span>;
  <span class="hljs-attribute">outline</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#0d1216</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span> <span class="hljs-number">12px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">2.5rem</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">40rem</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">10</span>;
}
</code></pre>
<p>In the CSS above, we set the container to <code>position: relative</code> so that we can position the background image inside it. We also set the <code>overflow</code> property to <code>hidden</code> so that the background image doesn't overflow the container.</p>
<p>We also set the <code>z-index</code> property of the <code>h1</code> and <code>input</code> elements to <code>10</code> so that they appear above or on top of the background image.</p>
<p>Here is what we have so far:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682688423424/16f7a390-9a78-4136-bc62-32c9643d8d50.png" alt="Static website Design that reads Thanks for 15 billion designs!" /></p>
<h2 id="heading-adding-the-background-image">Adding the Background Image</h2>
<p>Next, we'll target the <code>.hover</code> class and add the background image. We'll also set the <code>position</code> property to <code>absolute</code> so that we can position the image inside the container.</p>
<p>Copy the following CSS into the <code>styles.css</code> file:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hover</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">10rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">10rem</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">url</span>(img/giphy.gif) <span class="hljs-number">50%</span> <span class="hljs-number">50%</span> fixed;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">30px</span> <span class="hljs-number">30px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.15</span>);
  <span class="hljs-attribute">filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">1px</span>);
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">5</span>;
}
</code></pre>
<p>The <code>opacity</code> property is set to <code>0</code> so that the image is hidden by default. We'll use JavaScript to change the opacity to <code>1</code> when the user hovers over the container.</p>
<p>Additionally, for the <code>background</code> property, we used the shorthand syntax to set the background image, position, size and repeat. We also set the <code>fixed</code> value for the <code>background-attachment</code> property so that the image stays in place when the mouse moves around.</p>
<p>If you prefer, you can use the longhand syntax to set the background image, position, size and repeat:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">background-image</span>: <span class="hljs-selector-tag">url</span>(<span class="hljs-selector-tag">img</span>/<span class="hljs-selector-tag">giphy</span><span class="hljs-selector-class">.gif</span>);
<span class="hljs-selector-tag">background-position</span>: 50% 50%;
<span class="hljs-selector-tag">background-attachment</span>: <span class="hljs-selector-tag">fixed</span>;
</code></pre>
<h2 id="heading-revealing-the-background-image">Revealing the Background Image</h2>
<p>To reveal the background image, we'll use JavaScript to track the movement of the user's mouse cursor and change the opacity of the background image.</p>
<p>Create a new file called <code>app.js</code> and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> hover = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".hover"</span>);
<span class="hljs-keyword">const</span> hoverWHalf = hover.offsetWidth / <span class="hljs-number">1.5</span>;
<span class="hljs-keyword">const</span> container = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".container"</span>);

container.addEventListener(<span class="hljs-string">"mousemove"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  hover.style.left = e.pageX - hoverWHalf + <span class="hljs-string">"px"</span>;
  hover.style.top = e.pageY - hoverWHalf + <span class="hljs-string">"px"</span>;
  hover.style.opacity = <span class="hljs-string">"1"</span>;
});
</code></pre>
<p>In the code above, we first select the <code>.hover</code> and <code>.container</code> elements using the <code>querySelector</code> method.</p>
<p>Then, we add an event listener to the container element. The event listener listens for the <code>mousemove</code> event, so that whenever the user moves their mouse over the container, the code will calculate the position of the <code>.hover</code> element and update its <code>position</code> and <code>opacity</code> dynamically.</p>
<p>Specifically, it calculates the horizontal and vertical midpoint of the <code>.hover</code> element using the <code>offsetWidth</code> property and then subtracts half of this value from the <code>pageX</code> and <code>pageY</code> properties of the mousemove event to determine the position of the .hover element relative to the mouse cursor.</p>
<p>Don't forget to link the <code>app.js</code> file inside the <code>index.html</code> file ;)</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">"app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>With this, we have successfully created the mousemove hover effect. Here is the final result:</p>
<p>Check out the <a target="_blank" href="https://reveal-background-image.netlify.app/">live demo</a> and source code on <a target="_blank" href="https://github.com/israelmitolu/Reveal-Background-Image-Mousemove">Github</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682689958479/be534455-5a5e-484a-9983-25c5034e2e71.gif" alt="Our Replica of the background image reveal effect" class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>And there you have it! You've made it to the end of this article on creating visual interest using the MouseMove hover effect with HTML, CSS, and JavaScript. By now, you should have a good understanding of how to use this technique to add some excitement and creativity to your website.</p>
<p>Remember, there are countless ways to customize this effect and make it your own. Don't be afraid to experiment with different images, colours, and animations to create a look that perfectly matches your brand or personal style.</p>
<p>As you continue to develop your web design skills, be sure to keep an eye out for new and exciting trends in the industry. Who knows, you might just discover the next big thing :)</p>
<hr />
<p>Thank you for reading! I hope you found this article helpful. If you did, kindly like and share it with your friends and colleagues :)</p>
<p>I would love to connect with you on <a target="_blank" href="https://twitter.com/israelmitolu"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.linkedin.com/in/israeloyetunji/"><strong>LinkedIn</strong></a> | <a target="_blank" href="https://github.com/israelmitolu"><strong>Github</strong></a> | <a target="_blank" href="https://israelmitolu.netlify.app/"><strong>Portfolio</strong></a>.</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><p><a target="_blank" href="https://israelmitolu.hashnode.dev/using-custom-cursors-with-javascript-for-a-better-user-experience">Using Custom Cursors with JavaScript for a Better UX</a></p>
</li>
<li><p><a target="_blank" href="https://hibbard.eu/how-to-center-an-html-element-using-javascript/">How to Center an HTML Element Using JavaScript</a></p>
</li>
<li><p>MDN Web Docs - <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseover_event">mousemove</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Top VS Code Extensions Every Frontend Developer Needs in Their Toolkit]]></title><description><![CDATA[Visual Studio Code, commonly known as VS Code, is a free and open-source code editor developed by Microsoft for Windows, Linux, and macOS. It has become one of the most popular code editors in the developer community due to its user-friendly interfac...]]></description><link>https://blog.mitolu.dev/the-top-vs-code-extensions-every-frontend-developer-needs-in-their-toolkit</link><guid isPermaLink="true">https://blog.mitolu.dev/the-top-vs-code-extensions-every-frontend-developer-needs-in-their-toolkit</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[vscode extensions]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Wed, 26 Apr 2023 22:12:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682500511805/97d82d93-d251-41d6-969c-8ed880b0ba10.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a>, commonly known as VS Code, is a free and open-source code editor developed by Microsoft for Windows, Linux, and macOS. It has become one of the most popular code editors in the developer community due to its user-friendly interface and numerous features that cater to different programming languages and frameworks.</p>
<p>As a frontend developer, having the right extensions installed can greatly improve your workflow, making it more efficient and effective. In this article, I will introduce you to my top VS Code extensions that every frontend developer should consider adding to their toolkit.</p>
<h2 id="heading-1-live-serverhttpsmarketplacevisualstudiocomitemsitemnameritwickdeyliveserver">1. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer">Live Server</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548006876/010aee59-e7b9-41d8-89a8-8ebd03a882b1.png" alt="Live Server - Visual Code Extension" class="image--center mx-auto" /></p>
<p>Live Server is a valuable tool for frontend developers that allows you to launch a local development server with live reload capability. This extension creates a local development server and opens your project in a browser. It automatically refreshes the page every time you save changes to your code, providing a seamless and efficient development experience.</p>
<h2 id="heading-2-visual-studio-intellicodehttpsmarketplacevisualstudiocomitemsitemnamevisualstudioexptteamvscodeintellicode">2. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode">Visual Studio IntelliCode</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548629423/69254ab1-f494-4187-8eda-8aba76907a91.png" alt="Visual Studio Intellicode" class="image--center mx-auto" /></p>
<p>Visual Studio IntelliCode is an AI-powered extension that provides intelligent code completion suggestions based on your code context. This extension can save you time and effort by eliminating the need to manually type out every line of code. With Visual Studio IntelliCode, you can write code more efficiently and with fewer errors.</p>
<h2 id="heading-3-prettier-code-formatterhttpsmarketplacevisualstudiocomitemsitemnameesbenpprettier-vscode">3. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier - Code Formatter</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548653004/f3655e68-1a5c-474b-bc12-1ee27d3f5607.png" alt="Prettier - Code Formatter" class="image--center mx-auto" /></p>
<p>Prettier is a code formatter that helps you format your code according to a set of rules. It is very useful when you are working on a project that requires you to follow a specific coding style. It also helps you avoid common mistakes such as missing semicolons and trailing commas.</p>
<h2 id="heading-4-path-intellisensehttpsmarketplacevisualstudiocomitemsitemnamechristian-kohlerpath-intellisense">4. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense">Path IntelliSense</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548668435/5492c5b6-8424-44b0-8c75-5496a22442d7.png" alt="Path Intellisense" class="image--center mx-auto" /></p>
<p>Path Intellisense is an extension that provides auto-completion for file paths in your project. This can be especially useful when working with large codebases that have many nested directories. With Path Intellisense, you can quickly and easily navigate to the file you need without having to remember its exact location.</p>
<h2 id="heading-5-gitlens-git-superchargedhttpsmarketplacevisualstudiocomitemsitemnameeamodiogitlens">5. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens — Git supercharged</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548751617/ab773c34-c856-4461-b5c4-4d325bf3140f.png" alt="GitLens " class="image--center mx-auto" /></p>
<p>GitLens is a powerful extension that provides a visual representation of your Git repository. It allows you to see the history of your commits, branches, and tags. This can be especially useful when working on a project with multiple developers. With GitLens, you can easily track changes, view blame annotations, code authorship, commit history, and much more.</p>
<h2 id="heading-5-auto-rename-taghttpsmarketplacevisualstudiocomitemsitemnameformulahendryauto-rename-tag">5. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag">Auto Rename Tag</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548710180/d0c7d25e-25d0-4f11-8fec-6b4cae81f01f.png" alt="Auto Rename Tag" class="image--center mx-auto" /></p>
<p>When you're working with HTML or XML, it is essential to ensure that your tags are properly named and closed. Auto Rename Tag is an extension that automatically renames your opening or closing HTML tags when you rename one of them. This extension can save you a significant amount of time and prevent errors that may arise from mismatched tag names.</p>
<h2 id="heading-6-css-peekhttpsmarketplacevisualstudiocomitemsitemnamepranaygpvscode-css-peek">6. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=pranaygp.vscode-css-peek">CSS Peek</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548732077/a1f7d3be-763a-4abb-9012-2d28b2480ed6.png" alt="CSS Peek" class="image--center mx-auto" /></p>
<p>CSS Peek is an extension that allows you to quickly navigate from an HTML file to the corresponding CSS definition. This can be extremely helpful when working with large codebases that have many CSS files. With CSS Peek, you can easily locate and modify the CSS rules that affect specific HTML elements.</p>
<h2 id="heading-7-eslinthttpsmarketplacevisualstudiocomitemsitemnamedbaeumervscode-eslint">7. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">ESLint</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548764678/a646da2e-42f4-4165-ac7b-fc9b160cab4e.png" alt="ESLint" class="image--center mx-auto" /></p>
<p>ESLint is a powerful linter that can analyze your code and identify potential errors or coding style issues. This extension integrates with VS Code to provide real-time feedback on your JavaScript and TypeScript code as you write it. With ES Lint, you can ensure that your code is free of errors and follows best coding practices.</p>
<h2 id="heading-8-live-sharehttpsmarketplacevisualstudiocomitemsitemnamems-vslivesharevsliveshare">8. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare">Live Share</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548925880/38d8ce17-0048-466a-a98e-3a5e21c1563a.png" alt="Live Share" class="image--center mx-auto" /></p>
<p>Live Share is an extension that allows you to collaborate with other developers on the same project. This extension allows you to share your code with other developers and work on it together in real-time. With Live Share, you can easily collaborate with other developers on the same project.</p>
<h2 id="heading-9-intellisense-for-css-class-names-in-htmlhttpsmarketplacevisualstudiocomitemsitemnamezigndhtml-css-class-completion">9. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Zignd.html-css-class-completion">Intellisense for CSS class names in HTML</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682548971820/3daf035c-0eef-45b1-b692-51626180183b.png" alt="Intellisense for CSS Class Names in HTML" class="image--center mx-auto" /></p>
<p>IntelliSense for CSS class names is an extension that provides autocompletion for CSS class names. This extension can save you time and effort by eliminating the need to manually type out every CSS class name. With IntelliSense for CSS class names, you can write CSS code more efficiently and with fewer errors.</p>
<h2 id="heading-10-tailwind-css-intellisensehttpsmarketplacevisualstudiocomitemsitemnamebradlcvscode-tailwindcss">10. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss">Tailwind CSS IntelliSense</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682549038579/433633f4-0bfa-4d9b-a40f-a6488ff7a329.png" alt="Tailwind CSS Intellisense" class="image--center mx-auto" /></p>
<p>Tailwind CSS is a popular utility-first CSS framework that can help you write clean and responsive code. With the Tailwind CSS IntelliSense extension, you can enjoy autocompletion and syntax highlighting for Tailwind CSS classes in VS Code. This extension can save you time and effort by allowing you to quickly and easily apply Tailwind CSS classes to your HTML and CSS code.</p>
<h2 id="heading-11-vs-code-es7-reactreduxreact-nativejs-snippetshttpsmarketplacevisualstudiocomitemsitemnamedsznajderes7-react-js-snippets">11. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets">VS Code ES7+ React/Redux/React-Native/JS snippets</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682549119960/86daf294-0f62-429b-8b66-93e9f16c5b0d.png" alt="ES7+ React/Redux/React-Native Snippets" class="image--center mx-auto" /></p>
<p>VS Code ES7+ React/Redux/React-Native/JS snippets is an extension that provides auto-completion for React and React Native code. This extension can save you time and effort by allowing you to quickly and easily write React and React Native code. With VS Code ES7+ React/Redux/React-Native/JS snippets, you can write React and React Native code more efficiently and with fewer errors.</p>
<h2 id="heading-12-better-commentshttpsmarketplacevisualstudiocomitemsitemnameaaron-bondbetter-comments">12. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments">Better Comments</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682549138676/e3f8f3da-b65b-4a2b-8005-4ed48bfc2020.png" alt="Better Comments" class="image--center mx-auto" /></p>
<p>Better Comments is an extension that allows you to add color to your comments. This extension can help you organize your code and make it easier to read. With Better Comments, you can easily distinguish between different types of comments and identify important information at a glance.</p>
<h2 id="heading-13-github-copilothttpsmarketplacevisualstudiocomitemsitemnamegithubcopilot">13. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot">Github Copilot</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682549173890/dd6e96b8-9bc3-4a67-9387-3f5693c6b469.png" alt="Github Copilot" class="image--center mx-auto" /></p>
<p>GitHub Copilot is a revolutionary AI-powered code completion tool that uses machine learning to help developers write code faster and more accurately. It suggests code snippets and even entire functions based on the context of the code you are writing. With Copilot, you can save time and focus on the logic of your code.</p>
<h2 id="heading-14-rest-clienthttpsmarketplacevisualstudiocomitemsitemnamehumaorest-client">14. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client">REST Client</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682549184961/ed88f34c-7f0c-4cbc-9a9e-0d1b36145c6c.png" alt="REST Client" class="image--center mx-auto" /></p>
<p>REST Client allows you to send HTTP requests and view the responses directly in VS Code. It supports many HTTP methods, including GET, POST, PUT, DELETE, and more. This extension is perfect when you who want to test APIs and web services quickly and easily.</p>
<h2 id="heading-15-live-sass-compilerhttpsmarketplacevisualstudiocomitemsitemnameglenn2223live-sass">15. <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=glenn2223.live-sass">Live Sass Compiler</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1682549189899/335adcf2-df1b-431a-a593-f75c7fae850b.png" alt="Live Sass Compiler" class="image--center mx-auto" /></p>
<p>If like me, you love <a target="_blank" href="https://www.freecodecamp.org/news/the-beginners-guide-to-sass/">using Sass</a>, then you will love this extension. It allows you to compile your Sass and Scss code to CSS in real-time. The extension also offers other useful features, such as live browser reloading and customizations for the output CSS.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Having the right VS Code extensions can greatly improve your workflow as a frontend developer, increasing productivity, reducing errors, and improving readability.</p>
<p>While there are many other useful VS Code extensions beyond the 15 mentioned in this article, these are the ones that I use on a regular basis. I hope you find them useful as well!</p>
<p>I would love to hear your thoughts on this article. Which VS Code extensions do you use to improve your frontend development workflow? Do you have any favorite extensions not mentioned in this article? Let me know in the comments below!</p>
<hr />
<p>Thank you for reading! I hope you found this article helpful. If you did, kindly like and share it with your friends and colleagues :)</p>
<p>I would love to connect with you on <a target="_blank" href="https://twitter.com/israelmitolu">Twitter</a> | <a target="_blank" href="https://www.linkedin.com/in/israeloyetunji/">LinkedIn</a> | <a target="_blank" href="https://github.com/israelmitolu">Github</a> | <a target="_blank" href="https://israelmitolu.netlify.app/">Portfolio</a>.</p>
]]></content:encoded></item><item><title><![CDATA[A Beginner's Guide to Portals in React: Everything You Need to Know]]></title><description><![CDATA[React is a popular JavaScript library for building user interfaces. One of the powerful features of React is the use of portals, which allow developers to render content outside of the normal React tree structure. In this article, you'll learn all ab...]]></description><link>https://blog.mitolu.dev/a-beginners-guide-to-portals-in-react-everything-you-need-to-know</link><guid isPermaLink="true">https://blog.mitolu.dev/a-beginners-guide-to-portals-in-react-everything-you-need-to-know</guid><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 07 Apr 2023 19:30:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678641523190/95876c63-969d-40b8-9acb-3629bf31bf42.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>React is a popular JavaScript library for building user interfaces. One of the powerful features of React is the use of portals, which allow developers to render content outside of the normal React tree structure. In this article, you'll learn all about what portals are, how to use them, and some common use cases for portals in React applications.</p>
<h2 id="heading-what-are-portals">What are Portals?</h2>
<p>React portals provide a way to render content outside of the normal DOM hierarchy of a component. This means that you can render a child component at a different place in the DOM tree than its parent component.</p>
<p>In technical terms, portals in React work by creating a new DOM node and rendering a component into it. This new node is appended to the body element of the HTML document, which is outside of the normal React component tree. The rendered content can then be positioned and styled independently of the parent component.</p>
<h2 id="heading-advantages-of-using-portals">Advantages of Using Portals</h2>
<p>There are several advantages to using portals in React:</p>
<ul>
<li><p><strong>Flexibility</strong>: Portals allow you to render a component outside of its parent container, which can be useful in situations where you need to display content in a different part of the page.</p>
</li>
<li><p><strong>Improved performance</strong>: By rendering a component outside of its parent container, you can reduce the amount of DOM manipulation needed to update the component, which can improve performance.</p>
</li>
</ul>
<h2 id="heading-common-use-cases-for-portals">Common Use Cases for Portals</h2>
<p>Portals can be used for a variety of UI elements. Here are some common use cases of portals in React:</p>
<ul>
<li><p>Modals</p>
</li>
<li><p>Dialog boxes</p>
</li>
<li><p>Tooltips</p>
</li>
<li><p>Hover cards</p>
</li>
<li><p>Popovers</p>
</li>
<li><p>Dropdown Menus</p>
</li>
<li><p>Loaders</p>
</li>
</ul>
<h2 id="heading-how-to-create-a-portal-in-react">How to Create a Portal in React</h2>
<p>To create a portal in React, we make use of the <a target="_blank" href="https://react.dev/reference/react-dom/createPortal"><code>createPortal()</code></a> API. This method takes two arguments:</p>
<ol>
<li><p><code>children</code>: Any valid React element that can be rendered.</p>
</li>
<li><p><code>domNode</code>: DOM container where the <code>children</code> should be rendered</p>
</li>
</ol>
<p>Here is the syntax:</p>
<pre><code class="lang-javascript">createPortal(children, domNode);
</code></pre>
<p>Example:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { createPortal } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom"</span>;

<span class="hljs-keyword">const</span> Modal = <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> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Modal Content<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">const</span> Portal = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> createPortal(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Modal</span> /&gt;</span></span>, <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"modal-root"</span>));
};
</code></pre>
<p>In the example above, we define a Modal component that we want to render using a portal. We then define a Portal component that uses the <code>createPortal()</code> method to render the Modal component in a container with the ID of <code>modal-root</code>.</p>
<h2 id="heading-how-to-use-portals">How to Use Portals</h2>
<p>To see how portals work, let's create a simple React app that uses portals to render a modal component. If you're working on an existing project, you can skip the first step. The complete code for this example is available on <a target="_blank" href="https://github.com/israelmitolu/React-Portals-Demo">GitHub</a>.</p>
<h3 id="heading-step-1-initialise-your-react-app">Step 1: Initialise your React app</h3>
<p>First, create a <a target="_blank" href="https://israelmitolu.hashnode.dev/why-you-should-ditch-create-react-app-for-vite">new React app</a> using Vite:</p>
<pre><code class="lang-bash">npm create vite@latest my-react-app --template react
</code></pre>
<p>Navigate to the project directory and install the dependencies:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> my-react-app
npm install
npm run dev
</code></pre>
<h3 id="heading-step-2-create-a-portal-container">Step 2: Create a portal container</h3>
<p>Next, create a container element in the <code>index.html</code> file that will host the portal. This element should be located outside the root node of the React app, which is typically a div with an ID of <code>root</code>.</p>
<p>For this example, we'll create a div with an ID of <code>portal</code> and add it to the body of the HTML file:</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">id</span>=<span class="hljs-string">"root"</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">id</span>=<span class="hljs-string">"portal"</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>
<h3 id="heading-step-3-create-the-overlay-component">Step 3: Create the Overlay component</h3>
<p>We will use the Overlay component as a backdrop effect when the modal opens, and also create the portal inside it.</p>
<p>Create a new file <code>Overlay.jsx</code>, in the <code>src</code> directory and paste the following code:</p>
<pre><code class="lang-jsx"><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> { createPortal } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-keyword">const</span> Overlay = <span class="hljs-function">(<span class="hljs-params">{ closeModal, children }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> createPortal(
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"overlay"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{closeModal}</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"portal"</span>)
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Overlay;
</code></pre>
<p>The Overlay component returns a JSX fragment that uses the <code>createPortal()</code> method from the <code>react-dom</code> package to render a div with children elements inside a portal.</p>
<p>We then set the <code>onClick</code> event for the div to call the <code>closeModal</code> function that is passed as a prop.</p>
<h3 id="heading-step-4-create-the-modal-component">Step 4: Create the Modal component</h3>
<p>Next, create a new file <code>Modal.jsx</code> in the <code>src</code> directory, and add the following code.</p>
<pre><code class="lang-jsx"><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> Overlay <span class="hljs-keyword">from</span> <span class="hljs-string">"./Overlay"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-keyword">const</span> Modal = <span class="hljs-function">(<span class="hljs-params">{ closeModal }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Overlay</span> <span class="hljs-attr">closeModal</span>=<span class="hljs-string">{closeModal}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> e.stopPropagation()}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Modal<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Odit, libero.
          Exercitationem quod quam perspiciatis quasi voluptate sint facilis
          quia praesentium pariatur veniam quidem nam, ullam porro eveniet
          molestiae minima eligendi.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{closeModal}</span>&gt;</span>Close<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">Overlay</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Modal;
</code></pre>
<p>In the code above, the <code>onClick</code> event for the div is set to stop event propagation so that clicking inside the modal doesn't close it.</p>
<p>Additionally, the <code>onClick</code> event for the button element will call the <code>closeModal</code> function that was passed as a prop.</p>
<h3 id="heading-step-5-edit-the-app-component">Step 5: Edit the App component</h3>
<p>Copy and paste the following code into the <code>App.jsx</code> file.</p>
<pre><code class="lang-jsx"><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> Modal <span class="hljs-keyword">from</span> <span class="hljs-string">"./Modal"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// State to manage whether the modal is open or closed</span>
  <span class="hljs-keyword">const</span> [modalOpen, setModalOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-comment">// Function to toggle the state between open and closed</span>
  <span class="hljs-keyword">const</span> handleModal = <span class="hljs-function">() =&gt;</span> {
    setModalOpen(!modalOpen);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>React Modal Demo<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        This is a demo of how to use portals to create a modal component in
        React.
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleModal}</span>&gt;</span>Open Modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

      {modalOpen &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">Modal</span> <span class="hljs-attr">closeModal</span>=<span class="hljs-string">{()</span> =&gt;</span> setModalOpen(false)} /&gt;}
    <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> App;
</code></pre>
<p>Here we define a state to manage whether the modal is open or closed, and a function to toggle the state. When the "Open Modal" button is clicked, the <code>handleModal</code> function is called and the <code>modalOpen</code> state is updated accordingly.</p>
<p>If the state is <code>true</code>, the Modal component is rendered, which includes a "Close" button that sets the modalOpen state to false.</p>
<h3 id="heading-step-6-style-the-app">Step 6: Style the App</h3>
<p>Finally, let's style the app. Add the following code inside the <code>App.css</code> file:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.App</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100vw</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f5f5f5</span>;
}

<span class="hljs-selector-class">.overlay</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>);
  <span class="hljs-attribute">backdrop-filter</span>: <span class="hljs-built_in">blur</span>(<span class="hljs-number">3px</span>);
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.modal</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">450px</span>;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">250px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">20%</span>;
  <span class="hljs-attribute">background-color</span>: white;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">15px</span>;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.7rem</span> <span class="hljs-number">1.4rem</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>;
}
</code></pre>
<p>Here's what the final app looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680886920876/c1df90ef-c4f9-4214-96f8-1143ece8dbf4.gif" alt="React modal demo" /></p>
<p>Now, let's take a closer look at how this works. Open the <strong>Developer Tools</strong> in your browser and inspect the page. You'll notice that while the modal is closed, the <code>portal</code> div is empty.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680886941153/997b316e-a0f8-4609-8576-321b7158401d.png" alt="DevTools with modal closed" /></p>
<p>When you click the button to open the modal, the modal is rendered inside the <code>portal</code> div, just as we specified.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680886967354/6bb1804c-e81e-4bf6-b5d3-be35949b7a24.png" alt="DevTools with the modal open" /></p>
<p>The new DOM nodes are now mounted inside the <code>portal</code> node instead of the <code>App</code> node specified in the source code. This demonstrates the functionality of portals in action.</p>
<h2 id="heading-event-bubbling-and-portals">Event Bubbling and Portals</h2>
<p>Event bubbling refers to the way events propagate from child components to parent components in the component tree. In the case of React portals, this means that even if we render a portal outside of the parent DOM element, it still behaves like a standard React component, with access to props and state, as it remains within the DOM tree hierarchy.</p>
<p>Since portals only affect the HTML DOM structure and not the React component tree, features such as Context API and Event Bubbling continue to work as they did before.</p>
<p>To prevent event bubbling, as demonstrated in the example, you can use the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation"><code>stopPropagation()</code></a> method to ensure that only the event listener on the portal's container is triggered.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Portals in React are a powerful feature that allows developers to render content outside of the normal component hierarchy. This can be useful for creating modals, popovers, and other UI elements that need to be positioned relative to a specific element or displayed on top of the main application interface.</p>
<p>When using portals, it's important to keep in mind some best practices.</p>
<ul>
<li><p>First, always make sure to clean up the portal node when the component unmounts. This can be done using the <code>useEffect</code> hook and the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild"><code>removeChild</code></a> method.</p>
</li>
<li><p>Second, be careful not to use portals excessively, as they can add unnecessary complexity to your code.</p>
</li>
<li><p>Finally, always test your portals thoroughly for accessibility issues, as they can sometimes cause unexpected behaviour in certain browsers or screen readers. Ensure to follow the <a target="_blank" href="https://www.w3.org/WAI/ARIA/apg/#dialog_modal">WAI-ARIA Modal Authoring Practices</a> when working with modals.</p>
</li>
</ul>
<p>Thank you for reading! I hope you found this article helpful. If you did, kindly like and share it with your friends and colleagues :)</p>
<p>I would love to connect with you on <a target="_blank" href="https://twitter.com/israelmitolu">Twitter</a> | <a target="_blank" href="https://www.linkedin.com/in/israeloyetunji/">LinkedIn</a> | <a target="_blank" href="https://github.com/israelmitolu">Github</a> | <a target="_blank" href="https://israelmitolu.netlify.app/">Portfolio</a>.</p>
<p>Happy coding!</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><p><a target="_blank" href="https://react.dev/reference/react-dom/createPortal">React Official Docs</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=LyLa7dU5tp8">Web Dev Simplified: Learn React Portal in 12 Minutes By Building a Modal</a></p>
</li>
<li><p><a target="_blank" href="https://blog.logrocket.com/event-bubbling-capturing-react/">Event Bubbling and Capturing in React</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Augmented Analytics and How It Can Help]]></title><description><![CDATA[In today’s fast-paced business environment, data is more important than ever. With the rise of big data, organizations are struggling to keep up with the sheer volume of information at their disposal. Traditional analytics methods are no longer suffi...]]></description><link>https://blog.mitolu.dev/augmented-analytics-and-how-it-can-help</link><guid isPermaLink="true">https://blog.mitolu.dev/augmented-analytics-and-how-it-can-help</guid><category><![CDATA[AI]]></category><category><![CDATA[Devops]]></category><category><![CDATA[analytics]]></category><category><![CDATA[big data]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Tue, 14 Mar 2023 05:56:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678773546985/908d5253-838c-4338-9544-7d9f376c3052.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today’s fast-paced business environment, data is more important than ever. With the rise of big data, organizations are struggling to keep up with the sheer volume of information at their disposal. Traditional analytics methods are no longer sufficient to uncover valuable insights from all this data, which is why augmented analytics is fast becoming a game changer in the industry.</p>
<p>In this article, we’ll explore what augmented analytics is and how it can help organizations make better decisions, improve their operations, and stay ahead of the curve in their industry. From identifying patterns and trends in data to providing accurate predictions, augmented analytics is a powerful tool that every organization should consider.</p>
<h2 id="heading-what-is-augmented-analytics"><strong>What Is Augmented Analytics?</strong></h2>
<p>Augmented analytics is a technology that uses artificial intelligence (AI) and machine learning (ML) to analyze large amounts of data and produce valuable insights. It automates the process of data preparation, analysis, and visualization, making it easier for organizations to gain a better understanding of their data and identify areas for improvement.</p>
<h2 id="heading-benefits-of-augmented-analytics"><strong>Benefits of Augmented Analytics</strong></h2>
<p>There are several benefits of using augmented analytics, including the following: </p>
<p><img src="https://webplutora.wpenginepowered.com/wp-content/uploads/2023/02/Asset-25@3x.png" alt /></p>
<h3 id="heading-more-effective-decision-making"><strong>More effective decision-making</strong></h3>
<p>Augmented analytics allows organizations to analyze large and complex data sets more quickly and efficiently than traditional methods. With the ability to automatically identify patterns and trends in data, organizations can identify valuable insights that would otherwise be difficult or impossible to uncover manually. This can lead to more accurate predictions, improved decision-making, and a greater understanding of the underlying drivers of business performance. </p>
<h3 id="heading-faster-data-preparation"><strong>Faster data preparation</strong></h3>
<p>You can make data preparation for building, testing, and training machine learning models more efficient with augmented data preparation techniques. This approach allows users to select and merge data sets; perform cleaning, formatting, and enrichment; and even discover new data sets that can help improve the performance of their models. The process is guided by suggestions tailored to the specific needs of the project. </p>
<h3 id="heading-data-democratization"><strong>Data democratization</strong></h3>
<p>Augmented analytics can help organizations democratize data analysis by making it more accessible to a wider range of users. By providing an intuitive and user-friendly interface, these tools can enable nontechnical users to access and analyze data easily. This can help organizations foster a data-driven culture and empower employees at all levels to make more informed decisions. </p>
<h3 id="heading-improved-predictions"><strong>Improved predictions</strong></h3>
<p>Augmented analytics analyzes data using AI and ML algorithms, which can precisely discover patterns and trends in the data, allowing for better data prediction. It’s used for a variety of purposes, including predictive maintenance, fraud detection, consumer segmentation, and others. </p>
<h2 id="heading-how-augmented-analytics-works-and-how-it-can-help"><strong>How Augmented Analytics Works and How It Can Help</strong></h2>
<p>To understand how augmented analytics works, it’s important to know its three key components: </p>
<h3 id="heading-1-machine-learning"><strong>1. Machine learning</strong></h3>
<p>ML is a subset of AI that enables machines to learn from data and provide predictive insights without human input. These models are highly dependent on large amounts of data, and as they process more information, they become increasingly efficient and precise in their interpretations. </p>
<h3 id="heading-2-natural-language-technologies"><strong>2. Natural language technologies</strong></h3>
<p>Natural language technologies enable humans and computers to communicate seamlessly through the use of natural language processing (NLP) and natural language generation (NLG). NLP interprets human language for computers, while NLG translates computer code into human language, allowing business professionals to interact with machines through familiar domain and industry terms in a conversational manner. </p>
<h3 id="heading-3-automation"><strong>3. Automation</strong></h3>
<p>By automating repetitive manual tasks throughout the data analytics process, ML-driven tools significantly reduce the time needed to build, train, and deploy ML models. This allows organizations to reduce reliance on their IT departments and free up analysts to focus on more important tasks. </p>
<p>Essentially, augmented analytics utilizes machine learning to make data analytics and related technologies more accessible to everyone, even those without technical expertise. This allows for the discovery of valuable insights from raw and unstructured data, resulting in faster data preparation and analytics processes. </p>
<p>When it comes to DevOps metrics, augmented analytics can be particularly useful in identifying patterns and trends in the performance of systems, applications, and user experience. This can help organizations identify areas of improvement, optimize their processes, and ultimately deliver better software to their customers. </p>
<p>Some of the features available with augmented analytics for DevOps metrics include the following: </p>
<ul>
<li><p><strong>Automated data collection and analysis.</strong> This can save time and reduce the need for manual data entry.</p>
</li>
<li><p><strong>Self-service.</strong> Users can access the analytics tools and data without needing specialized training or assistance from IT or data scientists.</p>
</li>
<li><p><strong>Real-time monitoring.</strong> This provides up-to-date information on the performance of software and infrastructure.</p>
</li>
<li><p><strong>Dashboards and visualizations.</strong> These can help users easily understand and interpret the data.</p>
</li>
<li><p><a target="_blank" href="https://www.plutora.com/blog/natural-language-queries-explained"><strong>Natural language querying</strong></a><strong>.</strong> This can make it easier for users to interact with the data and ask questions in a more human-like way.</p>
</li>
</ul>
<h2 id="heading-augmented-analytics-examples-and-use-cases"><strong>Augmented Analytics Examples and Use Cases</strong></h2>
<p>Augmented analytics can be applied in a wide range of industries and help organizations gain a better understanding of their data and identify areas for improvement.</p>
<p>Here are a few examples of industries that have embraced augmented analytics:</p>
<ul>
<li><p><strong>Healthcare.</strong> Patient data analysis can help identify vital sign patterns that can lead to better treatment decisions.</p>
</li>
<li><p><strong>Finance.</strong> Financial data analysis can help identify fraud or other financial risks.</p>
</li>
<li><p><strong>Supply chain.</strong> Augmented analytics can optimize logistics and inventory and transportation management.</p>
</li>
<li><p><strong>Retail.</strong> Sales data analysis of customer behavior and preferences can help improve sales and customer satisfaction.</p>
</li>
<li><p><strong>Manufacturing.</strong> Augmented analytics can help optimize production processes and identify areas for efficiency improvements.</p>
</li>
<li><p><strong>Human resources.</strong> Employee data analysis can help identify trends and patterns that can lead to better recruitment, retention, and performance management.</p>
</li>
</ul>
<h2 id="heading-challenges-of-augmented-analytics"><strong>Challenges of Augmented Analytics</strong></h2>
<p>The implementation of augmented analytics comes with its own set of challenges that organizations need to be prepared to address.</p>
<h3 id="heading-data-literacy-and-analytics-proficiency"><strong>Data Literacy and Analytics Proficiency</strong></h3>
<p>Organizations face the challenge of ensuring that their employees have the necessary data literacy and analytics proficiency to effectively use augmented analytics. This includes making sure that employees understand the basics of data analysis and that they can use the tools and techniques required to effectively analyze data.</p>
<h3 id="heading-ethical-use-of-ai"><strong>Ethical Use of AI</strong></h3>
<p>To maintain ethical and legal standards, organizations must implement <a target="_blank" href="https://www.plutora.com/blog/ai-analytics-defined-and-explained">AI analytics</a> while adhering to all relevant regulations. This entails protecting individuals’ privacy and ensuring unbiased decision-making when utilizing the data. </p>
<h3 id="heading-augmented-analytics-limitations"><strong>Augmented Analytics Limitations</strong></h3>
<p>While augmented analytics provides many benefits, it also has its limitations. Organizations need to be aware of these limitations and understand that insights provided by augmented analytics are not always perfect. They may require additional validation and verification. </p>
<h3 id="heading-misconceptions-of-ai-and-ml"><strong>Misconceptions of AI and ML</strong></h3>
<p>Organizations may have misconceptions about what AI and ML can do or the level of accuracy they can achieve. They must understand that AI and ML are not a panacea for all problems. They require a significant investment in data and personnel to be effective. </p>
<h3 id="heading-data-governance-and-management"><strong>Data governance and management</strong></h3>
<p>Ensuring that the data used in the analysis is accurate and reliable is crucial for achieving meaningful insights. This requires proper data governance and management practices, including regular data backups, encryption, and access controls. </p>
<h3 id="heading-scalability"><strong>Scalability</strong></h3>
<p>Organizations need to ensure that the augmented analytics solutions they implement can handle large amounts of data and can be scaled to meet the changing needs of the organization. This can be a challenge as the organization’s data needs may change over time and as the solution adapts. </p>
<h2 id="heading-best-practices-for-augmented-analytics"><strong>Best Practices for Augmented Analytics</strong></h2>
<p>For organizations to fully utilize augmented analytics, they need to implement them effectively and efficiently. Below are some of the key best practices.</p>
<h3 id="heading-collaborate-to-build-trust"><strong>Collaborate to build trust</strong></h3>
<p>Collaborate with different departments and stakeholders to build trust and buy-in. This includes involving key stakeholders in the planning and implementation. Make sure they understand how the solution will benefit the organization.</p>
<p>By building trust and buy-in, organizations can ensure that the solution is being used to its full potential and that it’s driving business value.</p>
<h3 id="heading-create-a-data-driven-culture"><strong>Create a data-driven culture</strong></h3>
<p>Create a data-driven culture. This includes should include training and development programs. The programs must ensure that employees have the necessary data literacy and analytics proficiency to effectively use the solution.</p>
<p>Furthermore, organizations must invest in solutions that guarantee the quality of their data and conform to legal and regulatory standards. </p>
<h3 id="heading-automate-your-workflows"><strong>Automate your workflows</strong></h3>
<p>By automating workflows, organizations can improve efficiency and reduce the risk of errors. This includes automating data integration and management as well as automating the generation of insights and reports.</p>
<h3 id="heading-start-small-and-align-kpis"><strong>Start small and align KPIs</strong></h3>
<p>Start small and align key performance indicators (KPIs) with the organization’s goals. This includes identifying specific use cases and goals as well as outlining the data and personnel resources required. By starting small and aligning KPIs, organizations can see to it that the solution is meeting its goals and driving business value.</p>
<h2 id="heading-plutoras-augmented-analytics-tool-and-how-it-can-help"><strong>Plutora’s Augmented Analytics Tool and How It Can Help</strong></h2>
<p>Plutora’s augmented analytics tool is designed to empower agile business users such as product managers and business analysts with DevOps metrics. It’s a simple and user-friendly platform that offers a variety of features. These features help users perform ETL, create visualizations, and conduct data analysis.</p>
<p>With Plutora’s augmented analytics tool, your application delivery teams can address issues and track outcomes. By utilizing machine learning and artificial intelligence, it can reveal valuable data insights. These insights can help users make better decisions and improve their overall business performance.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Augmented analytics is a powerful tool that can help organizations make sense of their data and make better decisions. Furthermore, as organizations generate more data, augmented analytics will become increasingly important for staying competitive and extracting valuable insights. Investing in an augmented analytics solution can be a step in the right direction for organizations looking to improve their decision-making processes, boost efficiency and productivity, and drive business growth. If you want to learn more about how augmented analytics can help, we’re hosting a webinar on the topic.</p>
<p><em>This post was originally published on</em> <a target="_blank" href="https://www.plutora.com/blog/augmented-analytics-how-it-help"><strong><em>Plutora's blog</em></strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Markdown for Technical Writers: Tips, Tricks, and Best Practices]]></title><description><![CDATA[As a technical writer, you know how important it is to create documentation that is not only informative but also visually appealing and easy to read. However, formatting and styling your documents can be a tedious and time-consuming task, especially...]]></description><link>https://blog.mitolu.dev/markdown-for-technical-writers-tips-tricks-and-best-practices</link><guid isPermaLink="true">https://blog.mitolu.dev/markdown-for-technical-writers-tips-tricks-and-best-practices</guid><category><![CDATA[markdown]]></category><category><![CDATA[development]]></category><category><![CDATA[Technical writing ]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Thu, 09 Mar 2023 15:16:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678201630629/36c9d90c-e3b8-4615-9f0d-4b5edda94bad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a technical writer, you know how important it is to create documentation that is not only informative but also visually appealing and easy to read. However, formatting and styling your documents can be a tedious and time-consuming task, especially when working with complex content. This is where Markdown can be a lifesaver - a simple markup language that can help you create polished and professional-looking documentation without spending hours on formatting.</p>
<p>In this article, I'll share my experience with Markdown and how it has improved my technical documentation. I'll also provide some tips, tricks, and best practices that I've learned along the way. Whether you're an experienced technical writer or just starting out, this article will equip you with the knowledge you need to begin using Markdown and enhance your technical writing skills.</p>
<h2 id="heading-what-is-markdown">What is Markdown?</h2>
<p>Markdown is a lightweight <a target="_blank" href="https://en.wikipedia.org/wiki/Markup_language">markup language</a> that allows users to format text in a simple and intuitive way. It was developed in 2004 by John Gruber and Aaron Swartz, with the goal of creating a text-to-HTML conversion tool for writers who wanted an easy way to write for the web. Since then, Markdown has grown in popularity and is now used by a wide range of people, including programmers, writers, and bloggers.</p>
<p>The beauty of Markdown lies in its simplicity. Unlike other markup languages such as HTML, Markdown uses plain text with simple syntax to format text. This means that anyone can learn Markdown with ease, even if they have no programming experience.</p>
<h2 id="heading-markdown-and-html">Markdown and HTML</h2>
<p>Markdown and HTML are two markup languages that are commonly used for formatting text on the web. While they share some similarities, they are fundamentally different in terms of syntax and purpose.</p>
<p>Markdown is designed to be easy to read and write, using simple syntax that is intuitive for anyone to use. On the other hand, HTML uses more complex syntax and is designed to provide more advanced formatting capabilities.</p>
<h2 id="heading-benefits-of-using-markdown-for-technical-writing">Benefits of Using Markdown for Technical Writing</h2>
<p>There are many benefits to using Markdown for technical writing. Here are some of the benefits:</p>
<ul>
<li><p><strong>Easy to learn</strong>: Markdown's syntax is simple and intuitive, which makes it easy to learn even for non-technical writers. The syntax is intuitive and uses symbols like asterisks and hash symbols to indicate formatting elements like headings, lists, and emphasis.</p>
</li>
<li><p><strong>Faster Writing</strong>: With Markdown, you can focus on writing content, rather than formatting it. This speeds up the writing process and allows you to produce more content in less time.</p>
</li>
<li><p><strong>Portable</strong>: Markdown is a plain text format, which means that you can use it on any platform. Whether you’re working on a Windows, Mac, or Linux machine, you can use Markdown to format your files.</p>
</li>
<li><p><strong>Compatible with a range of tools and platforms</strong>: Markdown can be easily converted into HTML or other formats using conversion tools, and it is supported by most blogging and content management systems. This makes it highly compatible with a range of tools and platforms, including GitHub, Jekyll, and WordPress.</p>
</li>
<li><p><strong>Efficient and streamlined workflow</strong>: Markdown can speed up the writing process by allowing writers to focus on content rather than formatting. Since Markdown is a plain text format, it can be easily edited, revised, and collaborated on without the need for complex formatting tools.</p>
</li>
</ul>
<h2 id="heading-getting-started-with-markdown">Getting Started with Markdown</h2>
<p>To get started with Markdown, all you need is a plain text editor and some basic knowledge of Markdown syntax. Most text editors, such as Sublime Text, Atom, or Visual Studio Code, have built-in support for Markdown syntax highlighting.</p>
<h2 id="heading-basic-markdown-syntax">Basic Markdown Syntax</h2>
<h3 id="heading-headings">Headings</h3>
<p>To create a heading, start the line with one or more hash (#) symbols followed by a space and the heading text. The number of hash symbols indicates the level of the heading, with one hash symbol for the largest heading and six for the smallest.</p>
<pre><code class="lang-txt"># Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
</code></pre>
<h3 id="heading-formatting-text">Formatting Text</h3>
<p>You can format text using the following syntax:</p>
<p><em>Italic</em>: surround the text with a single asterisk (*).</p>
<p><strong>Bold</strong>: surround the text with two asterisks (**).</p>
<p><strong><em>Bold and Italic</em></strong>: surround the text with three asterisks (***).</p>
<p><code>Code</code>: surround the text with a backtick (`).</p>
<pre><code class="lang-txt">*Italic*
**Bold**
***Bold and Italic***
`Code`
</code></pre>
<h3 id="heading-lists">Lists</h3>
<p>Markdown supports ordered and unordered lists. To create an unordered list, prefix each item with an asterisk (*) or a dash (-) followed by a space, and then the list item. To create an ordered list, use a number followed by a period (.) and a space, and then the list item.</p>
<pre><code class="lang-txt">Unordered List:
- Item 1
- Item 2
- Item 3

Ordered List:
1. Item 1
2. Item 2
3. Item 3
</code></pre>
<p>To create a nested list, indent each item by four spaces or one tab. For example:</p>
<pre><code class="lang-md"><span class="hljs-bullet">-</span> Item 1
<span class="hljs-bullet">  -</span> Item 1.1
<span class="hljs-bullet">  -</span> Item 1.2
<span class="hljs-bullet">-</span> Item 2
<span class="hljs-bullet">  -</span> Item 2.1
</code></pre>
<ul>
<li><p>Item 1</p>
<ul>
<li><p>Item 1.1</p>
</li>
<li><p>Item 1.2</p>
</li>
</ul>
</li>
<li><p>Item 2</p>
<ul>
<li>Item 2.1</li>
</ul>
</li>
</ul>
<h3 id="heading-links-and-images">Links and Images</h3>
<p>To create a link, surround the link text with square brackets [] followed by the URL in parentheses ().</p>
<p>To insert an image, use the same syntax as for links but with an exclamation mark (!) at the beginning.</p>
<pre><code class="lang-md">Link:
[<span class="hljs-string">Link text</span>](<span class="hljs-link">https://www.example.com</span>)

Image:
![<span class="hljs-string">Alt text</span>](<span class="hljs-link">image-url</span>)
</code></pre>
<p><a target="_blank" href="https://www.example.com">Link text</a></p>
<p><img src="https://picsum.photos/300" alt="Lorem Picsum random photo" class="image--center mx-auto" /></p>
<h3 id="heading-blockquotes">Blockquotes</h3>
<p>Blockquotes are used to quote text from another source. To create a blockquote, add a greater than symbol (&gt;) before the text you want to quote.</p>
<pre><code class="lang-txt">&gt; This is a blockquote
</code></pre>
<blockquote>
<p>The more control you have over your attention, the more control you have over your future. - James Clear</p>
</blockquote>
<p>For a blockquote with multiple paragraphs, add a &gt; before each paragraph.</p>
<pre><code class="lang-txt">&gt; Don’t postpone joy until you have learned all of your lessons. Joy is your lesson.
&gt;
&gt; ~ Alan Cohen
</code></pre>
<blockquote>
<p>Don’t postpone joy until you have learned all of your lessons. Joy is your lesson.</p>
<p>~ Alan Cohen</p>
</blockquote>
<h2 id="heading-extended-markdown-syntax">Extended Markdown Syntax</h2>
<h3 id="heading-tables">Tables</h3>
<p>To create tables in Markdown, you can use the pipe (|) character to separate columns and the dash (-) character to separate the header row from the body. However, creating tables manually can be a bit tricky since the pipes must align vertically. So, I recommend using a <a target="_blank" href="https://www.tablesgenerator.com/markdown_tables">table generator tool</a> to simplify the process.</p>
<pre><code class="lang-txt">| Column 1   | Column 2   | Column 3   |
| ---------- | ---------- | ---------- |
| R1, C1     | R1, C2     | R1, C3     |
| R2, C1     | R2, C2     | R2, C3     |
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Column 1</td><td>Column 2</td><td>Column 3</td></tr>
</thead>
<tbody>
<tr>
<td>R1, C1</td><td>R1, C2</td><td>R1, C3</td></tr>
<tr>
<td>R2, C1</td><td>R2, C2</td><td>R2, C3</td></tr>
</tbody>
</table>
</div><h3 id="heading-code-blocks">Code Blocks</h3>
<p>To create a code block, use three backticks (```) followed by the programming language in which the code is written. Then, insert your code in between the backticks.</p>
<pre><code class="lang-txt">```javascript
console.log('example log')
```
</code></pre>
<p>The rendered output looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"example log"</span>);
</code></pre>
<h3 id="heading-horizontal-rules">Horizontal Rules</h3>
<p>To insert a horizontal rule, use three or more hyphens (---), asterisks (***) or underscores (___) on a separate line.</p>
<pre><code class="lang-txt">---
</code></pre>
<hr />
<h3 id="heading-footnotes">Footnotes</h3>
<p>Markdown also supports footnotes, which are useful for adding additional information or clarifications to your text without disrupting the flow of your content. To add a footnote, use the following syntax:</p>
<ol>
<li><p>Place a caret (^) where you want to add the footnote reference.</p>
</li>
<li><p>Add the footnote text in square brackets [], immediately followed by a colon (:).</p>
</li>
<li><p>Add the footnote content on a separate line, indented by one tab or four spaces.</p>
</li>
</ol>
<pre><code class="lang-md">The Mediterranean diet is a healthy eating pattern rich in fruits, vegetables, whole grains, and healthy fats like olive oil[^1]. This diet has been shown to improve heart health and reduce inflammation[^2]. To learn more about the Mediterranean diet, check out resources like Oldways[^3].

[<span class="hljs-symbol">^1</span>]: <span class="hljs-link">Keys A, Anderson JT, Grande F. "Serum cholesterol response to changes in the diet. IV. Particular saturated fatty acids in the diet." Metabolism. 1965;14:776-87.</span>
[<span class="hljs-symbol">^2</span>]: <span class="hljs-link">Estruch R, Ros E, Salas-Salvadó J, et al. "Primary prevention of cardiovascular disease with a Mediterranean diet supplemented with extra-vir</span>
</code></pre>
<p>When you render your Markdown document, the footnote reference will be replaced with a superscript number that links to the footnote content at the bottom of the page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678374771695/d673a0fc-cebf-4deb-8650-87d5592cb9c4.png" alt="Footnotes demo" /></p>
<h3 id="heading-task-lists">Task Lists</h3>
<p>Markdown also allows you to create task lists, which are useful for keeping track of items that need to be completed. To create a task list, use a hyphen (-), a space, and square brackets [] to create a checkbox. To mark an item as complete, add an x inside the brackets.</p>
<pre><code class="lang-txt">- [ ] Task 1
- [x] Task 2
- [ ] Task 3
</code></pre>
<p>Here's what the rendered output looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678374810568/1b535a77-6fd2-4d72-8bcb-f4b40b835a35.png" alt="Task Lists demo" class="image--center mx-auto" /></p>
<h3 id="heading-embedding-html">Embedding HTML</h3>
<p>Markdown also allows you to embed HTML code directly into your document. This can be useful when you need to include more advanced formatting that is not available in Markdown. To embed HTML code, simply include the HTML tags in your Markdown document. For example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color:red; background: #f9f9f9; border-radius: 10px"</span>
  &gt;</span>Click Me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>
&gt;</span>
</code></pre>
<p>Click Me</p>
<h2 id="heading-how-to-remember-markdown-syntax">How to Remember Markdown Syntax</h2>
<p>Although this looks like a lot of information, you don't need to memorize all of the Markdown syntaxes. You can always refer to this guide or use a Markdown cheat sheet. Additionally, after you've written a few Markdown articles, you'll start to remember the syntax.</p>
<p>Platforms like <a target="_blank" href="https://support.hashnode.com/en/articles/6423225-markdown-guidelines">Hashnode</a>, <a target="_blank" href="https://dev.to/new#markdown">Dev.to</a> and <a target="_blank" href="https://ghost.org/">Ghost</a> have provided cheat sheets for their Markdown editors. They also have a preview feature, allowing you to see your Markdown document's rendered output as you type.</p>
<h2 id="heading-how-to-speed-up-your-workflow">How to speed up your workflow</h2>
<h3 id="heading-keyboard-shortcuts">Keyboard Shortcuts</h3>
<p>The easiest way to speed up your workflow is through keyboard shortcuts. Keyboard shortcuts allow you to perform common tasks without using your mouse. Many text editors have keyboard shortcuts for working with Markdown syntax. The more universal are include:</p>
<ul>
<li><p>Ctrl + B: <strong>Bold</strong></p>
</li>
<li><p>Ctrl + I: <em>Italic</em></p>
</li>
<li><p>Ctrl + K: <a target="_blank" href="https://example.com">Add a link</a></p>
</li>
</ul>
<p>However, these shortcuts are only available in some text editors and IDEs. You can also use extensions to add keyboard shortcuts to your text editor.</p>
<h3 id="heading-tools-to-speed-up-your-workflow">Tools to speed up your workflow</h3>
<p>There are also many tools available that are designed specifically for working with Markdown. Here are a few popular ones:</p>
<ul>
<li><p><a target="_blank" href="https://typora.io/">Typora</a>: A cross-platform Markdown editor with a WYSIWYG interface.</p>
</li>
<li><p><a target="_blank" href="https://markdownpad.com/?ref=changelog">MarkdownPad</a>: A Windows-only Markdown editor with a live preview feature.</p>
</li>
<li><p><a target="_blank" href="https://ia.net/writer">iA Writer</a>: A macOS app that provides live previews and syntax highlighting.</p>
</li>
<li><p><a target="_blank" href="https://pandoc.org/MANUAL.html">Pandoc</a>: A command-line tool for converting Markdown to other formats, such as HTML, PDF, or LaTeX.</p>
</li>
</ul>
<h3 id="heading-extensions-to-use">Extensions to use</h3>
<p>There are many extensions that you can use to enhance the functionality of Markdown. Here are some of the popular extensions:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/yzhang-gh/vscode-markdown">Markdown All in One</a>: This extension adds many useful features to Markdown, such as a table of contents, a live preview, and auto-completion.</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.wordcount">Code Spell Check</a>: Enhances accuracy by detecting and highlighting grammatical errors and misspellings.</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint">markdownlint</a>: This extension checks your Markdown content for syntax errors and formatting issues, helping you to write better Markdown by highlighting common errors.</p>
</li>
<li><p><a target="_blank" href="https://redirect.viglink.com/?format=go&amp;jsonp=vglnk_167837522388510&amp;key=eac202ea7a96cf485281d6c4ffa2069e&amp;libId=lf197vjj0103es17000DL7dj3s42b&amp;loc=https%3A%2F%2Fwww.makeuseof.com%2Fvscode-ultimate-markdown-editor%2F&amp;ccpaConsent=1---&amp;v=1&amp;opt=true&amp;optExText=false&amp;out=https%3A%2F%2Fmarketplace.visualstudio.com%2Fitems%3FitemName%3Dms-vscode.wordcount&amp;ref=https%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3Dbest%2Bvscode%2Bextensions%2Bfor%2Bmarkdown%26oq%3Dbest%2Bextensions%2Bfor%2Bmarkdown%2B%26aqs%3Dchrome.1.69i57j0i22i30j0i390l5.16388j0j9%26sourceid%3Dchrome%26ie%3DUTF-8&amp;title=How%20to%20Turn%20VSCode%20Into%20the%20Ultimate%20Markdown%20Editor&amp;txt=Word%20Count">Word Count</a>: It counts the number of words, characters, and lines in your Markdown content. Word Count is a useful tool for tracking your progress and ensuring that your content meets your word count goals.</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=bierner.markdown-emoji">Markdown Emoji</a>: It provides a library of emoji that you can easily add to your Markdown content. Markdown Emoji is a fun and easy way to add some personality to your writing and engage with your readers.</p>
</li>
</ul>
<h2 id="heading-how-i-use-markdown-to-write-articles">How I Use Markdown to Write Articles</h2>
<p>To boost my productivity, I incorporate various tools along with the aforementioned extensions. These tools aid me in my writing and publishing tasks, as well as in note-taking and project management. I use specific tools for each task, including:</p>
<ul>
<li><p><strong>VS Code</strong>: for writing my articles.</p>
</li>
<li><p><strong>Hashnode (and Dev.to)</strong>: to publish personal articles</p>
</li>
<li><p><strong>GitHub</strong>: For safekeeping and easy access, I back up my articles on GitHub. This ensures that my work is secure, and I can always refer to it in the future if needed.</p>
</li>
<li><p><strong>Notion</strong>: Lastly, I use Notion as my project management tool and note-taking app. This versatile platform enables me to organize my tasks and ideas in one place, making it easier to stay on top of my work and manage my time more effectively.</p>
</li>
</ul>
<p>By using these tools, I can quickly write Markdown articles in VS Code and easily publish and back them up.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678375390391/adc207bc-3c12-486c-b8e4-0eb35a2af294.png" alt="My VS Code Writing Workspace" class="image--center mx-auto" /></p>
<h2 id="heading-markdown-best-practices">Markdown Best Practices</h2>
<p>Here are some best practices that you can follow when using Markdown for technical writing:</p>
<ul>
<li><p><strong>Don't use too many levels of heading</strong>: Use headings to break down your content into sections, but don't use too many headings. Too many headings can make your document look cluttered. Stick to using two or three levels of headings.</p>
</li>
<li><p><strong>Use lists</strong>: Use lists to break down complex information into smaller, more manageable pieces.</p>
</li>
<li><p><strong>Make links descriptive</strong>: When creating links, use descriptive anchor text instead of generic phrases like "click here" or "read more". This helps search engines understand the context of the link.</p>
</li>
<li><p><strong>Link to external resources</strong>: When linking to external resources, use the full URL instead of a relative link. This helps search engines understand the context of the link. It also helps boost your SEO.</p>
</li>
<li><p><strong>Add a table of contents</strong>: Add a table of contents to your document to make it easier for readers to navigate your content.</p>
</li>
<li><p><strong>Use alt text for images</strong>: Use alt text to describe images, which helps search engines and screen readers understand what the image is about.</p>
</li>
</ul>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Markdown is a versatile and efficient tool for technical writers to create high-quality content. By utilizing the basic and advanced syntax of Markdown, along with keyboard shortcuts and extensions, technical writers can streamline their workflow and save time.</p>
<p>Now that you have learned the benefits and best practices of using Markdown for technical writing, it's time to start incorporating it into your writing process. With a little practice and experimentation, you'll be amazed at how quickly you can create polished and professional-looking documents.</p>
<p>Thanks for reading. Happy writing!</p>
<p>I'd love to connect with you on <a target="_blank" href="https://twitter.com/israelmitolu"><strong>Twitter</strong></a> | <a target="_blank" href="https://www.linkedin.com/in/israeloyetunji/"><strong>LinkedIn</strong></a> | <a target="_blank" href="https://github.com/israelmitolu"><strong>GitHub</strong></a> | <a target="_blank" href="https://israelmitolu.netlify.app"><strong>Portfolio</strong></a></p>
<h2 id="heading-resources-and-further-reading">Resources and Further Reading</h2>
<ul>
<li><p><a target="_blank" href="https://www.markdownguide.org/">Markdown Guide</a></p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/powershell/scripting/community/contributing/general-markdown">Microsoft Docs: Markdown Best Practices</a></p>
</li>
<li><p><a target="_blank" href="https://document360.com/blog/markdown-for-technical-writing/">The Ins and Outs of Using Markdown for Technical Writing</a></p>
</li>
<li><p><a target="_blank" href="https://dzone.com/articles/writing-in-markdown-an-introduction">Writing in Markdown: An Introduction</a></p>
</li>
<li><p><a target="_blank" href="https://www.makeuseof.com/vscode-ultimate-markdown-editor/https://www.makeuseof.com/vscode-ultimate-markdown-editor/">How to Turn VSCode Into the Ultimate Markdown Editor</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[My 2022 Year in Review: To The Moon?]]></title><description><![CDATA[Reading through my journal and reflecting on the past year, I can't help but feel grateful for all the experiences that 2022 brought me. Of course, it had its fair share of highs and lows, achievements and lessons learned.
So, you might be wondering ...]]></description><link>https://blog.mitolu.dev/my-2022-year-in-review-to-the-moon</link><guid isPermaLink="true">https://blog.mitolu.dev/my-2022-year-in-review-to-the-moon</guid><category><![CDATA[Developer]]></category><category><![CDATA[Career]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Technical writing ]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Tue, 10 Jan 2023 10:59:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673338798468/ff01d6b9-48d2-4b53-9ee6-972e4678eb15.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Reading through my journal and reflecting on the past year, I can't help but feel grateful for all the experiences that 2022 brought me. Of course, it had its fair share of highs and lows, achievements and lessons learned.</p>
<p>So, you might be wondering why I named this post <em>To the Moon.</em> Well, it's a phrase that's popular among crypto traders and folks in the tech community. It means experiencing astronomical growth, you know like a rocket shooting off to the moon.</p>
<p>That was my mindset for the whole year, aiming high, shooting for the stars. But, did I make it to the moon? No, not quite. But I did take off, and that's what is important :)</p>
<blockquote>
<p>You don't have to be great to start, but you have to start to be great. — Zig Ziglar</p>
</blockquote>
<p>With that said, let's dive into how the journey began and how it's going. My goal in sharing my story is to not only give you an inside look into my journey but to also serve as a source of inspiration for you to have faith in God and pursue your passions.</p>
<h2 id="heading-timeline">Timeline</h2>
<h3 id="heading-q1-january-to-march">Q1: January to March</h3>
<p>At the start of 2022, I found myself struggling with a lack of direction and motivation. However, that changed when I came across the Year in Review articles of some amazing developers, like <a target="_blank" href="https://kadet.dev/blog/2021-happier-than-ever">Kadet</a>, <a target="_blank" href="https://yinkakun.medium.com/my-20-years-in-review-failures-growth-and-new-beginnings-fddf5fcbca0e">Yinka Adedire</a>, <a target="_blank" href="https://vickyikechukwu.hashnode.dev/2021-year-in-review-the-good-the-bad-and-the-expectations">Victor IK</a> and <a target="_blank" href="https://ruthikegah.xyz/2021-in-review-the-categorical-leap">Ruth Ikegah</a>. Their stories stood out to me and served as an inspiration to set my own goals for the year.</p>
<p>Among these goals include spending time to hone my skills, being active on Social media(Tech Twitter and Linkedin), and building quality relationships with friends and family.</p>
<p>Here are the highlights for the first quarter:</p>
<ul>
<li>Got AWS certified! I set a personal challenge to earn a Cloud certification, even though I don't plan to pursue a career in Cloud Engineering. Reading through all the AWS whitepapers was quite boring and draining, but it was a rewarding way to start the new year.</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.credly.com/badges/80971d35-5bb2-4211-974b-601f87a55887">https://www.credly.com/badges/80971d35-5bb2-4211-974b-601f87a55887</a></div>
<p> </p>
<ul>
<li>Took my first step into content creation by writing my first technical article 🎉</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://israelmitolu.hashnode.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline">https://israelmitolu.hashnode.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline</a></div>
<p> </p>
<ul>
<li><p>Tried my luck applying to about 10 technical writing jobs after writing my third technical article, but I didn't get any callback.</p>
</li>
<li><p>Joined freeCodeCamp as a Contributing Writer.</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/israelmitolu/status/1504050105569226757?s=20&amp;t=5ke_-psXpNS6u5Oj5HvOjg">https://twitter.com/israelmitolu/status/1504050105569226757?s=20&amp;t=5ke_-psXpNS6u5Oj5HvOjg</a></div>
<p> </p>
<ul>
<li><p>Completed <a target="_blank" href="https://developedbyed.com/p/the-creative-html5-css3-course">The Creative HTML5 and CSS3 Course</a> by Dev Ed.</p>
</li>
<li><p>Relearned Javascript concepts and methods.</p>
</li>
<li><p>Finally started learning React using Youtube videos and playlists.</p>
</li>
<li><p>At this point, I was struggling financially and had to seek ways to pay for my data subscription. I even had to borrow money from a couple of friends, it was that bad.</p>
</li>
</ul>
<h3 id="heading-q2-april-to-june">Q2: April to June</h3>
<p>Things started to get interesting during this period:</p>
<ul>
<li><p>I received a congratulatory mail on April 1st from ContentLab, inviting me to join their team as a Technical Author and Specialist.</p>
</li>
<li><p>Started making $$ from writing.</p>
</li>
<li><p>Started getting more engagements on my Twitter because of the published articles.</p>
</li>
<li><p>Got my first stars on a project on Github.</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/israelmitolu/">https://github.com/israelmitolu/</a></div>
<p> </p>
<ul>
<li><p>Received a couple of rejection emails that were difficult to take, but we move!</p>
</li>
<li><p>My productivity increased when I started using Vite for React projects. Even wrote an article on Vite that currently has 11k+ views on Hashnode.</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://israelmitolu.hashnode.dev/why-you-should-ditch-create-react-app-for-vite">https://israelmitolu.hashnode.dev/why-you-should-ditch-create-react-app-for-vite</a></div>
<p> </p>
<ul>
<li><p>Joined HitSubscribe as a freelance writer in May.</p>
</li>
<li><p>2 of my articles got featured on Google's homepage.</p>
</li>
<li><p>Started contributing to Open Source.</p>
</li>
<li><p>Got accepted as a writer in the guest author program at Smashing magazine.</p>
</li>
<li><p>I made a horrible mistake at work which affected my credibility 😢</p>
</li>
<li><p>One of my articles made it to the Honorable Mentions category in <a target="_blank" href="https://townhall.hashnode.com/the-epic-hashnode-writeathon-the-winners#heading-100-honorable-mentions">Hashnode's Writeathon</a>.</p>
</li>
<li><p>I was generally happier and more confident in myself as an individual because I was more self-aware of the value I was providing.</p>
</li>
<li><p>Became a community moderator for a Twitter community, <a target="_blank" href="https://twitter.com/i/communities/1532313139810906114">Level Up Coding</a> which currently has 7k+ members.</p>
</li>
</ul>
<h3 id="heading-q3-july-to-september">Q3: July to September</h3>
<p>This period was a rollercoaster, because of the many bad experiences I had, but I also had my fair share of good moments.</p>
<ul>
<li><p>My mental health was pretty much down the drain at some point, but I made a conscious effort to not let my thoughts spiral out of control. And you know what helped? My <a target="_blank" href="https://open.spotify.com/playlist/1mHHegPekht63z4EYH5ugM?si=072f7caa9efa489c">Spotify playlist</a>. It was like my own personal therapy session. Music has a way of soothing the soul, you know?</p>
</li>
<li><p>I got really into playing Garena Free Fire, which ended up being an addiction.</p>
</li>
<li><p>Worked on a very cool app, but I can't say much about it since it's confidential.</p>
</li>
<li><p>Treated myself to a brand new phone and I started financially supporting my family by helping to fund some projects at home.</p>
</li>
<li><p>Landed my first professional Frontend Engineer role at a startup and I started working with Typescript and Next.js, which is a very sweet combination if you ask me.</p>
</li>
<li><p>I lost a couple of gigs because frankly, I was lazy, and didn't want to stress myself anymore.</p>
</li>
<li><p>The rate at which I published articles reduced significantly.</p>
</li>
<li><p>Tried to revive my workouts, but the motivation went back to zero just after a few days.</p>
</li>
</ul>
<h3 id="heading-q4-october-to-december">Q4: October to December</h3>
<ul>
<li><p>Completed my first Hacktoberfest by making contributions to various projects through technical documentation.</p>
</li>
<li><p>Became a maintainer for an open-source project, <a target="_blank" href="https://github.com/Evavic44/portfolio-ideas">Portfolio Ideas</a>.</p>
</li>
<li><p>I had the opportunity to speak at a Twitter Space in December, where I shared my insights on how to scale a technical writing career. It was a great experience and I'm looking forward to more opportunities to share my knowledge in the future 🤩</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.linkedin.com/feed/update/urn:li:activity:7013099639855833088/">https://www.linkedin.com/feed/update/urn:li:activity:7013099639855833088/</a></div>
<p> </p>
<h2 id="heading-highlights">Highlights</h2>
<p>Here's a quick summary of everything that happened last year.</p>
<h3 id="heading-good">Good</h3>
<ul>
<li><p>Discovered a new passion(Technical Writing)</p>
</li>
<li><p>Grew my online presence.</p>
</li>
<li><p>I was accepted as a technical writer for several publications, although I didn't write for all of them. However, I was able to write for a variety of B2B, B2C, and SaaS organizations, including TomTom, StackHawk, SolarWinds, and others. I enjoyed the diverse range of topics and the opportunity to continue learning.</p>
</li>
<li><p>Connected with, and made friends with some wonderful people on tech twitter.</p>
</li>
<li><p>Improved my relationship with my family and friends.</p>
</li>
<li><p>Travelled to Lagos with my friends on two different occasions. It was pretty fun.</p>
</li>
<li><p>I was able to be a better friend.</p>
</li>
<li><p>Decided to start writing technical articles as a means to share and solidify my knowledge, and also build a reputation for myself. I wrote a total of 20 articles, including paid articles (we go again next year 🚀✨ )</p>
</li>
<li><p>Overall, I think I did well at managing to balance school responsibilities with my pursuits in coding, technical writing, and maintaining a social life.</p>
</li>
<li><p>Played the guitar at a couple of interesting events.</p>
</li>
<li><p>I was surprised to be recognized for my articles, both offline and online, by people I didn't know. It was amusing and I must admit, it was quite flattering 😂</p>
</li>
<li><p>I was able to read and understand way faster than before, which really helped my academics.</p>
</li>
<li><p>Got 4 of my articles featured on <a target="_blank" href="https://app.daily.dev/IsraelMitolu">daily.dev</a> with over 300 upvotes.</p>
</li>
<li><p>Became freeCodeCamp's 2022 top contributor. I hope to do more in 2023!</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/israelmitolu/status/1601791165678112768?s=20&amp;t=IFggC94wX8UcxZj7A45Igg">https://twitter.com/israelmitolu/status/1601791165678112768?s=20&amp;t=IFggC94wX8UcxZj7A45Igg</a></div>
<p> </p>
<h3 id="heading-bad">Bad</h3>
<ul>
<li><p>During the ASUU strike, I, unfortunately, gained weight due to my unhealthy habits of eating junk food and spending all day sitting in front of my laptop.</p>
</li>
<li><p>I wasn't able to put as much time and effort into my personal projects as I had hoped, which included crafting a new <a target="_blank" href="https://israelmitolu.netlify.app">portfolio</a>.</p>
</li>
<li><p>I couldn't read as many books as I would have loved to.</p>
</li>
<li><p>My relationship with God had its struggles, I experienced some deep existential crises that caused my faith (being Christian) to waver. But, I am glad to say that I am recovering. I am grateful to know that God remains faithful and continues to show that He's intentional about me.</p>
</li>
<li><p>I came up with a lot of ideas for apps to develop and articles to write, but unfortunately, I wasn't able to get to work on them.</p>
</li>
<li><p>Procrastinated a whole lot.</p>
</li>
<li><p>Made mistakes that cost me a lot of money.</p>
</li>
<li><p>I wasn't opportune to attend any tech events.</p>
</li>
<li><p>Didn't get any merch or swag 😩</p>
</li>
<li><p>Didn't apply to as many jobs as I should have</p>
</li>
</ul>
<h2 id="heading-goals-for-2023">Goals for 2023 🚀</h2>
<p>As we reflect on the past year, it's also important to think about what we want to achieve in the future. One key aspect of this is setting goals for the upcoming year. In this section, I'll share my goals for 2023 and what I hope to accomplish in the coming months.</p>
<p>From professional aspirations to personal development, I'll be sharing my thoughts on what I want to accomplish in the new year. The purpose of this is not only to reflect on the past but also to set an intention and roadmap for the future. I invite you to join me as we explore the aspirations I have for 2023 and hopefully, this section will inspire you to think about your own goals for the year.</p>
<p>My primary objective for the new year is to deepen my personal relationship with God. I would also like to:</p>
<ul>
<li><p>Launch at least 2 new products.</p>
</li>
<li><p>Get accepted into more guest author programs, and write more articles.</p>
</li>
<li><p>Make more contributions to open-source projects.</p>
</li>
<li><p>Dive into mobile development by exploring frameworks like React Native and Flutter.</p>
</li>
<li><p>Attend and speak at more tech events, twitter spaces, and meetups.</p>
</li>
<li><p>Read more books and expand my knowledge base.</p>
</li>
<li><p>Get new equipment for my music hobby, specifically a guitar (or two) and an effect pedal.</p>
</li>
<li><p>Get a new Macbook.</p>
</li>
<li><p>Pay more attention to my health by working out and eating healthily.</p>
</li>
<li><p>Help more people break into tech.</p>
</li>
<li><p>Win a hackathon.</p>
</li>
<li><p>Improve my skillset and achieve mastery in software engineering and technical writing.</p>
</li>
<li><p>Increase my visibility and reputation in my career field.</p>
</li>
<li><p>Save more money and start building passive income streams.</p>
</li>
<li><p>Aim to earn my first million(NGN).</p>
</li>
<li><p>Hopefully, move out and rent an apartment.</p>
</li>
</ul>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In conclusion, as we reflect on the past, it's important to remember that amidst the challenges we faced, there were also things to be grateful for. Whether it was the support of loved ones, moments of triumph, or valuable lessons learned, these moments helped to guide us through difficult times.</p>
<p>As we look ahead to 2023, let's carry that gratitude with us and use it to set our goals and intentions for the coming year. Let's aim to be better, achieve more, and make a positive impact on the world. Let's remember to be thankful for all that we have and for the new opportunities that come our way.</p>
<p>I would like to extend my deepest gratitude to all those who helped make this year a truly amazing one. This includes my family, friends, church community, bosses in the tech industry, and many others whose names I may not have mentioned. I would like to specifically thank Daveyhert, Victor IK, Victor Eke(Chief of Open Source), B.Stella, Cess, Peace, Promise, Victory, Sheifunmi, Favourite Jome, Boluwatife the Vue Knight, Kenny, Israel Odejobi, Idris, Taofiq, Timi Lawani, Harry and Blessing(Bibi) for their support and engagement with my posts. Your help and contributions have not gone unnoticed and I am deeply grateful.</p>
<p>Every year is a chance for a fresh start, let's make the most of it. Thank you for being a part of this journey and let's look forward to an even more amazing 2023. Together, let's strive for the stars and beyond. WAGMI! To The Moon! 🚀✨</p>
]]></content:encoded></item><item><title><![CDATA[Google Sheets API Tutorial: The Basics You Need to Get Going]]></title><description><![CDATA[The Google Sheets API enables us to read, write, and update a spreadsheet's data. We can also use it to render user interfaces (UIs) by fetching data from Google Sheets, which will then serve as a database. The Google Sheets API helps developers impo...]]></description><link>https://blog.mitolu.dev/google-sheets-api-tutorial-the-basics-you-need-to-get-going</link><guid isPermaLink="true">https://blog.mitolu.dev/google-sheets-api-tutorial-the-basics-you-need-to-get-going</guid><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[newbie]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Thu, 04 Aug 2022 08:19:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659568056174/VMheSrDAj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Google Sheets API enables us to read, write, and update a spreadsheet's data. We can also use it to render user interfaces (UIs) by fetching data from Google Sheets, which will then serve as a database. The Google Sheets API helps developers import data into spreadsheets and build apps that interact with Google Sheets, maximizing functions and increasing productivity.</p>
<p>In this Google Sheets API tutorial for beginners, you’ll learn how to use the Google Sheets API to perform basic CRUD operations.</p>
<h2 id="heading-prerequisites-for-google-sheets-api-tutorial">Prerequisites for Google Sheets API Tutorial</h2>
<p>This tutorial assumes that you have</p>
<ul>
<li>a basic understanding of JavaScript and Node.js,</li>
<li><a target="_blank" href="https://nodejs.org/en/">Node.js</a> and <a target="_blank" href="https://www.npmjs.com/">npm</a> installed on your computer,</li>
<li>a code editor (such as VS Code), and</li>
<li>a Google account.</li>
</ul>
<h2 id="heading-project-setup">Project Setup</h2>
<p>To use the Google Sheets API, you need a Google Cloud Platform Project with the API enabled, as well as authorization credentials. To get those, follow the steps below.</p>
<h3 id="heading-step-1-create-a-new-project">Step 1: Create a New Project</h3>
<p>First, open the <a target="_blank" href="https://console.cloud.google.com/">Google Cloud Console</a>, and then create a new project.
<img src="https://fusebit.io/assets/images/11ty/fb1bc7f1-900.png" alt="Create a new project" /></p>
<h3 id="heading-step-2-enable-api-and-services">Step 2: Enable API and Services</h3>
<p>At the top left, click <strong>Menu ☰ &gt; APIs and Services &gt; Enabled APIs and Services</strong>.</p>
<p>Then click on the <strong> + Enable APIs and Services </strong>button.
<img src="https://fusebit.io/assets/images/11ty/9e752c92-657.png" alt="Enable AP" /></p>
<h3 id="heading-step-3-create-a-service-account">Step 3: Create a Service Account</h3>
<p>Now that the API is enabled, it will direct you to a page where you can configure the settings for the API.</p>
<p>In the left sidebar, click on the <strong>Credentials</strong> tab, and then click the <strong>Create Credentials</strong> button at the top.</p>
<p>Next, select <strong>Service Account</strong> in the drop-down menu.</p>
<p><img src="https://fusebit.io/assets/images/11ty/2a6470b3-743.png" alt="Create a Service account" /></p>
<p>In the next screen, provide the service account details required; then, click <strong>Create and Continue</strong>.</p>
<p><img src="https://fusebit.io/assets/images/11ty/b1e15154-649.png" alt="Provide the service account details" /></p>
<p>Click <strong>Continue</strong> and <strong>Done</strong> respectively on the next two dialogs.</p>
<p>Now, your newly created service account will be on the credentials page.</p>
<p>Copy the email address of the service account to the clipboard, as we'll need it later to share the spreadsheet with this account.</p>
<p>You'll be directed to the next screen, where we'll create a new key. To do so, click on the <strong>Keys</strong> tab, and then click on the <strong>Add Key</strong> button.</p>
<p>Select the <strong>Create New Key</strong> option, and then the key type of <strong>JSON</strong>.</p>
<p><img src="https://fusebit.io/assets/images/11ty/3dd09575-632.png" alt="Create a new key JSON file" /></p>
<p>Lastly, rename the downloaded JSON file, and move it into your project folder. This keyfile contains the credentials of the service account that we need in our Node.js script to access the spreadsheet from Google Sheets.</p>
<h2 id="heading-how-to-use-the-google-sheets-api">How to Use the Google Sheets API</h2>
<p>Now that we're done setting up the project and its credentials in the Google cloud console, let's explore how to use the basic API functions in Google Sheets.</p>
<h3 id="heading-create-a-spreadsheet">Create a Spreadsheet</h3>
<p>Before diving into the code, head over to <a target="_blank" href="https://docs.google.com/spreadsheets/u/0/">Google Sheets</a> and make a new spreadsheet. Enter in some dummy data so that we have something to fetch while testing the API.</p>
<p><img src="https://fusebit.io/assets/images/11ty/e129fa16-424.png" alt="Dummy data in spreadsheet" /></p>
<p>Now, let’s add the service account email address and assign it the Editor role, which gives it permission to read, write, update, and delete data.</p>
<p>Click on the <strong>Share</strong> button in the top-right corner. This will open a modal where we'll share the spreadsheet with the service account. Make sure to uncheck the <strong>Notify people</strong> checkbox.</p>
<p><img src="https://fusebit.io/assets/images/11ty/6d193e37-574.png" alt="Assign the service account the role of editor" /></p>
<p>Click the <strong>Share</strong> button to share the spreadsheet with the service account.</p>
<h3 id="heading-application-setup">Application Setup</h3>
<p>Now that we're done with the configuration, let's get into the code. Open up your code editor and create a new project folder. I'll be using <a target="_blank" href="https://code.visualstudio.com/Download">VS Code</a>.</p>
<p>Copy and paste the downloaded keyfile into the root of the directory. Rename the file to a simpler one, like keys.json.</p>
<p>Next, navigate to the root of the project, open up the integrated terminal in VS Code, and run this command:</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>This command will initialize the directory and create an empty package.json file, which defines important information about the project such as dependencies and project version.</p>
<p>Next, let's install a couple of dependencies:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/googleapis#installation">Google APIs</a>, to access the Google Sheets API</li>
<li><a target="_blank" href="https://www.npmjs.com/package/express">Express</a>, to manage the server and routing</li>
<li><a target="_blank" href="https://www.npmjs.com/package/nodemon">nodemon</a>, for local development so that the server will restart when we save the file</li>
</ul>
<pre><code class="lang-bash">npm install googleapis express
</code></pre>
<p>When that is finished installing, run the following code to install nodemon as a dev dependency:</p>
<pre><code class="lang-bash">npm install nodemon --save-dev
</code></pre>
<p>After running the commands, you'll get a package-lock.json file and the node_modules folder.</p>
<p>++Now, to configure nodemon to restart the server on every file save, open up the package.json file and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"dev"</span>: <span class="hljs-string">"nodemon ."</span>
  },
</code></pre>
<p>This will enable us to run the dev server using the command <strong>npm run dev</strong>.</p>
<p>Now, your package.json file should look like this:</p>
<pre><code class="lang-js">{
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"express"</span>: <span class="hljs-string">"^4.18.1"</span>,
    <span class="hljs-string">"googleapis"</span>: <span class="hljs-string">"^101.0.0"</span>,
  },
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"dev"</span>: <span class="hljs-string">"nodemon ."</span>
  },
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"quickstart"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-string">"keywords"</span>: [],
  <span class="hljs-string">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-string">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-string">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"nodemon"</span>: <span class="hljs-string">"^2.0.19"</span>
  }
}
</code></pre>
<p>Note that the dependency versions may be different.</p>
<h3 id="heading-integrating-the-google-sheets-api">Integrating the Google Sheets API</h3>
<p>Next, create a file named index.js in the project folder. Open the index.js file and import the dependencies we just installed.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);

<span class="hljs-keyword">const</span> { google } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"googleapis"</span>);
</code></pre>
<p>Initialize Express and listen for the server. I'm using port 8080, but you can choose any port.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">8080</span>;

<span class="hljs-comment">//This allows us to parse the incoming request body as JSON</span>
app.use(express.json());

<span class="hljs-comment">// With this, we'll listen for the server on port 8080</span>
app.listen(port, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Listening on port <span class="hljs-subst">${port}</span>`</span>));
</code></pre>
<p>Next, add the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">authSheets</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">//Function for authentication object</span>
  <span class="hljs-keyword">const</span> auth = <span class="hljs-keyword">new</span> google.auth.GoogleAuth({
    <span class="hljs-attr">keyFile</span>: <span class="hljs-string">"keys.json"</span>,
    <span class="hljs-attr">scopes</span>: [<span class="hljs-string">"https://www.googleapis.com/auth/spreadsheets"</span>],
  });

  <span class="hljs-comment">//Create client instance for auth</span>
  <span class="hljs-keyword">const</span> authClient = <span class="hljs-keyword">await</span> auth.getClient();

  <span class="hljs-comment">//Instance of the Sheets API</span>
  <span class="hljs-keyword">const</span> sheets = google.sheets({ <span class="hljs-attr">version</span>: <span class="hljs-string">"v4"</span>, <span class="hljs-attr">auth</span>: authClient });

  <span class="hljs-keyword">return</span> {
    auth,
    authClient,
    sheets,
  };
}
</code></pre>
<p>Let's break the above code into bits:</p>
<p>First, we created a new Google auth object so that we can authorize the API request. This works by passing in the <strong>keyFile</strong>, which is the keys.json (assuming the file is stored at the root level of the project), and scopes property, which specifies the Google API we're using.</p>
<p>Second, the <strong>authClient</strong> variable stores the service account details (client instance) from the <strong>getClient()</strong> method once the details have been verified in the auth object.</p>
<p>Third, we create an instance of the Google Sheets API. It takes an object with two properties: version (the current version, in our case v4) and auth, the authClient that we created.</p>
<p>Lastly, we return the variables so that we can access the spreadsheet values in any routes of the app.</p>
<h3 id="heading-reading-data-from-a-spreadsheet">Reading Data From a Spreadsheet</h3>
<p>Now, to get the data from the spreadsheet, we'll use the <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get">sheets.spreadsheets.values.get</a> method, which takes in two required path parameters—<strong>spreadsheetId</strong> and <strong>range</strong>—and stores them in a response variable.</p>
<pre><code class="lang-js">app.get(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { sheets } = <span class="hljs-keyword">await</span> authSheets();

  <span class="hljs-comment">// Read rows from spreadsheet</span>
  <span class="hljs-keyword">const</span> getRows = <span class="hljs-keyword">await</span> sheets.spreadsheets.values.get({
    <span class="hljs-attr">spreadsheetId</span>: id,
    <span class="hljs-attr">range</span>: <span class="hljs-string">"Sheet1"</span>,
  });

  res.send(getRows.data);
});
</code></pre>
<p>We extract the <strong>spreadsheetId </strong>from the URL of the spreadsheet and store it in a global variable, <strong>id</strong>:</p>
<p><img src="https://fusebit.io/assets/images/11ty/d967e2d8-837.png" alt="Spreadsheet Id" /></p>
<p>The text underlined in red is the spreadsheet ID.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> id = <span class="hljs-string">"12fSSjNTpDtTJ8vF4T3LRYe36WjPEfn1_sr_lSSpZJUo"</span>;
</code></pre>
<p>While the range defines the range of cells to read from, here we use the spreadsheet name. Every spreadsheet file has at least one sheet, which is identified by the sheet name. In our example, we're using the sheet name Sheet 1, which you can find at the bottom left of the spreadsheet.</p>
<p>You can also limit the range of the sheet by adding the sheet name, an exclamation symbol, and then the column range (e.g., <strong>Sheet1!A1:C5</strong>).</p>
<p>The <strong>sheets.spreadsheets.values.get</strong> method has three other optional query parameters:</p>
<ul>
<li><strong>majorDimension</strong> defines the major dimension of the values, either rows or columns. The default is ROWS.</li>
<li><strong>valueRenderOption</strong> defines how values should be rendered in the output. The default is FORMATTED_VALUE.</li>
<li><strong>dateTimeRenderOption</strong> defines how dates, times and duration should be rendered in the output. The default is SERIAL_NUMBER.</li>
</ul>
<p>Now, run the following command in the terminal to start the server:</p>
<pre><code class="lang-js">npm run dev
</code></pre>
<p><img src="https://fusebit.io/assets/images/11ty/655726f9-476.png" alt="Screenshot of the Package.json file" /></p>
<p>Then, head over to your browser, and type in <a target="_blank" href="http://localhost:8080/">http://localhost:8080/</a>. The result should be something like this:</p>
<p><img src="https://fusebit.io/assets/images/11ty/46236701-369.png" alt="Response in the browser" /></p>
<p>If it doesn't look aligned like this, install <a target="_blank" href="https://chrome.google.com/webstore/detail/jsonvue/chklaanhfefbnpoihckbnefhakgolnmc/">JSONVue</a> to format your JSON files in the browser. With this, we've successfully read the data from our spreadsheet.</p>
<h3 id="heading-writing-and-updating-data-into-the-spreadsheet">Writing and Updating Data into the Spreadsheet</h3>
<p>To append data after a table of data in a sheet, use the <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append">sheets.spreadsheets.values.append</a> method, which takes in the spreadsheet ID, the range of cells to write into, the value entered by the user, and the resource object containing the information to insert into the rows.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Write rows to spreadsheet</span>
<span class="hljs-keyword">await</span> sheets.spreadsheets.values.append({
  <span class="hljs-attr">spreadsheetId</span>: id,
  <span class="hljs-attr">range</span>: <span class="hljs-string">"Sheet1"</span>,
  <span class="hljs-attr">valueInputOption</span>: <span class="hljs-string">"USER_ENTERED"</span>,
  <span class="hljs-attr">resource</span>: {
    <span class="hljs-attr">values</span>: [[<span class="hljs-string">"Gabriella"</span>, <span class="hljs-string">"Female"</span>, <span class="hljs-string">"4. Senior"</span>]],
  },
});
</code></pre>
<p>The  <strong>valueInputOption</strong> property defines how the input data should be interpreted. The values will be parsed according to how the user typed them into the UI.</p>
<p>The <strong>resource</strong> object has a child, <strong>values</strong>, which is an array of the data to be added to the sheets. Here, we're adding a new row with the values for the student name, gender, and class level.</p>
<p>Save the code and head over to Google Sheets, where you'll find that the new entry has been added. If not, refresh the local server and then go back to Google Sheets.</p>
<p><img src="https://fusebit.io/assets/images/11ty/4d4d9200-419.png" alt="Append data into a row" /></p>
<p>There's also the option of using the <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update">spreadsheets.values.update</a> request. This enables us to write data to a specified range. For example, the following code will update the class level of Alexandra to "2. Sophomore":</p>
<pre><code class="lang-js"><span class="hljs-keyword">await</span> sheets.spreadsheets.values.update({
  <span class="hljs-attr">spreadsheetId</span>: id,
  <span class="hljs-attr">range</span>: <span class="hljs-string">"Sheet1!C2"</span>,
  <span class="hljs-attr">valueInputOption</span>: <span class="hljs-string">"USER_ENTERED"</span>,
  <span class="hljs-attr">resource</span>: {
    <span class="hljs-attr">values</span>: [[<span class="hljs-string">"2. Sophomore"</span>]],
  },
});
</code></pre>
<p><img src="https://fusebit.io/assets/images/11ty/355f6783-400.png" alt="Updating data into a table" /></p>
<h3 id="heading-deleting-data-from-google-sheets">Deleting Data From Google Sheets</h3>
<p>Using the <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/clear">spreadsheets.values.clear</a> method, we can clear values from a spreadsheet. To do that, specify the spreadsheet ID and range. The code below will clear all the values from row A6 to C6:</p>
<pre><code class="lang-js"><span class="hljs-keyword">await</span> sheets.spreadsheets.values.clear({
  <span class="hljs-attr">spreadsheetId</span>: id,
  <span class="hljs-attr">range</span>: <span class="hljs-string">"Sheet1!A6:C6"</span>,
});
</code></pre>
<h3 id="heading-updating-spreadsheet-formatting">Updating Spreadsheet Formatting</h3>
<p>With the Google Sheets API, we can update the formatting of cells and ranges within spreadsheets.</p>
<p>The following code defines the style for each cell for the range defined. Here, we add a dashed border line with a red color:</p>
<pre><code class="lang-js"><span class="hljs-keyword">await</span> sheets.spreadsheets.batchUpdate({
  <span class="hljs-attr">spreadsheetId</span>: id,
  <span class="hljs-attr">resource</span>: {
    <span class="hljs-attr">requests</span>: [
      {
        <span class="hljs-attr">updateBorders</span>: {
          <span class="hljs-attr">range</span>: {
            <span class="hljs-attr">sheetId</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">startRowIndex</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">endRowIndex</span>: <span class="hljs-number">6</span>,
            <span class="hljs-attr">startColumnIndex</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">endColumnIndex</span>: <span class="hljs-number">3</span>,
          },
          <span class="hljs-attr">top</span>: {
            <span class="hljs-attr">style</span>: <span class="hljs-string">"DASHED"</span>,
            <span class="hljs-attr">width</span>: <span class="hljs-number">1</span>,
            <span class="hljs-attr">color</span>: {
              <span class="hljs-attr">red</span>: <span class="hljs-number">1.0</span>,
            },
          },
          <span class="hljs-attr">bottom</span>: {
            <span class="hljs-attr">style</span>: <span class="hljs-string">"DASHED"</span>,
            <span class="hljs-attr">width</span>: <span class="hljs-number">1</span>,
            <span class="hljs-attr">color</span>: {
              <span class="hljs-attr">red</span>: <span class="hljs-number">1.0</span>,
            },
          },
          <span class="hljs-attr">innerHorizontal</span>: {
            <span class="hljs-attr">style</span>: <span class="hljs-string">"DASHED"</span>,
            <span class="hljs-attr">width</span>: <span class="hljs-number">1</span>,
            <span class="hljs-attr">color</span>: {
              <span class="hljs-attr">red</span>: <span class="hljs-number">1.0</span>,
            },
          },
        },
      },
    ],
  },
});
</code></pre>
<p>This is the resulting layout:</p>
<p><img src="https://fusebit.io/assets/images/11ty/3c899282-382.png" alt="Styled borders" /></p>
<h2 id="heading-google-sheets-api-faqs">Google Sheets API FAQs</h2>
<p>Here are some frequently asked questions about the Google Sheets API.</p>
<h3 id="heading-can-i-use-google-sheets-api-for-free">Can I Use Google Sheets API for Free?</h3>
<p>Using the Google Sheets API is free, but each user has usage limits. The Google Sheets API <a target="_blank" href="https://developers.google.com/sheets/api/limits">usage limits</a> are quotas and limitations imposed by Google to make sure their API is used fairly and to protect their systems.</p>
<p><img src="https://fusebit.io/assets/images/11ty/2d28e505-900.png" alt="Limits of Google Sheets API" /></p>
<p>However, you may not exhaust this limit unless your app has a lot of users. If you do exceed the limits, you’ll get a <strong>429: Too many requests</strong> error. If this happens, try using the <a target="_blank" href="https://developers.google.com/sheets/api/limits#exponential">exponential backoff algorithm</a>.</p>
<h3 id="heading-how-do-i-use-apis-in-google-sheets">How Do I Use APIs in Google Sheets?</h3>
<p>Just as you can use the Google Sheets API to read and write Google Sheets, you can also take advantage of its connectivity to use other APIs.</p>
<p>Take, for example, the <a target="_blank" href="https://developer.fusebit.io/docs/google-sheets-addon">Fusebit add-on for Google Sheets</a>. Fusebit enables you to use Node.js, npm, and Fusebit Connectors to quickly connect to any API or data source with low friction and the flexibility of code. This gives you a lot of superpowers, including getting contacts from Salesforce, query data in MongoDB, importing companies from HubSpot, importing records from MySQL, downloading unpaid invoices from QuickBooks, and more. <a target="_blank" href="https://fusebit.io/blog/run-nodejs-from-google-sheets/">Check out the Fusebit blog</a> to see what else you can achieve with the Fusebit Google Sheets Add-On.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we've explored some basic functions of the Google Sheets API, and you've seen how easy it is to set up. But that's not all. From here, you can make anything simple from a Google Forms clone to a video requests app, or you can use it for something more complex like using it as a database from where an app fetches data and renders it on the front end.</p>
<p>I hope you found this post helpful and insightful. If you want to explore the other functions, check out the <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values">documentation</a>. Also, check out the complete code for this tutorial on <a target="_blank" href="https://github.com/israelmitolu/Google-Sheets-API_Node.js">Github</a>. </p>
<p>Happy coding!</p>
<p><em>This post was originally published on <a target="_blank" href="https://fusebit.io/blog/google-sheets-api-tutorial/">Fusebit blog</a></em></p>
]]></content:encoded></item><item><title><![CDATA[Learn how React Context API works by Building a Minimal Ecommerce Shopping App]]></title><description><![CDATA[So, this is a project that has been on my mind for a while, but I didn't put much thought or effort into building it.
Then Hashnode's Writeathon came up, and I thought, this is the perfect opportunity to write this article that will both help me impr...]]></description><link>https://blog.mitolu.dev/build-an-ecommerce-shopping-app-with-react-context-api</link><guid isPermaLink="true">https://blog.mitolu.dev/build-an-ecommerce-shopping-app-with-react-context-api</guid><category><![CDATA[THW Web Apps]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 20 May 2022 10:04:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1653038175996/MWXMCKIK5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, this is a project that has been on my mind for a while, but I didn't put much thought or effort into building it.</p>
<p>Then <a target="_blank" href="https://townhall.hashnode.com/the-epic-hashnode-writeathon?source=hashnode_feed">Hashnode's Writeathon</a> came up, and I thought, this is the perfect opportunity to write this article that will both help me improve my React knowledge and also help other developers who are learning about it for the first time or want to brush up on their knowledge of the subject. Win-win situation!</p>
<p>In this article, you'll learn about the React Context API, how it solves prop drilling, and how I built this simple shopping app with the following features:</p>
<ul>
<li>Store current items</li>
<li>Update the context when the user clicks on the "Add to Cart" button</li>
<li>Display the cart count in the Navigation bar</li>
<li>Add and remove items from the Cart</li>
<li>Save cart items to local storage</li>
</ul>
<p>Below is a screenshot of what we'll be building:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653040241254/HBRMNUmPm.png" alt="Fashion Store" /></p>
<p>If that looks good, let's get started!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes that you have :</p>
<ul>
<li>A basic knowledge of HTML, CSS, JavaScript, and React.</li>
<li><a target="_blank" href="https://nodejs.org/en/">Node</a> and <a target="_blank" href="https://www.npmjs.com/">npm</a> installed on your local development machine.</li>
<li>Code Editor(VS Code)</li>
</ul>
<h2 id="heading-overview-of-react-context">Overview of React Context</h2>
<h3 id="heading-what-is-react-context">What is React Context?</h3>
<p>React Context is a method used to pass data(and functions) from parent to child component(s), by storing the data in a store (similar to <a target="_blank" href="https://redux.js.org/">Redux</a>), from where you can easily access and import the data into whatever components you choose.</p>
<p>This is a better alternative to <em>prop drilling</em>, which is the term used to describe the passing of data through several layers of components, even if those components have no actual need for the data.</p>
<h3 id="heading-when-to-use-context">When to use Context?</h3>
<p>Context is designed to share data that can be considered "global" to the whole app. An example would be the currently authenticated user, a theme, or user preferences(for example, language or locale).</p>
<blockquote>
<p>"Context is primarily used when some data needs to be accessible by many components at different nesting levels. Apply it sparingly because it makes component reuse more difficult."</p>
<p>Source: <a target="_blank" href="https://reactjs.org/docs/context.html#when-to-use-context">Official Docs</a></p>
</blockquote>
<h2 id="heading-building-the-ecommerce-web-app">Building the eCommerce Web app</h2>
<h3 id="heading-illustrations">Illustrations</h3>
<p>Before we get into the code, let’s look at the component hierarchy to better understand the relationship between the components of the app.</p>
<p>The illustration below shows how data will be passed down from the root component level (<code>App</code>) to the component rendering what is to be displayed(<code>items</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653035193327/Up8FvCvWm.png" alt="Prop Drilling representation" /></p>
<p>However, what we'll be using in our app is what Context solves:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653035206884/XSzZi3Wq7.png" alt="With Context API" /></p>
<p>As you can see, the Context is like a store in your application. And once it is set up, you can simply import it into whatever component needs that data.</p>
<p>Now that we have gone through a basic overview of React Context, let's jump right into the project.</p>
<blockquote>
<p>Here is the <a target="_blank" href="https://shop-context.netlify.app/">live demo</a> of what we'll be building, and if you also want to see the code, it's available on <a target="_blank" href="https://github.com/israelmitolu/Learn-Context-by-Building-a-Shopping-Website">Github</a>.</p>
</blockquote>
<h3 id="heading-project-set-up">Project Set up</h3>
<p>Let's start by creating a new React project. I will be using Vite in this tutorial. If you haven't heard about it, do well to check out my <a target="_blank" href="https://israelmitolu.hashnode.dev/why-you-should-ditch-create-react-app-for-vite">previous article</a> on it.</p>
<p>Of course, feel free to use your bundler of choice: Vite or CRA.</p>
<pre><code class="lang-node"># vite
npm init vite@latest react-shopping-cart --template react

# create react app
npx create-react-app react-shopping-cart
</code></pre>
<p>Once it is finished, run:</p>
<pre><code class="lang-node">cd react-shopping-cart
npm install
</code></pre>
<p>Dependencies we'll be using:</p>
<ul>
<li><a target="_blank" href="https://reactrouter.com/docs/en/v6">React Router</a></li>
</ul>
<pre><code class="lang-node">npm install react-router-dom@6
</code></pre>
<ul>
<li><a target="_blank" href="https://styled-components.com/">Styled components</a></li>
</ul>
<pre><code class="lang-node">npm install --save styled-components
</code></pre>
<blockquote>
<p>Note: We won’t be covering the styling to keep our code minimal; this is just to explain how the app works.</p>
<p>Also, I have included some comments in the code so that you understand their purpose.</p>
</blockquote>
<h3 id="heading-context-setup">Context Setup</h3>
<p>In complex applications where the need for context is usually necessary, there can be multiple contexts, with each having its data and functions relating to the set of components that requires those data and functions.</p>
<p>For example, there can be a <code>ProductContext</code> for handling the components which use product-related data, and another <code>ProfileContext</code> for handling data related to authentication and user data.</p>
<p>However, for the sake of keeping things as simple as possible, we’ll use just one context instance.</p>
<p>In the <code>src</code> directory, create three folders: <code>Context</code>, <code>components</code> and <code>pages</code>.</p>
<p>Inside the <code>Context</code> folder create another folder, <code>Cart</code>.</p>
<p>Navigate to the <code>Cart</code> folder and add the following to a new file, <code>CartTypes.js</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// /src/Context/Cart/CartTypes.js`:</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ADD_TO_CART = <span class="hljs-string">"ADD_TO_CART"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> REMOVE_ITEM = <span class="hljs-string">"REMOVE_ITEM"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> INCREASE = <span class="hljs-string">"INCREASE"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> DECREASE = <span class="hljs-string">"DECREASE"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CHECKOUT = <span class="hljs-string">"CHECKOUT"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CLEAR = <span class="hljs-string">"CLEAR"</span>;
</code></pre>
<p>Here, we are defining the action types that our Context should have, and exporting them to be used within the Context.</p>
<p>Next, add the following to a new file, <code>CartContext.jsx</code> in the same directory to create the context:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> CartContext = createContext();

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> CartContext;
</code></pre>
<p>Next, create a new file <code>CartState.jsx</code> inside the <code>Cart</code> folder. Add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useReducer } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> CartContext <span class="hljs-keyword">from</span> <span class="hljs-string">"./CartContext"</span>;
<span class="hljs-keyword">import</span> CartReducer <span class="hljs-keyword">from</span> <span class="hljs-string">"./CartReducer"</span>;
<span class="hljs-keyword">import</span> { sumItems } <span class="hljs-keyword">from</span> <span class="hljs-string">"./CartReducer"</span>;

<span class="hljs-keyword">const</span> CartState = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-comment">//   Initial State of the cart</span>
  <span class="hljs-keyword">const</span> initialState = {
    <span class="hljs-attr">cartItems</span>: [],
    <span class="hljs-attr">checkout</span>: <span class="hljs-literal">false</span>,
  };

  <span class="hljs-comment">//Set up the reducer</span>
  <span class="hljs-keyword">const</span> [state, dispatch] = useReducer(CartReducer, initialState);

  <span class="hljs-comment">//Function to handle when an item is added from the store into the Cart</span>
  <span class="hljs-keyword">const</span> addToCart = <span class="hljs-function">(<span class="hljs-params">payload</span>) =&gt;</span> {
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"ADD_TO_CART"</span>, payload });
  };

  <span class="hljs-comment">//Function to handle when an item that is in the cart is added again</span>
  <span class="hljs-keyword">const</span> increase = <span class="hljs-function">(<span class="hljs-params">payload</span>) =&gt;</span> {
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"INCREASE"</span>, payload });
  };

  <span class="hljs-comment">//Function to handle when an item is removed from the cart</span>
  <span class="hljs-keyword">const</span> decrease = <span class="hljs-function">(<span class="hljs-params">payload</span>) =&gt;</span> {
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"DECREASE"</span>, payload });
  };

  <span class="hljs-comment">//Function to remove an item from the cart</span>
  <span class="hljs-keyword">const</span> removeFromCart = <span class="hljs-function">(<span class="hljs-params">payload</span>) =&gt;</span> {
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"REMOVE_ITEM"</span>, payload });
  };

  <span class="hljs-comment">//Function to clear the cart</span>
  <span class="hljs-keyword">const</span> clearCart = <span class="hljs-function">() =&gt;</span> {
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"CLEAR"</span> });
  };

  <span class="hljs-comment">//Function to handle when the user clicks the checkout button</span>
  <span class="hljs-keyword">const</span> handleCheckout = <span class="hljs-function">() =&gt;</span> {
    dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"CHECKOUT"</span> });
  };

  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">//Add the functions that have been defined above into the Context provider, and pass on to the children</span>
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CartContext.Provider</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">showCart:</span> <span class="hljs-attr">state.showCart</span>,
        <span class="hljs-attr">cartItems:</span> <span class="hljs-attr">state.cartItems</span>,
        <span class="hljs-attr">addToCart</span>,
        <span class="hljs-attr">removeFromCart</span>,
        <span class="hljs-attr">increase</span>,
        <span class="hljs-attr">decrease</span>,
        <span class="hljs-attr">handleCheckout</span>,
        <span class="hljs-attr">clearCart</span>,
        <span class="hljs-attr">...state</span>,
      }}
    &gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">CartContext.Provider</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> CartState;
</code></pre>
<p>Let's break the above code into bits.</p>
<p>First, the <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html#usereducer">useReducer</a> hook that is imported, accepts a reducer of type <code>(state, dispatch) =&gt; newState</code>, which then returns the current state. We also import the context files: <code>CartContext</code> and <code>CartReducer</code>.</p>
<p>Second, the <code>initialItems</code> is an array that defines the initial state of the cart when the page is loaded.</p>
<p>Third, in the <code>CartContext.Provider</code>, will render all of the props passed into it and will pass it through its <code>children</code>.</p>
<p>How the provider works is that the current context value is determined by the <code>value</code> prop of the nearest <code>&lt;CartContext.Provider&gt;</code>, and when it updates, the <code>useContext</code> hook will trigger a rerender with the latest context value passed to the <code>CartContext</code> provider.</p>
<p>Next, create a new file <code>CartReducer.jsx</code>, and add the following code:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// /src/Context/Cart/CartReducer.jsx</span>

<span class="hljs-comment">//Import the Action types</span>
<span class="hljs-keyword">import</span> {
  REMOVE_ITEM,
  ADD_TO_CART,
  INCREASE,
  DECREASE,
  CHECKOUT,
  CLEAR,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"./CartTypes.js"</span>;

<span class="hljs-comment">// Export function to calculate the total price of the cart and the total quantity of the cart</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sumItems = <span class="hljs-function">(<span class="hljs-params">cartItems</span>) =&gt;</span> {
  Storage(cartItems);
  <span class="hljs-keyword">let</span> itemCount = cartItems.reduce(
    <span class="hljs-function">(<span class="hljs-params">total, product</span>) =&gt;</span> total + product.quantity,
    <span class="hljs-number">0</span>
  );
  <span class="hljs-keyword">let</span> total = cartItems
    .reduce(<span class="hljs-function">(<span class="hljs-params">total, product</span>) =&gt;</span> total + product.price * product.quantity, <span class="hljs-number">0</span>)
    .toFixed(<span class="hljs-number">2</span>);
  <span class="hljs-keyword">return</span> { itemCount, total };
};

<span class="hljs-comment">// The reducer is listening for an action, which is the type that we defined in the CartTypes.js file</span>
<span class="hljs-keyword">const</span> CartReducer = <span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
  <span class="hljs-comment">// The switch statement is checking the type of action that is being passed in</span>
  <span class="hljs-keyword">switch</span> (action.type) {
    <span class="hljs-comment">// If the action type is ADD_TO_CART, we want to add the item to the cartItems array</span>
    <span class="hljs-keyword">case</span> ADD_TO_CART:
      <span class="hljs-keyword">if</span> (!state.cartItems.find(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id === action.payload.id)) {
        state.cartItems.push({
          ...action.payload,
          <span class="hljs-attr">quantity</span>: <span class="hljs-number">1</span>,
        });
      }

      <span class="hljs-keyword">return</span> {
        ...state,
        ...sumItems(state.cartItems),
        <span class="hljs-attr">cartItems</span>: [...state.cartItems],
      };

    <span class="hljs-comment">// If the action type is REMOVE_ITEM, we want to remove the item from the cartItems array</span>
    <span class="hljs-keyword">case</span> REMOVE_ITEM:
      <span class="hljs-keyword">return</span> {
        ...state,
        ...sumItems(
          state.cartItems.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id !== action.payload.id)
        ),
        <span class="hljs-attr">cartItems</span>: [
          ...state.cartItems.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id !== action.payload.id),
        ],
      };

    <span class="hljs-comment">// If the action type is INCREASE, we want to increase the quantity of the particular item in the cartItems array</span>
    <span class="hljs-keyword">case</span> INCREASE:
      state.cartItems[
        state.cartItems.findIndex(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id === action.payload.id)
      ].quantity++;
      <span class="hljs-keyword">return</span> {
        ...state,
        ...sumItems(state.cartItems),
        <span class="hljs-attr">cartItems</span>: [...state.cartItems],
      };

    <span class="hljs-comment">// If the action type is DECREASE, we want to decrease the quantity of the particular item in the cartItems array</span>
    <span class="hljs-keyword">case</span> DECREASE:
      state.cartItems[
        state.cartItems.findIndex(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id === action.payload.id)
      ].quantity--;
      <span class="hljs-keyword">return</span> {
        ...state,
        ...sumItems(state.cartItems),
        <span class="hljs-attr">cartItems</span>: [...state.cartItems],
      };

    <span class="hljs-comment">// If the action type is CHECKOUT, we want to clear the cartItems array and set the checkout to true</span>
    <span class="hljs-keyword">case</span> CHECKOUT:
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">cartItems</span>: [],
        <span class="hljs-attr">checkout</span>: <span class="hljs-literal">true</span>,
        ...sumItems([]),
      };

    <span class="hljs-comment">//If the action type is CLEAR, we want to clear the cartItems array</span>
    <span class="hljs-keyword">case</span> CLEAR:
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">cartItems</span>: [],
        ...sumItems([]),
      };

    <span class="hljs-comment">//Return the state if the action type is not found</span>
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> state;
  }
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> CartReducer;
</code></pre>
<p>Now that we're done setting up the context, the next thing will be to wrap the <code>App</code> inside the <code>Context</code>.</p>
<p>To do that, navigate to the <code>main.jsx</code>(Vite) or <code>index.js</code>(CRA) in the root directory. Add the following code:</p>
<pre><code class="lang-jsx"><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"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> CartState <span class="hljs-keyword">from</span> <span class="hljs-string">"./Context/Cart/CartState"</span>;

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">CartState</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">CartState</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>So, now our entire app has access to the Context.</p>
<h3 id="heading-building-out-the-components">Building out the Components</h3>
<p>For the <code>App.jsx</code>, we'll add the code that handles the application’s navigation.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar"</span>;
<span class="hljs-keyword">import</span> Store <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Store"</span>;
<span class="hljs-keyword">import</span> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/About"</span>;
<span class="hljs-keyword">import</span> { BrowserRouter, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> Cart <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Cart"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Store</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">About</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/cart"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Cart</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Now, let's create the components we'll need for our app's basic navigation to function properly.</p>
<p>Create a new file <code>Navbar.jsx</code> inside the <code>components</code> folder, and add the following:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// General</span>
<span class="hljs-keyword">import</span> { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Link, NavLink } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> CartIcon <span class="hljs-keyword">from</span> <span class="hljs-string">"/assets/icons/cart.svg"</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;
<span class="hljs-keyword">import</span> CartContext <span class="hljs-keyword">from</span> <span class="hljs-string">"../Context/Cart/CartContext"</span>;
<span class="hljs-keyword">import</span> { useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [toggle, setToggle] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [innerWidth, setInnerWidth] = useState(<span class="hljs-built_in">window</span>.innerWidth);

  <span class="hljs-comment">// Get Screen Size</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> changeWidth = <span class="hljs-function">() =&gt;</span> {
      setInnerWidth(<span class="hljs-built_in">window</span>.innerWidth);
    };

    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, changeWidth);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"resize"</span>, changeWidth);
    };
  }, []);

  <span class="hljs-comment">// Extract itemscount from CartContext</span>
  <span class="hljs-keyword">const</span> { cartItems } = useContext(CartContext);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">NavContainer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Left</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"/"}&gt;</span>FASHION.<span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Left</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">Right</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">NavRightContainer</span>
            <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
              <span class="hljs-attr">transform:</span>
                <span class="hljs-attr">innerWidth</span> &lt;= <span class="hljs-string">500</span>
                  ? <span class="hljs-attr">toggle</span> &amp;&amp; "<span class="hljs-attr">translateY</span>(<span class="hljs-attr">100vh</span>)"
                  <span class="hljs-attr">:</span> "<span class="hljs-attr">translateY</span>(<span class="hljs-attr">0</span>%)",
            }}
          &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">NavList</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">NavItem</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setToggle(!toggle)}&gt;
                  Store
                <span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">NavItem</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">NavItem</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setToggle(!toggle)}&gt;
                  About
                <span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">NavItem</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">NavItem</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://twitter.com/israelmitolu"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
                  Contact
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">NavItem</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">NavItem</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/cart"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setToggle(!toggle)}&gt;
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Cart<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">NavCartItem</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{CartIcon}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Shopping cart"</span> /&gt;</span>
                    {/* If the number of cartItems is greater than 0, display the
                    number of items in the cart */}
                    {cartItems.length &gt; 0 &amp;&amp; (
                      <span class="hljs-tag">&lt;<span class="hljs-name">CartCircle</span>&gt;</span>{cartItems.length}<span class="hljs-tag">&lt;/<span class="hljs-name">CartCircle</span>&gt;</span>
                    )}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">NavCartItem</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">NavItem</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">NavList</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">NavRightContainer</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">MenuBtn</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setToggle(!toggle)}&gt;
            <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">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">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">MenuBtn</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Right</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">NavContainer</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Nav</span>&gt;</span></span>
  );
};
</code></pre>
<p>The above code sets up the Navigation bar, which will look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653035226678/jJShXDGNf.PNG" alt="Navigation Bar" /></p>
<p>In the <code>pages</code> folder, which is in the <code>src</code> directory, create <code>Store.jsx</code>, <code>Cart.jsx</code> and <code>About.jsx</code>.</p>
<p>For the <code>Store.jsx</code>,</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { products } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;
<span class="hljs-keyword">import</span> ProductCard <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/ProductCard"</span>;

<span class="hljs-keyword">const</span> Store = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Browse the Store!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>New Arrivals for you! Check out our selection of products.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ProductsContainer</span>&gt;</span>
        {products.map((product) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">ProductCard</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{product.id}</span> <span class="hljs-attr">product</span>=<span class="hljs-string">{product}</span> /&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ProductsContainer</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Store;
</code></pre>
<p>The <code>Store</code> contains the Product Cards, which are generated dynamically by mapping through the available <code>products</code> array which is exported from the <code>data.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> products = [
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Cerveza Modelo"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">919.11</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/1.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Diesel Life"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1257.92</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/2.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Indian Cricket Team jersey"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1500.85</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/3.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">4</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"One Punch man - OK"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1250.9</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/4.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">5</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Hiking jacket"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1750.85</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/5.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">6</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Real Heart"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">3100.61</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/6.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">7</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Fredd - Black and White"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1801.1</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/7.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">8</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Star Wars - The Last"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1199.99</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/8.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">9</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Yellow Blouse"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">2395.16</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/9.png"</span>,
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-number">10</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Rick and Morty - Supreme"</span>,
    <span class="hljs-attr">price</span>: <span class="hljs-number">1243.82</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/assets/img/10.png"</span>,
  },
];
</code></pre>
<p>The <code>ProductCard</code> component shows the product details for each product.</p>
<p>Note that we would import useContext and CartContext in all the components where we need the data that is stored in the context.</p>
<p>The <code>onClick</code> events in the buttons handle the <code>addToCart</code> and <code>increase</code> functions which we have extracted from the CartContext:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> { formatCurrency } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;
<span class="hljs-keyword">import</span> CartContext <span class="hljs-keyword">from</span> <span class="hljs-string">"../Context/Cart/CartContext"</span>;
<span class="hljs-keyword">import</span> { useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> ProductCard = <span class="hljs-function">(<span class="hljs-params">{ product }</span>) =&gt;</span> {
  <span class="hljs-comment">// Extract these functions from the CartContext</span>
  <span class="hljs-keyword">const</span> { addToCart, increase, cartItems, sumItems, itemCount } =
    useContext(CartContext);

  <span class="hljs-comment">//Check whether the product is in the cart or not</span>
  <span class="hljs-keyword">const</span> isInCart = <span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> !!cartItems.find(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id === product.id);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CardWrapper</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ProductImage</span>
        <span class="hljs-attr">src</span>=<span class="hljs-string">{product.image</span> + "?<span class="hljs-attr">v</span>=<span class="hljs-string">" + product.id}
        alt={product.name}
      /&gt;
      &lt;ProductName&gt;{product.name}&lt;/ProductName&gt;
      &lt;ProductCardPrice&gt;{formatCurrency(product.price)}&lt;/ProductCardPrice&gt;
      &lt;ProductCardButtons&gt;
        {isInCart(product) &amp;&amp; (
          &lt;ButtonAddMore
            onClick={() =&gt; {
              increase(product);
            }}
            className="</span><span class="hljs-attr">btn</span>"
          &gt;</span>
            Add More
          <span class="hljs-tag">&lt;/<span class="hljs-name">ButtonAddMore</span>&gt;</span>
        )}

        {!isInCart(product) &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> addToCart(product)}&gt;Add to Cart<span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ProductCardButtons</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">CardWrapper</span>&gt;</span></span>
  );
};
</code></pre>
<p>For the code below, we will extract the state and functions that we need for the <code>Cart</code> component, which are: <code>cartItems</code>, <code>checkout</code> and <code>clearCart</code>.</p>
<p>Then, if there are any items in the <code>cartItems</code> array, render the items as <code>CartItem</code> components to the page:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> CartItem <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/CartItem"</span>;
<span class="hljs-keyword">import</span> { useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> CartContext <span class="hljs-keyword">from</span> <span class="hljs-string">"../Context/Cart/CartContext"</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;
<span class="hljs-keyword">import</span> Checkout <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Checkout"</span>;
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Cart = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// Extract the functions from the Context</span>
  <span class="hljs-keyword">const</span> { cartItems, checkout, clearCart } = useContext(CartContext);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
          Shopping Cart
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>({cartItems.length})<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>

      {/* Show the checkout message when the Checkout Button has been clicked */}
      {checkout &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">CheckoutMsg</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Thank you for your purchase!<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
            Your order has been placed and will be delivered to you within 24
            hours.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ShopBtn</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{clearCart}</span>&gt;</span>Continue Shopping<span class="hljs-tag">&lt;/<span class="hljs-name">ShopBtn</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">CheckoutMsg</span>&gt;</span>
      )}

      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</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">CartItemWrapper</span>&gt;</span>
              {/* If cart is empty, display message, and if not, display each cart
            Item in cart: {cartItems.length} */}
              {cartItems.length === 0 ? (
                <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{}}</span>&gt;</span>Cart is empty<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
              ) : (
                <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
                  {cartItems.map((product) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">CartItem</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{product.id}</span> <span class="hljs-attr">product</span>=<span class="hljs-string">{product}</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">CartItemWrapper</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>
          {/* Checkout component  */}
          {cartItems.length &gt; 0 &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">Checkout</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">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<p>The <code>CartItem</code> component contains the items that are present in the current state. And, we'll extract some functions from the <code>CartContext</code>, namely: <code>removeFromCart</code>, <code>increase</code> and <code>decrease</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> CartContext <span class="hljs-keyword">from</span> <span class="hljs-string">"../Context/Cart/CartContext"</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;
<span class="hljs-keyword">import</span> { formatCurrency } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;
<span class="hljs-keyword">import</span> TrashIcon <span class="hljs-keyword">from</span> <span class="hljs-string">"/assets/icons/trash-outline.svg"</span>;
<span class="hljs-keyword">import</span> Plus <span class="hljs-keyword">from</span> <span class="hljs-string">"/assets/icons/add-circle-outline.svg"</span>;
<span class="hljs-keyword">import</span> Minus <span class="hljs-keyword">from</span> <span class="hljs-string">"/assets/icons/remove-circle-outline.svg"</span>;

<span class="hljs-keyword">const</span> CartItem = <span class="hljs-function">(<span class="hljs-params">{ product }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { removeFromCart, increase, decrease } = useContext(CartContext);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SingleCartItem</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CartImage</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{product.image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{product.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">h5</span>&gt;</span>{product.name}<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{formatCurrency(product.price)}<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>

      {/* Buttons */}
      <span class="hljs-tag">&lt;<span class="hljs-name">BtnContainer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> increase(product)}
          className="btn btn-primary btn-sm mr-2 mb-1"
        &gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Icon</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{Plus}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Qty: {product.quantity}<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>

        {/* Display a minus icon or trash/delete icon based on the quantity of a particular product is in the cart */}
        {product.quantity &gt; 1 &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> decrease(product)} className="btn"&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">Icon</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{Minus}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        )}

        {product.quantity === 1 &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> removeFromCart(product)} className="btn"&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">Icon</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{TrashIcon}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</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">BtnContainer</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">SingleCartItem</span>&gt;</span></span>
  );
};
</code></pre>
<h3 id="heading-adding-cart-management">Adding Cart Management</h3>
<p>Now that we can add, remove, and display products, the final thing to do is implement our cart management. We have already initialised the cart as an empty array in <code>CartState.jsx</code>, meaning that once we restart the app, it will revert to being empty.</p>
<p>Now, what we will do is make sure that we load the existing cart from the local storage on component load.</p>
<p>Update the <code>initialState</code> method in <code>CartState.jsx</code> as follows:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> initialState = {
  <span class="hljs-attr">cartItems</span>: storage,
  ...sumItems(storage),
  <span class="hljs-attr">checkout</span>: <span class="hljs-literal">false</span>,
};
</code></pre>
<p>Next, we need to define the <code>storage</code>, also in the <code>CartContext.jsx</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">//Local Storage</span>
<span class="hljs-keyword">const</span> storage = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"cartItems"</span>)
  ? <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"cartItems"</span>))
  : [];
</code></pre>
<p>Finally, in the <code>CartReducer.jsx</code>, we will define <code>Storage</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Storage = <span class="hljs-function">(<span class="hljs-params">cartItems</span>) =&gt;</span> {
  <span class="hljs-built_in">localStorage</span>.setItem(
    <span class="hljs-string">"cartItems"</span>,
    <span class="hljs-built_in">JSON</span>.stringify(cartItems.length &gt; <span class="hljs-number">0</span> ? cartItems : [])
  );
};
</code></pre>
<p>And export the function to calculate the total price of the cart and the total quantity of the cart</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sumItems = <span class="hljs-function">(<span class="hljs-params">cartItems</span>) =&gt;</span> {
  Storage(cartItems);
  <span class="hljs-keyword">let</span> itemCount = cartItems.reduce(
    <span class="hljs-function">(<span class="hljs-params">total, product</span>) =&gt;</span> total + product.quantity,
    <span class="hljs-number">0</span>
  );
  <span class="hljs-keyword">let</span> total = cartItems
    .reduce(<span class="hljs-function">(<span class="hljs-params">total, product</span>) =&gt;</span> total + product.price * product.quantity, <span class="hljs-number">0</span>)
    .toFixed(<span class="hljs-number">2</span>);
  <span class="hljs-keyword">return</span> { itemCount, total };
};
</code></pre>
<p>With this, we've successfully completed the implementation of the Shopping App.</p>
<p>Check out the <a target="_blank" href="https://shop-context.netlify.app/">live demo</a> and the <a target="_blank" href="https://github.com/israelmitolu/Learn-Context-by-Building-a-Shopping-Website">code repository</a> on Github.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>And we're done!</p>
<p>In the course of this article, we discussed Context and its use and used React to scaffold the interface of a minimal shopping app. We also used context to move data and methods between multiple components and added its functionality using <code>useReducer</code> and <code>dispatch</code>.</p>
<p>If you found this post useful (and I'm sure you did), do well to share this resource with your friends and co-workers, and follow me for more content. If you have a question or find an error or typo, kindly leave your feedback in the comments section.</p>
<p>Thanks for reading, and happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Animating in React (The Many Ways!)]]></title><description><![CDATA[This article introduces you to the different options for creating animations in React, in the process we will look at the pros, and cons of using each technique, and some important statistics, so you know when to use which of them.
Depending on the c...]]></description><link>https://blog.mitolu.dev/animation-libraries-for-react</link><guid isPermaLink="true">https://blog.mitolu.dev/animation-libraries-for-react</guid><category><![CDATA[THW Web Apps]]></category><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Tue, 10 May 2022 17:38:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1652203718720/jmW9E4oyJ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article introduces you to the different options for creating animations in React, in the process we will look at the pros, and cons of using each technique, and some important statistics, so you know when to use which of them.</p>
<p>Depending on the complexity of your animation, some techniques would be fine as they are, and others that are optimized to handle more complex animations.</p>
<p>Here are the several methods we will discuss in this article.</p>
<ol>
<li>Straight up CSS</li>
<li>React Transition Group</li>
<li>React Motion</li>
<li>React Spring</li>
<li>Greensock Animation Platform(GSAP)</li>
<li>Framer Motion</li>
</ol>
<p>Let's get right into it!</p>
<h2 id="heading-1-straight-up-css">1. Straight up CSS</h2>
<p>When building a simple web animation, this is a great option. Using CSS enhances your site performance by requiring only a small amount of resources from your browser and machine’s RAM, giving your app a decent feel when using transitions.</p>
<p>You also get some control by using the <code>animation</code> property and its sub-properties such as <code>animation-delay</code> and <code>animation-iteration-count</code>.</p>
<p>CSS is best when you have simple transitions like toggling the states of UI elements or doing a <code>fadeIn</code> or <code>fadeOut</code> animation.</p>
<p>However, suppose you want to chain more than three animations in a row. In that case, you should use a JavaScript (JS) library instead of CSS because CSS sequencing becomes complicated with delays, and you'll end up with a lot of recalculations if you miss the timing.</p>
<p>Now, let's look into the JS/ React libraries for animation.</p>
<h2 id="heading-2-react-transition-group">2. React Transition Group</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652200720936/3SufYS-s4.png" alt="React Transition Group" class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://reactcommunity.org/react-transition-group/">React Transition Group</a> offers a straightforward approach to animations and transitions by providing its in-built components such as <code>TransitionGroup</code> for defining animations.</p>
<p>It also exposes transition stages, manages classes, grouping elements, and manipulates the DOM in useful ways making animating very convenient.</p>
<p>Another exciting feature is that it's relatively small and won't add to the size of your bundle, and you can also use the CDN.</p>
<p>Here are some stats on React Transition Group:</p>
<ul>
<li><strong>Popularity</strong>: 9.2k stars on <a target="_blank" href="https://github.com/reactjs/react-transition-group">Github</a>, and more than 8.4 million weekly downloads on <a target="_blank" href="https://www.npmjs.com/package/react-transition-group">NPM</a>.</li>
<li><strong>Documentation</strong>: Its docs are beginner-friendly; include Codesandbox examples to better illustrate.</li>
<li><strong>Support for Typescript</strong>: React transition group comes with the backing for TypeScript out of the box, and it can be installed using the command below:</li>
</ul>
<pre><code class="lang-javascript">npm install @types/react-transition-group
</code></pre>
<ul>
<li><strong>Bundle size(minified)</strong>: <a target="_blank" href="https://bundlephobia.com/package/react-transition-group@4.4.2">13.5kB</a></li>
</ul>
<p>Overall, it's a good animation library to consider for your next React project.</p>
<h2 id="heading-3-react-motion">3. React Motion</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652200747086/tII4oDSmX.png" alt="React Motion.png" class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://github.com/chenglou/react-motion">React Motion</a> is an animation library for React applications that makes realistic animations easier to build and implement.</p>
<p>It makes use of physics to create animations that feel natural. All you have to do is provide values for <strong>stiffness</strong> and <strong>damping</strong> within one of its components.</p>
<ul>
<li><code>stiffness</code> refers to the speed with which the destination value is reached.</li>
<li><code>damping</code> is the friction encountered by the animated component during the transition.</li>
</ul>
<p>Sequencing animations using React Motion, however, is not as easy and readable as some of the alternatives due to its complex nature.</p>
<p>Now, let's see some stats:</p>
<ul>
<li><strong>Popularity</strong>: 20.8k stars on <a target="_blank" href="https://github.com/chenglou/react-motion">Github</a>, and more than 611k weekly downloads on <a target="_blank" href="https://www.npmjs.com/package/react-motion">NPM</a></li>
<li><strong>Documentation</strong>: They have <a target="_blank" href="https://github.com/chenglou/react-motion#readme">well-documented</a> examples of their features, and you can copy the source code of a given component.</li>
<li><strong>Support for Typescript</strong>: You can use Typescript with React Motion by using the <a target="_blank" href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-motion/index.d.ts">type definitions</a>.</li>
<li><strong>Bundle size(minified)</strong>: <a target="_blank" href="https://bundlephobia.com/package/react-motion@0.5.2">19.8kB</a></li>
</ul>
<h2 id="heading-4-react-spring">4. React Spring</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652200770486/g2Obt8BOm.PNG" alt="react spring" class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://react-spring.io/">React Spring</a> is a spring-physics based animation library that follows a modern approach to animation.</p>
<p>It’s highly flexible and covers most animations needed for your User interface.</p>
<p>React spring inherits some properties from React Motion, such as ease of use, interpolation, and performance.</p>
<p>One really cool special about React Spring is the ability to apply animations without relying on React to render updates frame by frame.</p>
<p>Now, let’s see how React spring stacks up against other React animation libraries:</p>
<ul>
<li><strong>Popularity</strong>: 23.1k stars on <a target="_blank" href="https://github.com/pmndrs/react-spring">Github</a>, and more than 727k weekly downloads on <a target="_blank" href="https://www.npmjs.com/package/react-spring">NPM</a></li>
<li><strong>Documentation</strong>: Well-written and beginner-friendly documentation. You can copy a code snippet from the documentation and test or preview CodeSandbox.</li>
<li><strong>Support for Typescript</strong>: You can install the package for <a target="_blank" href="https://www.npmjs.com/package/@react-spring/types">NPM</a> or <a target="_blank" href="https://yarnpkg.com/package/@react-spring/types">Yarn</a></li>
<li><strong>Bundle size(minified)</strong>: <a target="_blank" href="https://bestofjs.org/projects/react-spring">172kB!</a></li>
</ul>
<h2 id="heading-5-gsap">5. GSAP</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652200788941/OLDCEbirN.png" alt="React and GSAP" class="image--center mx-auto" /></p>
<p>The <a target="_blank" href="https://greensock.com/react/">Greensock Animation Platform (GSAP)</a> is a great animation library for the web because it enables you to animate just about anything that can be accessed with JavaScript, including DOM Elements, SVGs, generic objects, canvases, and more.</p>
<p>GSAP is a great tool to build simple to very complex physics-based animations. It allows developers to sequence motion and controls the animation dynamically.</p>
<p>GSAP is also a great choice because it is:</p>
<ul>
<li>flexible</li>
<li>lightweight</li>
<li>fast (estimated to be 20x faster than jQuery),</li>
<li>has a large and supportive community via the <a target="_blank" href="https://greensock.com/forums/">forums</a>.</li>
</ul>
<p>Now, for the report card, </p>
<ul>
<li><strong>Popularity</strong>: 14.1k stars on <a target="_blank" href="https://github.com/greensock/GSAP">Github</a>, and more than 270k weekly downloads on <a target="_blank" href="https://www.npmjs.com/package/gsap">NPM</a></li>
<li><strong>Documentation</strong>: Has <a target="_blank" href="https://greensock.com/docs/">well detailed docs</a> with Codepen examples. If you need any help, ask on the GreenSock [<a target="_blank" href="https://greensock.com/forums/">forums</a>]</li>
<li><strong>Support for Typescript</strong>: Use the Typescript definitions on <a target="_blank" href="https://github.com/greensock/GSAP/tree/master/types">Github</a></li>
<li><strong>Bundle size(minified)</strong>: <a target="_blank" href="https://bundlephobia.com/package/gsap@3.10.4">62.1kB</a></li>
</ul>
<blockquote>
<p>While you're here, check out <a target="_blank" href="https://israelmitolu.hashnode.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline">this article</a> to learn how to build a sleek preloader animation :)</p>
</blockquote>
<h2 id="heading-6-framer-motion">6. Framer Motion</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652200807808/IsILgxcUC.png" alt="Framer Motion" class="image--center mx-auto" /></p>
<p><a target="_blank" href="https://www.framer.com/motion/">Framer Motion</a> is a popular React animation library. Like GSAP, Framer Motion allows us to apply both simple and complex animations and transitions to DOM elements defined in React components.
Its syntax is easy to understand which helps you make animations faster and also improves the code readability.</p>
<p>Overall, Framer Motion is a very strong, highly customizable, and powerful library:</p>
<ul>
<li><strong>Popularity</strong>: 14.5k stars on <a target="_blank" href="https://github.com/framer/motion">Github</a>, and more than 1.1 million weekly downloads on <a target="_blank" href="https://www.npmjs.com/package/framer-motion">NPM</a></li>
<li><strong>Documentation</strong>: Easy to understand and beginner-friendly; you can find the source code of a given component in the docs and also view it in CodeSandbox.</li>
<li><strong>Support for Typescript</strong>: Framer motion supports Typescript off the bat. Even if the files are in TypeScript, you can write regular ES6/JavaScript and they will still work.</li>
<li><strong>Bundle size(minified)</strong>: <a target="_blank" href="https://bundlephobia.com/package/framer-motion@6.3.3">140.7kB</a></li>
</ul>
<p>Although it could get bulky as your animation elements grow, and also has a much larger bundle size when compared to some other libraries, its widely used nowadays, and merits consideration for your next React project.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There are a lot of options when it comes to creating user-friendly animations and transitions in your React applications. Many of which are easy to get started with and customize.</p>
<p>In this article, you have been introduced to some of these libraries, and hopefully, with the comparisons, you can choose the option that best suits your animation needs for your next React project.</p>
<p>Also, if you have used any other animation libraries, kindly share them in the comments section!</p>
<p>Thanks for reading, and have fun animating ;)</p>
<hr />
<p>Before you go, check out this list of other library animations:</p>
<ul>
<li><a target="_blank" href="https://github.com/FormidableLabs/react-animations">React-animations</a> — the library is built on all animations with animate.css. It is easy to use and has a lot of animation collections.</li>
<li><a target="_blank" href="https://github.com/animatedjs/animated">animated</a> — Declarative Animations Library for React and React Native</li>
<li><a target="_blank" href="https://github.com/juliangarnier/anime/">Anime.js</a> — Anime.js (/ˈæn.ə.meɪ/) is a lightweight JavaScript animation library with a simple, yet powerful API. It works with CSS properties, SVG, DOM attributes and JavaScript Objects.</li>
<li><a target="_blank" href="https://github.com/matthieua/WOW">wow</a> — Reveal Animations When You Scroll. Very Animate.css Friend.</li>
<li><a target="_blank" href="https://github.com/react-tools/react-move">react-move</a> — Beautiful, data-driven animations for React.</li>
<li><a target="_blank" href="https://github.com/julianshapiro/velocity">velocity</a> — Velocity is an animation engine with the same API as jQuery’s $.animate().</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Why You Should Ditch Create-React-App for Vite]]></title><description><![CDATA[Create React App (CRA) has long been the go-to tool for many developers of all skill levels when it comes to constructing a React app (beginners, intermediate and even experts). It does, however, have some significant drawbacks, which are speed and p...]]></description><link>https://blog.mitolu.dev/why-you-should-ditch-create-react-app-for-vite</link><guid isPermaLink="true">https://blog.mitolu.dev/why-you-should-ditch-create-react-app-for-vite</guid><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[THW Web Apps]]></category><category><![CDATA[create-react-app]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Thu, 05 May 2022 08:20:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1651737921306/kAgUe3YTx.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Create React App (CRA) has long been the go-to tool for many developers of all skill levels when it comes to constructing a React app (beginners, intermediate and even experts). It does, however, have some significant drawbacks, which are speed and performance.</p>
<p>It is well known that CRA can be somewhat slow while building a project and setting up a development server, requiring around 5-10 minutes (depending on factors like hard disks, and internet connectivity issues). Time typically adds up, which is why I'm going to introduce you to a tool called <a target="_blank" href="https://vitejs.dev/guide/">Vite</a>. Vite is a build tool similar to Webpack (CRA uses Webpack beneath the hood). More information is available <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">here</a>).)</p>
<p>In this article, I'll walk you through the process of building a React App with Vite. You will learn the differences between CRA and Vite, as well as some of its features and benefits, and also how to create a React App with Vite.</p>
<h2 id="heading-what-is-vite">What is Vite?</h2>
<p>Vite, pronounced <code>/vit/</code>, like "veet" is the next generation in frontend tooling. It focuses on speed and performance by improving the development experience for modern web projects.</p>
<p>Vite is created by Evan You, who is the creator of Vue.js, but it's not a Vue-only tool. It can be used for React, Preact, Svelte, Vue, Vanilla JS, and LitElements.</p>
<p>It consists of two major parts:</p>
<ul>
<li>A dev server which provides support for Hot Module Replacement (HMR) for updating modules during the execution of the application. When changes are made to the source code of an application, only the changes are updated, rather than the entire application. This feature helps speed up development time.</li>
<li>A build command that bundles your code with Rollup, pre-configured to output highly optimised static assets for production.</li>
</ul>
<h2 id="heading-how-does-vite-work">How does Vite work?</h2>
<p>At its core, Vite does 2 things</p>
<ol>
<li>Serve your code locally during development.</li>
<li>Bundle your Javascript, CSS and other assets together for production.</li>
</ol>
<p>There are other tools(bundlers) that do the same thing, such as webpack, Parcel and Rollup, so what makes Vite different?</p>
<p>The problem with the tools mentioned above is that they have to build everything on every save, and if you have a very large application, that could take several minutes every time you save, even with Hot reloading in some frameworks, the update speed gets significantly slower as you add more code and dependencies to the app.</p>
<p>Vite has taken a different approach to this, kind of a reverse approach. Vite starts the server right away, takes the dependencies that don't change often, and pre-bundles them using <code>esbuild</code>.</p>
<blockquote>
<p><a target="_blank" href="https://esbuild.github.io/">Esbuild</a> is a Javascript build tool written in Go, which pre-bundles dependencies 10-100 times faster than Javascript-based bundlers.</p>
</blockquote>
<p>Let's take a look at the illustrations below to get a better understanding of how it works.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651736119031/R1IfbXjmA.png" alt="Bundle Based Development Server" class="image--center mx-auto" /></p>
<p>The above diagram represents a traditional bundle based development server, where we have an entry point, the possible routes and all the modules, which are then bundled together, and then the development server is ready.</p>
<p>Vite, on the other hand, uses route-based code-splitting to figure out what parts of the code actually need to be loaded, and doesn't have to pre-bundle everything.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651736175487/vwwCzKcgT.png" alt="Native ES Modules Based Development Server" class="image--center mx-auto" /></p>
<p>Vite serves the source code using native ES Module support in modern browsers. This lets the browser take the job of bundling in development, which consequently makes your code to load instantly, no matter how large the app is.</p>
<p>It also supports Hot Module Replacement for an extremely fast feedback loop during development.</p>
<p>When building for production, Vite uses Rollup under the hood, so you don't have to worry about configuring it.</p>
<h2 id="heading-why-use-vite-over-cra">Why use Vite over CRA?</h2>
<p>You may be asking why you should use Vite now that we've covered what it is and how it works.</p>
<p>We've gone through a few benefits in the previous section, so I will just highlight them here.</p>
<h3 id="heading-performance">Performance</h3>
<p>Pre-bundling with ESbuild makes it 10 to 100 times faster than using any other JS bundler. This is because it helps improve page speed and convert CommonJS / UMD modules to ESM.</p>
<h3 id="heading-hot-module-replacementhmr">Hot Module Replacement(HMR)</h3>
<p>Vite uses HMR capabilities to keep track of changes in your application without reloading the full page. With the HMR API, the browser will only load the modified section of the page and still retain the application's state.</p>
<p>You don't need to manually set these up - when you create an app via <code>create-vite</code>, the selected templates would have these pre-configured for you already.</p>
<h3 id="heading-native-ecmascript-module-support">Native ECMAscript module support</h3>
<p>Vite supports ES modules natively. It allows you to develop for the browser with bare imports like Typescript and it converts them into properly versioned imports on build.</p>
<h3 id="heading-rich-features">Rich Features</h3>
<p>Out-of-the-box support for TypeScript, JSX, CSS and more.</p>
<p>Check out other Features <a target="_blank" href="https://vitejs.dev/guide/features.html">here</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before using Vite, you'll need a couple of prerequisites:</p>
<ul>
<li><a target="_blank" href="https://nodejs.org/en/download/">Node.js version 14.18</a> or higher</li>
<li>Package manager - <a target="_blank" href="https://www.npmjs.com/get-npm">Npm</a> or <a target="_blank" href="https://classic.yarnpkg.com/en/">Yarn</a></li>
<li>Compatible browser for development</li>
</ul>
<p>The third requirement is a browser that supports <strong>dynamic imports</strong>. You can check to see if your browser is supported by visiting: https://caniuse.com/es6-module-dynamic-import.</p>
<p>Most modern browsers are supported, with the exceptions being Internet Explorer, Opera Mini, and the Baidu browsers. But if you're on a somewhat recent version of Chrome, Edge, Safari, or Firefox, you should be all set.</p>
<h2 id="heading-creating-a-project-with-vite">Creating a Project with Vite</h2>
<p>To <a target="_blank" href="https://vitejs.dev/guide/#scaffolding-your-first-vite-project">create a Vite</a> application, open your terminal and navigate to the folder where you want to save the Vite program. Then run this command:</p>
<pre><code><span class="hljs-built_in">npm</span> create vite@latest
</code></pre><p>You'll be prompted to select a framework and a variant(template). In our case, we'll be using React, so select React.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651736259805/THXs5mvby.PNG" alt="Select framework and variant" class="image--center mx-auto" /></p>
<p>You can also directly specify the template you want to use and the project name in one single line: </p>
<pre><code>npm create vite@latest my<span class="hljs-operator">-</span>react<span class="hljs-operator">-</span>app <span class="hljs-operator">-</span><span class="hljs-operator">-</span>template react
</code></pre><p>Note: <code>my-react-app</code> is the name of the Vite application that we want to create. You can change it to whatever name you prefer.</p>
<p>Next, run the following commands in the terminal</p>
<pre><code>cd my-react-app
<span class="hljs-built_in">npm</span> install
<span class="hljs-built_in">npm</span> run dev
</code></pre><p>Moving on... Running the above command will start the development server. Then open your browser and enter <code>http://localhost:3000</code>.</p>
<p>You should see a React logo with a counter and a button, as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651736282804/e-_Eu7IBv.png" alt="New Vite + React App" class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There we go! We've looked at what Vite is, how it works, and some of its features. We also learned how to set up applications using Vite.</p>
<p>For this project, the create vite app command was set up in 10 seconds. After which I ran <code>npm install</code> to install dependencies, which took 35 seconds. So, all in all, the project was set up in 45 seconds. Which I'm sure you'll agree is way faster than CRA ;-)</p>
<p>I would love to hear your thoughts in the comments section, and if you enjoyed this post or found it insightful, kindly share it with your friends and colleagues. Also, consider subscribing to my blog.</p>
<p>Till next time, thanks for reading, and happy coding!</p>
<p>Before you go, here are some <a target="_blank" href="https://github.com/vitejs/awesome-vite#user-content-react">community-maintained templates</a> to check out.</p>
]]></content:encoded></item><item><title><![CDATA[Writing Cleaner CSS using BEM Methodology]]></title><description><![CDATA[Have you ever come across this syntax and assumed it was autogenerated and didn't give it much thought? There's a good chance you have.
what__is--this

That's what we'll look at in this article, where you'll learn about BEM, its advantages, and wheth...]]></description><link>https://blog.mitolu.dev/writing-cleaner-css-using-bem-methodology</link><guid isPermaLink="true">https://blog.mitolu.dev/writing-cleaner-css-using-bem-methodology</guid><category><![CDATA[CSS]]></category><category><![CDATA[#codenewbies]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[Sass]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Sat, 12 Mar 2022 08:50:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1647074203275/lHBCnLjgc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever come across this syntax and assumed it was autogenerated and didn't give it much thought? There's a good chance you have.</p>
<pre><code class="lang-HTML">what__is--this
</code></pre>
<p>That's what we'll look at in this article, where you'll learn about BEM, its advantages, and whether or not you should use it in your next project.</p>
<h2 id="heading-what-is-bem">What is BEM?</h2>
<p>The <strong>Block Element Modifier</strong> methodology (abbreviated BEM) is a popular naming convention for HTML and CSS classes that helps to keep your code organized and maintainable.</p>
<p>What is a "Maintainable code"?
Maintainable code is code that is simple to modify or extend, which implies it takes less time to make changes without affecting other things.</p>
<h2 id="heading-how-does-bem-work">How does BEM Work?</h2>
<p>A BEM class name consists of 3 entities:</p>
<ol>
<li>Block: A standalone entity that is meaningful on its own e.g <code>Navbar</code>, <code>button</code>, <code>image</code>, <code>footer</code>, <code>section</code>, <code>container</code>.</li>
<li>Element: A part of a block that has no standalone meaning and is semantically tied to its block. E.g <code>title</code>, <code>item</code>, <code>list</code>, <code>checkbox</code>.</li>
<li>Modifier: Is used to change the appearance, behaviour or state of a Block or Element. e.g <code>background</code>, <code>focus</code>, <code>hover</code>, <code>checked</code>.</li>
</ol>
<p>When all three are combined in a class name, it is written as:</p>
<pre><code>[<span class="hljs-built_in">block</span>]__[element]<span class="hljs-operator">-</span><span class="hljs-operator">-</span>[<span class="hljs-function"><span class="hljs-keyword">modifier</span>]</span>
</code></pre><p>Let's take a look at the illustration below to get a better understanding of how BEM works.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647066699265/2EPzPVSvG.png" alt="BEM Cards.png" /></p>
<p>From the illustration,</p>
<ul>
<li>The <code>card</code> is the <strong>Block</strong>, which can also be seen as a parent container.</li>
<li>The <code>card__image</code>, <code>card__description</code>, <code>card__button</code> are all <strong>Elements</strong>, which are semantically connected to the <code>card</code> Block. They can also be seen as child items, and are denoted by two underscores following the name of the block.</li>
<li>The <code>card__button--hovered</code> is a <strong>Modifier</strong> that manipulates the <code>card__button</code> so that we can style it without inflicting changes on any completely unrelated module. This is done by appending two hyphens to the name of the element.</li>
</ul>
<h2 id="heading-why-use-bem">Why Use BEM?</h2>
<ul>
<li>BEM methodology gives your CSS code a solid structure that remains simple and easy to understand.</li>
<li>It communicates the purpose of your selectors</li>
<li>BEM helps you think systematically about CSS organization. This enables you to work efficiently both alone and in teams.</li>
<li>BEM has a unique style to create CSS class names, we won't run into conflicts with other class names.</li>
</ul>
<h2 id="heading-when-to-use-bem">When to use BEM</h2>
<p>BEM is mostly used when you have a project that has multiple pages, HTML selectors and a large code structure, which tends to be difficult to maintain the code and may even lead to issues of specificity and inheritance.</p>
<p>BEM is not required in cases when you can already scope your components, such as styled-components or CSS modules.</p>
<h2 id="heading-profile-card-example">Profile Card example</h2>
<p>Here's a profile card I created to show how BEM is utilized. This is the card's final preview.</p>
<p>Before we go any further, I'd like you to try to figure out which of the components on this card is a Block, Element, or Modifier.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647070089795/mV1NjCeQj.PNG" alt="Profile Card.PNG" /></p>
<p>Awesome! You got it right. </p>
<p>Now, on to the code.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/israelmitolu/pen/XWzvyNJ">https://codepen.io/israelmitolu/pen/XWzvyNJ</a></div>
<p>After seeing those classes pile up in the HTML file, you might think that's a lot of classes for a simple card, and you'd be right.</p>
<p>Recall that we learned earlier that we won't conflict with other class names since "BEM has a unique style for producing CSS class names". Because the fact is that the more CSS you have in a project, the more things build up, and the more specificity and complexity issues you face.</p>
<p>Also, note that you should <strong>not leave a modifier isolated</strong>. Always keep modifiers together with the element class as seen on <strong>line 80</strong> in the HTML, <code>card__button</code> comes before the <code>card__button--hovered</code>.</p>
<h3 id="heading-view-compiled-css">View Compiled CSS</h3>
<p>I use SASS in this case since it allows me to easily nest my classes without having to type <code>.card</code> every time. But don't worry if you don't understand SASS;  you can view the compiled CSS by following the arrows in the screenshots below. </p>
<p><em>1. Click Dropdown</em>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647067987795/OW2Uucv0V.png" alt="Click Dropdown BEM.png" /></p>
<p><em>2. Click "View Compiled CSS"</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647068144071/NtpxrnXMq.png" alt="View Compiled CSS BEM.png" /></p>
<blockquote>
<p>Check out my <a target="_blank" href="https://www.freecodecamp.org/news/the-beginners-guide-to-sass/">article on freeCodeCamp</a> to help you get started with SASS.</p>
</blockquote>
<h2 id="heading-summary">Summary</h2>
<p>The goal of a methodology like BEM is to have a set of guidelines that help with understanding and keeping CSS maintainable, so even if you don't strictly follow it, it's a great way to guide the way you write your code.</p>
<p>Regardless of approach, it must be readable and maintainable. Consider using BEM for an easy way to keep your code clean and clear.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading as always. If you found this post insightful (and I'm sure you did 😉), do well to share this article with your friends and co-workers, and follow me for more content. If you have a question or find an error or typo, kindly leave your feedback in the comments section.</p>
<p>Check out the <a target="_blank" href="http://bem-card.netlify.app/">live demo</a> and the <a target="_blank" href="https://github.com/israelmitolu/BEM-Profile-Card-">code repository</a> on Github.</p>
<h2 id="heading-further-reading">Further Reading</h2>
<ul>
<li><a target="_blank" href="https://en.bem.info/methodology/">BEM Methodology Docs</a></li>
<li><a target="_blank" href="https://9elements.com/bem-cheat-sheet/">BEM Cheatsheet</a></li>
<li><a target="_blank" href="https://www.smashingmagazine.com/2018/06/bem-for-beginners/">BEM for Beginners</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to Easily Host a Website for Free Using Netlify]]></title><description><![CDATA[So you've built a website with a fantastic UI or a fantastic feature. It may be a personal portfolio project or a YouTube tutorial you were watching.
And you'd like to spread the word about the website you've built. It runs well on your computer, and...]]></description><link>https://blog.mitolu.dev/how-to-easily-host-a-website-for-free-using-netlify</link><guid isPermaLink="true">https://blog.mitolu.dev/how-to-easily-host-a-website-for-free-using-netlify</guid><category><![CDATA[webdev]]></category><category><![CDATA[newbie]]></category><category><![CDATA[Web Hosting]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Mon, 21 Feb 2022 13:06:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1645444936895/0WWZnH6J8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So you've built a website with a fantastic UI or a fantastic feature. It may be a personal portfolio project or a YouTube tutorial you were watching.</p>
<p>And you'd like to spread the word about the website you've built. It runs well on your computer, and you think it's ready for the rest of the world to see.</p>
<p>However, you are unsure of where to begin. This is where Netlify enters the picture.</p>
<p>Netlify is a hosting platform that allows you to easily launch websites and web apps without having to manually configure and manage them.</p>
<p>It includes plenty of features that make it a favourite among today's web developers:</p>
<ul>
<li>Deploy in seconds</li>
<li>Continuous integration and Continuous Deployment(CI/CD)</li>
<li>Instant Forms: You can collect data from users without having to build a backend</li>
<li>Prevent spam submissions on Forms</li>
<li>Easily deploy websites from Github, Gitlab or Bitbucket</li>
<li>Host using a custom domain, or Netlify’s own customizable domains. </li>
<li>Analytics for your sites</li>
<li>Server-less Functions</li>
<li>Netlify CMS to manage your blog posts</li>
</ul>
<p>Other features can be found <a target="_blank" href="https://netlify.com/features">here</a>.</p>
<p>In this article, you'll learn how to deploy a static site on Netlify, as well as some of Netlify's features.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You'll need a Github account to follow along with this tutorial(or, Gitlab or Bitbucket). To create one, head over to <a target="_blank" href="https://github.com">Github to sign up</a>. This is important since that is where you will store your source code. </p>
<h2 id="heading-step-1-sign-up-on-netlify">Step 1:  Sign up on Netlify</h2>
<p>Head over to the <a target="_blank" href="https://netlify.com">Netlify</a> homepage, click on <strong>Sign up</strong>. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446003349/pD_7eI_bT.PNG" alt="Netlify homepage" /></p>
<p>Select your preferred signup method. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446025458/K6oAbqmfw.PNG" alt="Sign up page" /></p>
<h2 id="heading-step-2-add-a-new-site">Step 2: Add a new site</h2>
<p>After signing up, you’ll be redirected to a dashboard where you can add your site. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446078397/fLhsFQqJo.PNG" alt="Netlify Team Overview" /></p>
<p>Now, to add a site, click on the <strong>Add new site</strong> button. The dropdown will display 3 options to add a new site. </p>
<ul>
<li>Import an existing project</li>
<li>Start from a template</li>
<li>Deploy manually. </li>
</ul>
<p>We'll choose the first option, which is to import an existing project. The other two methods will be discussed briefly later in this article.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446129602/DsPSkTxUb.PNG" alt="Add a new site to Netlify" /></p>
<h2 id="heading-step-3-connect-to-a-git-provider">Step 3: Connect to a Git provider</h2>
<p>Choose the Git provider for your website's source code. Github in this case.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446308232/wdjfpRk8Q.PNG" alt="Connect to a Git provider" /></p>
<h2 id="heading-step-4-choose-the-repository">Step 4: Choose the Repository</h2>
<p>Select the repository and branch you want to deploy from </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446348212/nXT9j5EZf.PNG" alt="Choose Repository and Branch to Deploy" /></p>
<h2 id="heading-step-5-configure-the-sites-settings">Step 5: Configure the site’s settings</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645446411800/ln6ru7s9Z.png" alt="Configure the Build settings" /></p>
<p>The options for deploying the website can be configured here. Set the <strong>Deployment Branch</strong> option to main (or whichever branch you intend to deploy).</p>
<p>You can go ahead and click <strong>Deploy Site</strong> if it's a simple static page.</p>
<p>But, if you're using a build tool or want to add some other options, you'll need to configure the build settings.</p>
<h2 id="heading-step-6-deploy-your-website">Step 6: Deploy your website</h2>
<p>You will now be directed to the Site overview page of the newly created webpage. The automated build process takes place here. With Netlify, the deployment process takes around a minute or less.</p>
<p>Your website has now been launched and is accessible via the internet. To check it out, click on the green link. </p>
<h2 id="heading-additional-points">Additional Points</h2>
<h3 id="heading-custom-domain">Custom Domain</h3>
<p>Awesome! You have launched your website 🎉. But before you share it with the world, you should change the domain name to something more in line with what the project is showcasing, and not just a random domain name.</p>
<p>Luckily for us, Netlify allows us to change the site name from the randomly generated one, to do that, click on <strong>Domain Settings</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645447395861/MOlQHSkOq.PNG" alt="Add a custom domain to your site" /></p>
<p>Toggle the <strong>Options</strong> dropdown and select <strong>Edit site name</strong> from the list. After that, type in the name you want to use, and save it.</p>
<p>If you want to use a custom domain, check out the <a target="_blank" href="https://docs.netlify.com/domains-https/custom-domains/">docs</a> on how to do so.</p>
<h3 id="heading-netlify-drop">Netlify Drop</h3>
<p>Remember how we said in Step 2 of the first method that there are three ways to add a new site to Netlify? The "Deploy manually" approach, also known as Netlify Drop, is used here.</p>
<p>You can create a new site by simply dragging a project folder on your computer to the deploy dropzone in your browser. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645447533299/W1MjnLHWf.PNG" alt="Netlify Drop" /></p>
<p>Check out the <a target="_blank" href="https://www.netlifycms.org/docs/start-with-a-template/">docs</a> for more information.</p>
<h3 id="heading-netlify-templates">Netlify Templates</h3>
<p>The other way of adding a new site is by using Netlify Templates. This is mainly used to create Jamstack sites, or when you want to add <a target="_blank" href="https://www.netlifycms.org/docs/start-with-a-template/">Netlify CMS</a> to an existing site. </p>
<h3 id="heading-make-changes-to-your-deploy">Make changes to your deploy</h3>
<p>Anytime you link Netlify to your Git provider, such as Github, it immediately updates when it detects a new change to the branch/repo that is connected.</p>
<p>After you have made a change to the code in your text editor, all you have to do is drag and drop the folder again into the deploy dropzone. </p>
<h3 id="heading-your-sites-are-secured">Your sites are secured</h3>
<p>Another interesting thing about Neltify is that your sites are HTTPS enabled(Hypertext Transfer Protocol Secure) with its Transport Layer Security (TLS) protocol option with a Let's Encrypt certificate. </p>
<p>All of this ensures that your Netlify-hosted sites are:</p>
<ul>
<li><strong>Secure</strong>: Protects users' information from being stolen, particularly when forms or data are being submitted.</li>
<li><strong>SEO optimised</strong>: Would rank higher than sites that are not secured. </li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! Your website is live, up and running. And you can now share with anyone using the customised URL.</p>
<p>That's it. Setting up your website on Netlify is as simple as that. I hope you found this post to be helpful. If you liked it, please leave a like and a comment.</p>
<p>Now go off and create amazing things, and share what you've created with the rest of the world!  </p>
]]></content:encoded></item><item><title><![CDATA[Using Custom Cursors with Javascript for a Better User Experience]]></title><description><![CDATA[Have you seen the popular trend where websites use a customized cursor that is different from the standard cursor and makes the site stand out from other websites? You might even be curious about how those cursors are created.
That's something I've h...]]></description><link>https://blog.mitolu.dev/using-custom-cursors-with-javascript-for-a-better-user-experience</link><guid isPermaLink="true">https://blog.mitolu.dev/using-custom-cursors-with-javascript-for-a-better-user-experience</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[webdev]]></category><category><![CDATA[user experience]]></category><category><![CDATA[Sass]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Fri, 11 Feb 2022 16:55:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1644514387392/psGeu6bi1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you seen the popular trend where websites use a customized cursor that is different from the standard cursor and makes the site stand out from other websites? You might even be curious about how those cursors are created.</p>
<p>That's something I've had a lot of experience with, starting when I visited the website for the creative and expressive <a target="_blank" href="https://studio-job.com">Studio Job</a>. I was really sucked in. It was unlike anything I'd ever seen before!</p>
<p>And in this article, I'll show you how to make your own custom cursors and discuss when you should use one for your next project. But, before we get started making custom cursors, let's define what they are.</p>
<h3 id="heading-why-use-a-custom-cursor">Why use a Custom Cursor?</h3>
<ul>
<li>Custom cursors are used in web design to add more personality to your website and also to give the users an awesome experience. This simple addition makes your website stand out. </li>
</ul>
<p>Consider the following scenario:</p>
<blockquote>
<p>You've been given the task of creating a modern website, such as a portfolio site with animations, parallax effects, and moving items.</p>
<p>Using a standard cursor on the design will most likely seem out of place.</p>
</blockquote>
<p>This is a great example of how a custom cursor may improve the user experience.</p>
<ul>
<li>You can also use them to direct visitors to where they should go and what they should do, providing a more engaging experience.</li>
</ul>
<p>With that in mind, we will create a custom cursor on a landing page to learn how we can maximise this feature for better UX(User Experience). </p>
<p>The image below shows a preview of the final design:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644507201980/mY_dyFdPP.gif" alt="41a1ae6e-da02-4568-aff3-66a27cfea345.gif" /></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes that you have:</p>
<ul>
<li>Knowledge of HTML </li>
<li>Knowledge of CSS (SCSS)</li>
<li>Knowledge of Javascript (ES6)</li>
<li>A code editor and browser</li>
</ul>
<h2 id="heading-how-do-custom-cursors-work">How do custom cursors work?</h2>
<p>We use a custom cursor by changing the default cursor to the image that we want. 
Like so: </p>
<pre><code class="lang-CSS"><span class="hljs-selector-tag">body</span> {
 <span class="hljs-attribute">cursor</span>: <span class="hljs-built_in">url</span>(<span class="hljs-string">'image-path.png'</span>), auto;
}
</code></pre>
<p>The next value of the property is a fallback, <code>auto</code> which sets the cursor to the default, just in case the image hasn’t loaded or if it can’t be found.  Adding this fallback is important so your website won’t be cursor-less. </p>
<blockquote>
<p>The cursor property can also be added to different elements and is not restricted to just the body. </p>
<p>For example, check out the codepen below:</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/israelmitolu/pen/YzEGxbL">https://codepen.io/israelmitolu/pen/YzEGxbL</a></div>
<h2 id="heading-creating-the-custom-cursor">Creating the Custom cursor</h2>
<p>This section has been broken down into different steps to make it easier to understand.</p>
<h3 id="heading-step-1-set-up-the-project">Step 1: Set up the Project</h3>
<p>To code along with me, you can clone or download the <a target="_blank" href="https://github.com/israelmitolu/Julia-Portfolio">landing page design</a> which is located in the starter folder. </p>
<p>To begin, start the development server in the IDE and open up your browser.</p>
<p>This is the resulting layout, where the custom cursor will be placed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644575913903/0gghC2mWgK.png" alt="julia captur.PNG" /></p>
<h3 id="heading-step-2-add-the-cursor-divs">Step 2: Add the cursor divs</h3>
<p>In the <code>index.html</code>, add the following code immediately after the <code>&lt;body&gt;</code> tag</p>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"cursor-ball"</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">"cursor-outline"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The first div represents the ball cursor, while the second represents the larger cursor.</p>
<p>Next, we would style these empty divs in CSS</p>
<h3 id="heading-step-3-style-the-cursor-divs">Step 3: Style the cursor divs</h3>
<p>In the <code>scss/cursor</code> folder, inside <code>_cursor.scss</code>, we will add the styling for the cursor. </p>
<p>First, we style the two cursors. </p>
<pre><code class="lang-CSS"><span class="hljs-selector-class">.cursor-ball</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#000</span>;
}

<span class="hljs-selector-class">.cursor-outline</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">3rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">3rem</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#000</span>;
  <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease;
}
</code></pre>
<p>As we progress, you'll see the effect of the <code>transition</code>.</p>
<p>The output 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644508265884/IaFanrTAI.png" alt="Screenshot 2022-02-08 230124.png" /></p>
<p>Isn't that going to make a terrible cursor? It's all square-shaped and boxy.</p>
<p>As a result, we'll add some styles that are shared by both cursors:</p>
<pre><code class="lang-CSS"><span class="hljs-selector-class">.cursor-ball</span>,
<span class="hljs-selector-class">.cursor-outline</span> {
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">100</span>;
  <span class="hljs-attribute">pointer-events</span>: none;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate</span>(-<span class="hljs-number">50%</span>, -<span class="hljs-number">50%</span>)
}
</code></pre>
<p>The code block above contains a <code>border-radius</code> which makes the divs into circles.</p>
<p>We add a <code>position</code> of <code>absolute</code> so that we can alter the mouse cursors' <code>top</code> and <code>left</code> locations in Javascript.</p>
<p>The <code>z-index</code> of <code>100</code> makes the two cursors to be on top of any other element on the web page.</p>
<blockquote>
<p>Note: Make sure the mouse has the highest z-index in any project you implement a custom cursor so that the cursor won’t be hidden at any point in time. </p>
</blockquote>
<p>The <code>pointer-events</code> of <code>none</code> disables any pointer events on the divs, in this case, so that we can select other elements behind the cursor divs. To understand better, see the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events">documentation</a></p>
<p>The transform property moves the element to the centre. </p>
<p>Now you should see a bit of the cursor at the top-left corner of the screen.</p>
<h3 id="heading-step-4-move-the-cursor-around-the-screen">Step 4: Move the cursor around the screen</h3>
<p>Now, let's get this cursor moving!</p>
<p>In the <code>app.js</code> file, we will start by grabbing the elements we need: </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> cursorBall = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".cursor-ball"</span>);
<span class="hljs-keyword">let</span> cursorOutline = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".cursor-outline"</span>);
</code></pre>
<p>Now, let’s add an event listener to get the exact coordinates of the mouse — when the mouse moves:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"mousemove"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  cursorBall.style.top = e.pageY + <span class="hljs-string">"px"</span>;
  cursorBall.style.left = e.pageX + <span class="hljs-string">"px"</span>;

  cursorOutline.style.top = e.pageY + <span class="hljs-string">"px"</span>;
  cursorOutline.style.left = e.pageX + <span class="hljs-string">"px"</span>;
});
</code></pre>
<p>Because we styled the cursors with a <code>position</code> of <code>absolute</code>, we can now modify the <code>top</code> and <code>left</code> properties of both the <code>cursorBall</code> and <code>cursorOutline</code> dynamically using Javascript. </p>
<p><code>e.pageY</code> returns the coordinates on the Y-axis (vertical) for every event <code>e</code> of a mouse moving on the screen. </p>
<p>While <code>e.pageX</code> returns the coordinates on the X-axis (horizontal) for every event <code>e</code> of a mouse move. </p>
<p>Here’s the result 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644511428771/BtshBfxPt.gif" alt="ezgif.com-gif-maker.gif" /></p>
<p>You’d notice that there is a delay in how the bigger cursor, <code>cursorOutline</code> catches up to the smaller cursor. And that is because of the <code>transition</code> of <code>all 0.5s ease</code> that we added when styling the <code>.cursor-outline</code>.</p>
<blockquote>
<p>Side note: You can experiment with different values for the <code>animation-duration</code>. Here, we used <code>0.5s</code>, and you can even try without any transition property. </p>
</blockquote>
<p>Furthermore, to completely remove the default pointer cursor, we would add the following code into the body tag found in the stylesheet <code>cursor: none</code>;</p>
<p>This will make sure that only our custom cursor shows. </p>
<p>We can afford to do this because we're working with pure CSS, which loads and renders quickly.</p>
<p>However, if we choose an image that may not load up in time due to a network fault, the User Experience would be poor.</p>
<p>In such scenarios, add the image location <code>url</code> and the fallback of <code>auto</code>, as we discussed in an earlier section. </p>
<p>Great job so far! 😃</p>
<hr />
<p>This is how our web page should look now 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644509619563/fdHYwnzkK.gif" alt="Untitled.gif" /></p>
<p>Now, what we've done so far is fine, but I'd like to see us take it a step further by adding more functionality depending on mouse events.</p>
<h2 id="heading-more-event-listeners">More Event Listeners</h2>
<p>To make the webpage more interesting, let’s add more functionality to the cursor:</p>
<h3 id="heading-mouseup-and-mousedown">Mouseup and Mousedown</h3>
<p>In the <code>app.js</code> file, we will utilise the <code>mousedown</code> and <code>mouseup</code> event types which will listen for when the mouse button is being pushed. </p>
<pre><code class="lang-javascript"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.button === <span class="hljs-number">0</span>) {
    cursorOutline.classList.add(<span class="hljs-string">"cursor-mousedown"</span>);
  }
});

<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function">() =&gt;</span> {
  cursorOutline.classList.remove(<span class="hljs-string">"cursor-mousedown"</span>);
});
</code></pre>
<p>For the <code>mousedown</code> effect to work on only the left mouse is clicked, we use the <code>button</code> property to select what part of the mouse will be used. Check the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button">documentation</a> for more info on the <code>button</code> property. </p>
<blockquote>
<p>The code then translates to: “If the left mouse is being clicked, add the class of <code>cursor-mousedown</code> to the <code>cursorOutline</code>. </p>
<p>And on the event of <code>mouseup</code>,  when the mouse is released from being pushed, remove the <code>cursor-mousedown</code> class. </p>
</blockquote>
<p>Then, head over to the <code>scss/cursor/_cursor.scss</code> to add the styling for this new effect:</p>
<pre><code class="lang-SCSS"><span class="hljs-selector-class">.cursor-mousedown</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">1.5rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">1.5rem</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#000</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#8454f4</span>;
}
</code></pre>
<p>This is the resulting effect 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644510317953/lPPNqZ8Xk.gif" alt="fb88a0a6-1ef7-4704-8bc3-bd4dbac945b3.gif" /></p>
<h3 id="heading-mouseover-and-mouseleave">Mouseover and Mouseleave</h3>
<p>In the app.js file, to get all the social media links from the DOM, use a querySelectorAll on the parent element of the links: </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> links = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".social-links a"</span>);
</code></pre>
<p>Now, using the ES6 syntax, the <code>forEach</code> method to listen for events on each <code>social-link</code> link:</p>
<pre><code class="lang-javascript">links.forEach(<span class="hljs-function">(<span class="hljs-params">link</span>) =&gt;</span> {
  link.addEventListener(<span class="hljs-string">"mouseover"</span>, <span class="hljs-function">() =&gt;</span> {
    cursorOutline.classList.add(<span class="hljs-string">"scale-link"</span>);
    link.classList.add(<span class="hljs-string">"hovered-link"</span>);
  });
</code></pre>
<p>In the code above, </p>
<ul>
<li>We’re listening for a <code>mouseover</code> event on each of the <code>link</code>.</li>
<li>We’re adding a class of <code>scale-link</code> to the <code>cursorOutline</code> in Javascript, and later set it up in SCSS</li>
<li>We’re adding a class of <code>hovered-link</code> to the <code>link</code> to change the appearance of the individual links when the cursor hovers over them. </li>
</ul>
<p>The styling for the new classes: </p>
<pre><code class="lang-SCSS"><span class="hljs-selector-class">.scale-link</span> {
  <span class="hljs-attribute">transform</span>: scale(<span class="hljs-number">1.5</span>);
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#000</span>;
}

<span class="hljs-selector-class">.hovered-link</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
}
</code></pre>
<p>This means that when the cursor hovers over any of the <code>link</code>,</p>
<ul>
<li>the <code>cursorOutline</code> will expand/scale to 1.5 times its original size.</li>
<li>The <code>hovered-link</code> will change the text colour of the links. </li>
</ul>
<p>This is the result we have 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644510997868/M9PUbrBli.gif" alt="27da163c-071e-4703-ad98-78d9fa2f89f9.gif" /></p>
<p>The scaling works alright, but there are a few things you’ll notice:</p>
<ol>
<li>The <code>cursorOutline</code> still remains active even though the links were no longer being hovered on. </li>
<li>The cursor is no longer centred.</li>
<li>The link does not turn white. Did the <code>hovered-link</code> class not work? </li>
<li>The link goes under the mouse.</li>
</ol>
<p>These were the challenges I discovered while working on it, and you may experience them as well; here's how I dealt with them.</p>
<h4 id="heading-to-solve-issue-1">To solve issue #1</h4>
<p>This is where we introduce the <code>mouseleave</code> event which is fired when the cursor moves out of the target element. </p>
<p>Still in the <code>forEach</code> block, add the following code:</p>
<pre><code class="lang-javascript">link.addEventListener(<span class="hljs-string">"mouseleave"</span>, <span class="hljs-function">() =&gt;</span> {
    cursorOutline.classList.remove(<span class="hljs-string">"scale-link"</span>);
    link.classList.remove(<span class="hljs-string">"hovered-link"</span>);
  });
</code></pre>
<p>Basically, we just remove the <code>scale-link</code> and <code>hovered-link</code> when the mouse moves out of the <code>link</code>. </p>
<h4 id="heading-to-solve-issue-2">To solve issue #2</h4>
<p>The problem here is that the <code>cursorOutline</code> does not scale as it should.</p>
<p>We'll experiment with the <code>transfom-origin</code> property here: </p>
<pre><code class="lang-SCSS">  <span class="hljs-attribute">transform-origin</span>: <span class="hljs-number">130%</span> <span class="hljs-number">100%</span>;
</code></pre>
<p>This defines the point around which a transformation is applied. In this case, we set it to <code>130%</code> on the left and right, and <code>100%</code> on the top and bottom. </p>
<p>Check out <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin">MDN</a> to learn more about this property.</p>
<p>Here’s what we get after solving issues 1 and 2 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644511366936/e2KSMsYEu.gif" alt="ezgif.com-gif-maker (1).gif" /></p>
<blockquote>
<p><strong>Side note</strong>: 
If you want the pointer cursor to be displayed, add <code>cursor: none;</code> to the specific element(s)</p>
</blockquote>
<h4 id="heading-to-solve-issue-3">To solve issue #3</h4>
<p>The <code>hovered-link</code> does work, but if we take a look using Chrome Dev Tools, the actual source of the problem is that the link's colour takes precedence over this new class.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644511629817/ksGmF5ljJ.png" alt="Capture.PNG" /></p>
<p>To fix this, we can use the <code>!important</code> property, and it will work. But using this property isn’t advisable to use because it generally breaks the natural cascading of stylesheets.  It should be used sparingly. </p>
<p>A better option is to be more specific with the styling by using </p>
<pre><code class="lang-SCSS"><span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.hovered-link</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
}
</code></pre>
<p>To learn more about specificity and the <code>!important</code> property <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity">here</a>. </p>
<h4 id="heading-to-solve-issue-4">To solve issue #4</h4>
<p>If you recall, we gave our cursors a <code>z-index</code> of <code>100</code> so that they would be on top of every element on the page, including the links.</p>
<p>So here's a quick fix:</p>
<blockquote>
<p>Add a <code>z-index</code> of <code>100</code> to the elements to which you want to apply the effect, such as the social media links.</p>
</blockquote>
<p>Final Result 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644511828838/JrHAsK3W_.gif" alt="41a1ae6e-da02-4568-aff3-66a27cfea345.gif" /></p>
<p>Awesome! We were able to resolve the issues we were having. We’re also done adding the custom cursor, and we have explored several interactions using the mouse event listeners. 😎💪</p>
<h2 id="heading-responsiveness">Responsiveness</h2>
<p>Lastly, before we can deploy the project, we need to add a media query that prevents the custom cursor from appearing on devices without a pointing device. </p>
<p>Because not everyone uses a mouse or trackpad, forcing them to use a custom cursor is pointless, thus we must disable it:</p>
<p>In <code>scss/responsive/_responsive.scss</code>, 
 We will create a mixin that we will then <strong>include </strong> in the <code>_cursor.scss</code></p>
<pre><code class="lang-SCSS"><span class="hljs-keyword">@mixin</span> anyPointer {
  <span class="hljs-keyword">@media</span> (any-pointer: fine) {
    <span class="hljs-keyword">@content</span>;
  }
}
</code></pre>
<p>Let's include the mixin to <code>_cursor.scss</code>, where we've already applied styling for both cursors.</p>
<p>We would set the <code>display</code> to <code>none</code> by default, and using the <code>@include</code> rule, we set the <code>display</code> to <code>block</code> so that the cursor will be displayed only if the user is using a pointing device (such as mouse or trackpad):</p>
<pre><code class="lang-SCSS"><span class="hljs-selector-class">.cursor-ball</span>,
<span class="hljs-selector-class">.cursor-outline</span> {
   ...

  <span class="hljs-attribute">display</span>: none; 
  <span class="hljs-keyword">@include</span> anyPointer {
    <span class="hljs-attribute">display</span>: block; 
    <span class="hljs-attribute">pointer-events</span>: none;
  }
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Finally, a big yay! 🥳  We've created a cool custom cursor, and you've learned about custom cursors and how they can impact your website's user experience.</p>
<p>If you found this post useful (and I'm sure you did), do well to share this resource with your friends and co-workers, and follow me for more content. 
If you have a question or find an error or typo, kindly leave your feedback in the comments section.</p>
<p>Also, kindly share what you think. Do you think it's a good idea to have a custom cursor? Are there any other instances when it won't be required? Leave your response in the comments section.</p>
<h2 id="heading-inspirations-from-the-web">Inspirations from the web</h2>
<ul>
<li><a target="_blank" href="http://www.iaragrinspun.com/#/">Iara Grinspun Portfolio</a></li>
<li><a target="_blank" href="https://umami-ware.com/">Unami Ware</a></li>
<li><a target="_blank" href="https://www.mubien.com/">Mubien</a></li>
<li><a target="_blank" href="https://natedentondesign.com/">Denton Design</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to Create a Sleek Preloader Animation Using GSAP Timeline]]></title><description><![CDATA[Have you ever come across a website with a fluidly animated preloader that drew you in and grabbed your interest from the first glance? It made you want to reload the page over and over again.
Last week, while browsing the internet, I came across Eny...]]></description><link>https://blog.mitolu.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline</link><guid isPermaLink="true">https://blog.mitolu.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline</guid><category><![CDATA[GSAP]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[CSS]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Israel Oyetunji]]></dc:creator><pubDate>Tue, 25 Jan 2022 11:40:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642967235819/AmUEqn3LR.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever come across a website with a fluidly animated preloader that drew you in and grabbed your interest from the first glance? It made you want to reload the page over and over again.</p>
<p>Last week, while browsing the internet, I came across <a target="_blank" href="https://enyata.com">Enyata's</a> website, and after seeing the preloader, I was pretty impressed, so I thought, "Why not develop it out?" And that's exactly what I did. </p>
<p>In this article, I'll show you how to use the timeline feature of the Greensock Animation Platform (GSAP), and at the end, you'll be able to make this preloader.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643023907050/uNRA_4g3W9.gif" alt="eb0bf3400755c6f40dcbd3c950dc5c3e5efc9426.gif" /></p>
<p>Let's get started!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes that you have: </p>
<ul>
<li>Basic understanding of HTML and CSS </li>
</ul>
<ul>
<li>Basic understanding of Javascript</li>
</ul>
<ul>
<li>A code editor (VS Code recommended)</li>
</ul>
<ul>
<li>A web browser (Chrome or Firefox recommended)</li>
</ul>
<h2 id="heading-what-is-gsap">What is GSAP?</h2>
<p>The Greensock Animation Platform (GSAP) is arguably one of the best animation libraries for the web because it enables you to animate just about anything that can be accessed with JavaScript, including DOM Elements, SVGs, generic objects, canvases, and more. </p>
<p>GSAP is also a great choice because it is:</p>
<ul>
<li><p>flexible</p>
</li>
<li><p>lightweight</p>
</li>
<li><p>fast, like really fast (estimated to be 20x faster than jQuery),</p>
</li>
<li><p>has a large and supportive community via the forums.</p>
</li>
</ul>
<p>You can learn more about this library <a target="_blank" href="https://greensock.com/why-gsap">here</a>. </p>
<h3 id="heading-tweens-and-timelines">Tweens and Timelines</h3>
<p>In GSAP, a <strong>Tween </strong>is a Javascript object that allows you to define parameters that control how animation plays out. 
Tweens works in such a way that you provide the properties in the format: </p>
<p><code>gsap.method(element, var, duration)</code> </p>
<p>A <strong>Timeline </strong> is a tool that allows you to chain together multiple animations (i.e Tweens, and other timelines by nesting ) which makes it easy to control the animations as a whole, and to precisely set their timing. A typical timeline is written like so: </p>
<p><code>let tl = gsap.timeline();</code> </p>
<p>We would be looking into the most commonly used methods to create Tweens:</p>
<ul>
<li><p>to()</p>
</li>
<li><p>from()</p>
</li>
<li><p>fromTo()</p>
</li>
</ul>
<h4 id="heading-to">to()</h4>
<p>This is the most common method in GSAP, as it allows you to define the destination values. 
Just as its name implies, it specifies where your animation is going <strong><em>to</em></strong> .</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/israelmitolu/pen/VwMoLgO">https://codepen.io/israelmitolu/pen/VwMoLgO</a></div>
<h4 id="heading-from">from()</h4>
<p>The <code>from()</code> method acts like a backwards tween, where you set where the values should start from, and then GSAP animates to the element’s current values. </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/israelmitolu/pen/WNZVQNj">https://codepen.io/israelmitolu/pen/WNZVQNj</a></div>
<h4 id="heading-fromto">fromTo()</h4>
<p>The <code>fromTo()</code> is really great because it allows us to define both the Start and End values for animations, just as shown in the demo below:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codepen.io/israelmitolu/pen/LYzwppK">https://codepen.io/israelmitolu/pen/LYzwppK</a></div>
<p>There are many other methods listed in the <a target="_blank" href="https://greensock.com/docs/v3/GSAP">documentation</a>, but we would be working with these three. </p>
<p>Now that we've learned the basics of Tweens and Timelines, let's build out the preloader!</p>
<h2 id="heading-building-out-the-preloader">Building out the preloader</h2>
<p>Yay! The time we've all been waiting for is here. In this section, we will build a very simple landing page with HTML and CSS, build the preloader, and use the GSAP timeline to animate the elements.</p>
<p>This section has been broken into different steps to make it easier to understand. </p>
<h3 id="heading-step-1-install-gsap">Step 1: Install GSAP</h3>
<p>First, to use GSAP in a project, we would have to install it. Luckily, there are different methods to do so. </p>
<h4 id="heading-using-the-cdn">Using the CDN</h4>
<p>You can easily set up GSAP into your project by adding the following to your HTML file:</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://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-installing-via-package-managers">Installing via Package managers</h4>
<pre><code class="lang-javascript"><span class="hljs-comment">//Using npm</span>
npm install gsap

<span class="hljs-comment">//or with yarn</span>
yarn add gsap
</code></pre>
<p>Now that it's installed, then import it into your project</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { gsap } <span class="hljs-keyword">from</span> <span class="hljs-string">"gsap"</span>;
</code></pre>
<h3 id="heading-step-2-creating-the-landing-page">Step 2: Creating the landing page</h3>
<p>We'll start by creating an HTML page with 2 divs — one, the 
<code>preloader</code> 
div, and the other will be the landing page. </p>
<p>Inside the <code>preloader</code> div, we have a div with a class <code>prl-logo</code> which is the logo on the preloader, and it contains an <code>h1</code> which would be hidden until it is defined later in the animation sequence. </p>
<p>The SVG element is the menu button in the navigation bar. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Intro --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preloader"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prl-logo"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hide"</span>&gt;</span> cruise. <span class="hljs-symbol">&amp;trade;</span> <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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"lightCyan-slider"</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">"persianGreen-slider"</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">"white-slider"</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-comment">&lt;!--Hero--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"logo"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.html"</span>&gt;</span>Cruise.<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"menu-btn"</span>
          <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
          <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
          <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span>
          <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M0 0h24v24H0z"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
            <span class="hljs-attr">d</span>=<span class="hljs-string">"M3 4h18v2H3V4zm0 7h12v2H3v-2zm0 7h18v2H3v-2z"</span>
            <span class="hljs-attr">fill</span>=<span class="hljs-string">"rgba(255,255,255,1)"</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hero-content"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Lets go on an adventure.<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>
          &gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorem
          culpa vero quae perferendis molestiae exercitationem nemo atque
          veritatis ratione rem dolore quibusdam quia, a totam quidem nostrum
          iusto! Reiciendis, rem.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>
        &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Explore<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">section</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"app.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>
</code></pre>
<p>Now, for the CSS, we have some basic styling, using <code>flex</code> and <code>position</code> to manipulate the layout and positioning of the elements. </p>
<p>You can also get the <a target="_blank" href="https://fonts.google.com/share?selection.family=Abril%20Fatface%7CMontserrat">fonts I used</a> from Google Fonts — Make sure to import them either in the <code>head</code> tag of your HTML file or in your CSS file. </p>
<p>For the preloader section and the sliders, we'll give them a <code>position</code> of <code>fixed</code> so that we can set the <code>top</code> and <code>left</code> properties for its positioning, and then the <code>height</code> and <code>width</code> of <code>100%</code>, so it takes up 100% of the viewport. </p>
<p>To make the landing page responsive on mobile devices, we'll define some media queries too.</p>
<pre><code class="lang-CSS">* {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Montserrat"</span>, sans-serif;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">url</span>(home1.jpg) no-repeat center center fixed;
  <span class="hljs-attribute">-webkit-background-size</span>: cover;
  <span class="hljs-attribute">-moz-background-size</span>: cover;
  <span class="hljs-attribute">-o-background-size</span>: cover;
  <span class="hljs-attribute">background-size</span>: cover;
  <span class="hljs-attribute">position</span>: relative;
}

<span class="hljs-selector-tag">nav</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">10vh</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">5rem</span>;
}

<span class="hljs-selector-class">.logo</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Abril Fatface"</span>, cursive;
  <span class="hljs-attribute">text-decoration</span>: underline;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.3rem</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">font-weight</span>: lighter;
}

<span class="hljs-selector-class">.menu-btn</span> {
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.hero-content</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate</span>(-<span class="hljs-number">50%</span>, -<span class="hljs-number">50%</span>);
  <span class="hljs-attribute">width</span>: <span class="hljs-number">60%</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
}
<span class="hljs-selector-class">.hero-content</span> <span class="hljs-selector-tag">h2</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3rem</span>;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Abril Fatface"</span>, cursive;
  <span class="hljs-attribute">font-weight</span>: lighter;
}
<span class="hljs-selector-class">.hero-content</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Montserrat"</span>, sans-serif;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#f0f2f2</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">1.5rem</span> <span class="hljs-number">0rem</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">24px</span>;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">outline</span>: none;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1.5rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#025959</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1.5rem</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
}

<span class="hljs-comment">/* Preloader  */</span>
<span class="hljs-selector-class">.preloader</span> {
  <span class="hljs-attribute">background</span>: white;
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
}

<span class="hljs-selector-class">.prl-logo</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Abril Fatface"</span>, cursive;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.3rem</span>;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">2</span>;
}

<span class="hljs-comment">/* Sliders  */</span>
<span class="hljs-selector-class">.lightCyan-slider</span>,
<span class="hljs-selector-class">.persianGreen-slider</span>,
<span class="hljs-selector-class">.white-slider</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">100%</span>);
}
<span class="hljs-selector-class">.lightCyan-slider</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#d5f2ef</span>;
}

<span class="hljs-selector-class">.persianGreen-slider</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#03a6a6</span>;
}

<span class="hljs-selector-class">.white-slider</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#fff</span>;
}

<span class="hljs-selector-class">.hide</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#03a6a6</span>;
  <span class="hljs-attribute">font-weight</span>: lighter;
}

<span class="hljs-comment">/* Responsive media queries */</span>
<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">768px</span>) {
  <span class="hljs-selector-class">.logo</span>,
  <span class="hljs-selector-class">.prl-logo</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  }
  <span class="hljs-selector-class">.hero-content</span> <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
  }
  <span class="hljs-selector-class">.hero-content</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.8rem</span>;
  }
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">425px</span>) {
  <span class="hljs-selector-tag">nav</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">3rem</span>;
  }
}
</code></pre>
<p>The resulting layout 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642830266158/32ruzgZqMa.png" alt="screencapture-cruise-landing-netlify-app-2022-01-22-06_43_07.png" /></p>
<p>Great, we have our landing page set up, now let's dive into Javascript and build the animation timeline.  </p>
<h3 id="heading-step-3-implementing-gsap-timeline">Step 3: Implementing GSAP timeline</h3>
<p>We start by creating a timeline, which accepts the <code>defaults</code> property. This lets you set properties that will be inherited by all tweens in that timeline. </p>
<p>In this case, we're setting the <a target="_blank" href="https://greensock.com/docs/v3/Eases">ease</a> animation to <code>power1.out</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> tl = gsap.timeline({ <span class="hljs-attr">defaults</span>: { <span class="hljs-attr">ease</span>: <span class="hljs-string">"power1.out"</span> } });
</code></pre>
<p>To define a tween: </p>
<ol>
<li><p>Define the target element</p>
</li>
<li><p>Define the vars parameter </p>
</li>
<li><p>Define the position parameter(Note: this is optional)</p>
</li>
</ol>
<pre><code class="lang-javascript">tl.to(<span class="hljs-string">".lightCyan-slider"</span>, {
  <span class="hljs-attr">x</span>: <span class="hljs-string">"-10%"</span>,
  <span class="hljs-attr">duration</span>: <span class="hljs-number">1</span>,
});
</code></pre>
<p>The code above first takes the element we want to animate( the target element), which in this case is the class <code>lightCyan-slider</code>. </p>
<p>Then, we add the <code>vars</code> parameter, which contains the properties/values you want the tween to have. 
In this case, the <code>vars</code> parameter contains the <code>x</code> key which is equivalent to a <code>transform:  translateX()</code> in CSS. </p>
<p>To position the animations in the timeline, you'll need to pass the <code>position</code> parameter after the <code>vars</code> parameter. Here it is defined as <code>"-=1"</code>, and it is super helpful because it tells the timeline exactly where to insert the animation. </p>
<p>In this case, the animation runs 1 second before the end of the previous animation. </p>
<pre><code class="lang-javascript">tl.to(
  <span class="hljs-string">".persianGreen-slider"</span>,
  {
    <span class="hljs-attr">x</span>: <span class="hljs-string">"-20%"</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">1.5</span>,
  },
  <span class="hljs-string">"-=1"</span>
);
</code></pre>
<p>To learn more about the position parameter, check <a target="_blank" href="https://greensock.com/position-parameter">here</a>.</p>
<p>Now, we can define the other tweens: </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> tl = gsap.timeline({ <span class="hljs-attr">defaults</span>: { <span class="hljs-attr">ease</span>: <span class="hljs-string">"power1.out"</span> } });

tl.to(<span class="hljs-string">".lightCyan-slider"</span>, {
  <span class="hljs-attr">x</span>: <span class="hljs-string">"-10%"</span>,
  <span class="hljs-attr">duration</span>: <span class="hljs-number">1</span>,
});

tl.to(
  <span class="hljs-string">".persianGreen-slider"</span>,
  {
    <span class="hljs-attr">x</span>: <span class="hljs-string">"-20%"</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">1.5</span>,
  },
  <span class="hljs-string">"-=1"</span>
);

tl.to(
  <span class="hljs-string">".white-slider"</span>,
  {
    <span class="hljs-attr">x</span>: <span class="hljs-string">"-30%"</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">1.5</span>,
  },
  <span class="hljs-string">"-=1"</span>
);

tl.to(<span class="hljs-string">".hide"</span>, {
  <span class="hljs-attr">x</span>: <span class="hljs-string">"0%"</span>,
  <span class="hljs-attr">duration</span>: <span class="hljs-number">2</span>,
  <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
});

tl.to(<span class="hljs-string">".preloader"</span>, {
  <span class="hljs-attr">x</span>: <span class="hljs-string">"200%"</span>,
  <span class="hljs-attr">duration</span>: <span class="hljs-number">3</span>,
});

tl.fromTo(
  <span class="hljs-string">"nav"</span>,
  {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>,
  },
  {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">1</span>,
  },
  <span class="hljs-string">"-=2"</span>
);

tl.fromTo(
  <span class="hljs-string">".hero-content"</span>,
  {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">y</span>: <span class="hljs-number">-20</span>,
  },
  {
    <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">y</span>: <span class="hljs-number">0</span>,
  },
  <span class="hljs-string">"-=1.5"</span>
);
</code></pre>
<blockquote>
<p><strong>Note</strong> : It is important that you write your tweens in the order that you want the timeline to run. </p>
</blockquote>
<p>For example, in the code above, we go from:</p>
<pre><code class="lang-javascript">.lightCyan-slider --&gt; .persianGreen-slider --&gt; .white-slider --&gt; .hide --&gt; .preloader --&gt; nav --&gt;.hero-content
</code></pre>
<p>Great!, we have successfully created a timeline using GSAP that chains all the animations for our landing page, see the result!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643023907050/uNRA_4g3W9.gif" alt="eb0bf3400755c6f40dcbd3c950dc5c3e5efc9426.gif" /></p>
<p>Check out the <a target="_blank" href="https://cruise-landing.netlify.app/">live demo</a> and the <a target="_blank" href="https://github.com/israelmitolu/Sleek-animation-preloader-using-GSAP-timeline">code repository</a> on Github.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congrats!!! If you made it to the end, that means you have successfully implemented the GSAP timeline and built an awesome preloader. I hope you got a lot of value from this article. </p>
<p>I want you to know there are a lot of possibilities in using GSAP and is not limited to preloaders. Check out some of the best GSAP animation websites on <a target="_blank" href="https://www.awwwards.com/websites/gsap-animation/">awwwards</a> and on <a target="_blank" href="https://greensock.com/showcase/">Greensock Showcase</a>. </p>
<p>Also, do well to like and follow for more content, and if you've got any questions or spotted any errors... please do well to leave some feedback as this is my first technical article :)</p>
<h2 id="heading-resources-and-further-reading">Resources and Further Reading</h2>
<ul>
<li><p><a target="_blank" href="https://greensock.com/docs/">Greensock Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://greensock.com/cheatsheet/">GSAP Cheatsheet</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>