<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Teyim Asobo</title>
    <description>The latest articles on Forem by Teyim Asobo (@teyim).</description>
    <link>https://forem.com/teyim</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F475429%2Feca311a6-ca27-4993-88fa-7e3ac1c96dc2.jpg</url>
      <title>Forem: Teyim Asobo</title>
      <link>https://forem.com/teyim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/teyim"/>
    <language>en</language>
    <item>
      <title>Conquer Every User Journey: Building Robust E2E Tests with Playwright- part 1</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Wed, 20 Mar 2024 09:33:15 +0000</pubDate>
      <link>https://forem.com/teyim/conquer-every-user-journey-building-robust-e2e-tests-with-playwright-part-1-1nbg</link>
      <guid>https://forem.com/teyim/conquer-every-user-journey-building-robust-e2e-tests-with-playwright-part-1-1nbg</guid>
      <description>&lt;p&gt;In everyday software development, making sure parts of our application work as expected will involve us testing the app. This could include clicking a button, a dropdown, or an input field to make sure they behave as expected. Testing very simple applications manually can get the work done and make you sleep at night. However, manual testing becomes tedious and inefficient for very large applications.&lt;/p&gt;

&lt;p&gt;Say Hello to &lt;strong&gt;Automated testing&lt;/strong&gt;.In part 1 of this post, you will learn about what automated testing is, the types of automated tests, the tools used for automated testing, and of course the fun part... we get our hands dirty with some code examples in part 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Automation testing
&lt;/h2&gt;

&lt;p&gt;Let's assume we are new to the English language, and break down these terms :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automation&lt;/strong&gt;:This refers to using machines or software to do tasks that would normally be done by a person. Imagine a car wash that automatically cleans your car instead of someone scrubbing it by hand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt;:In the world of software development, testing involves checking if a program works as expected. We want to make sure there are no bugs or errors that prevent it from functioning properly. Think of it like testing a new recipe in the kitchen to see if it turns out delicious!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the above definitions, we can say automated testing is writing code that tests our applications for us. So we use testing tools to say "Hey you see this element, when I do this action, I expect these results". and out test is said to "pass" when the &lt;strong&gt;expected result&lt;/strong&gt; = the &lt;strong&gt;actual results&lt;/strong&gt; from interacting with those elements when the test is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Automated test
&lt;/h2&gt;

&lt;p&gt;There are primarily 3 types of testing: Unit test, Integration test, and End-to-end test (E2E). These 3 test are usually presented by the following pyramid&lt;/p&gt;

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

&lt;p&gt;This pyramid indicates complexity in writing and maintaining tests as you go up the pyramid. This indicates we should write more Unit tests which are easier to write and run faster. On the other hand, we should write a few End-to-End tests for crucial user journeys since they are more complex to write and maintain and also take more time to run.&lt;br&gt;
Let's take a closer look at each of these types of tests: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unit test&lt;/strong&gt;: Unit test involves testing individual, isolated pieces of code e.g. testing a component, utility functions, etc. In frontend applications, it usually makes sense to write unit tests for crucial business logic and heavily used utility functions. with this type of test, &lt;strong&gt;mocking&lt;/strong&gt; is used to abstract out 3rd party logic and API calls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration test&lt;/strong&gt;: Integration testing is all about checking how 2 or more of these interact and collaborate. It verifies that when you perform actions on one component (like clicking a button), the other components react as expected (like displaying a new page or updating information). While &lt;strong&gt;unit testing&lt;/strong&gt; focuses on the individual functionality of each component in isolation, integration testing brings them together to ensure they play nicely as a team.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E2E test&lt;/strong&gt;: Imagine a user starting on the homepage of your application, clicking through different pages, interacting with various components, and finally reaching a specific goal, like completing a purchase. &lt;strong&gt;E2E&lt;/strong&gt; testing mimics this entire user experience from beginning to end. &lt;strong&gt;E2E&lt;/strong&gt; testing focuses on the user's perspective. It checks if the application flows smoothly, functions as expected at each step, and ultimately delivers a satisfactory experience for the user.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Popular Tools used for Frontend Automated testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit test/Integration test&lt;/strong&gt;: React Testing Library, Jest, Vitest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E test&lt;/strong&gt;: Playwright, Cypress&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we go ahead to start writing some test, let's look at &lt;strong&gt;test doubles&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Hand-shake With Test Doubles
&lt;/h2&gt;

&lt;p&gt;Test doubles play a crucial part in automation testing as they allow us to abstract away logic like API calls, and calls to 3rd part libraries and fully focus on testing a component or a group of components in isolation.&lt;/p&gt;

&lt;p&gt;Let's take a closer look at the 3 most common test doubles and their differences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stubs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function:&lt;/strong&gt; Stubs are the simplest type of test double. They are pre-programmed to return specific, predetermined values when their methods are called.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Stubs don't care about how they are called. They simply provide a fixed response for specific inputs, allowing you to test the component's behavior under those conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use case:&lt;/strong&gt; Stubs are ideal for testing how a component reacts to specific data or interactions, without the complexity of dealing with real dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; You are testing a component that displays a welcome message based on the user's name. You can use a stub to simulate an external API that fetches user data and always return a specific name, allowing you to test the component's logic for displaying that name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Spies:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function:&lt;/strong&gt; Spies are similar to stubs but offer additional capabilities. They can record information about how they are called, such as the number of times a method was called or the arguments passed to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Spies care about how they are called and can be used to verify specific interactions occurred during the test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use case:&lt;/strong&gt; Spies are helpful when you need to ensure that certain methods of a dependency were called with the expected arguments. They allow you to assert not only the outcome of the component but also the interactions it had with its dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; You are testing a component that sends data to an analytics service. You can use a spy to track how many times the analytics service method was called and with what data, ensuring the component interacts with the service as expected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mocks:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function:&lt;/strong&gt; Mocks are the most versatile type of test double. They combine the functionalities of stubs and spies, allowing you to pre-program expected behavior and verify interactions. Additionally, Mocks can simulate more complex behaviors, such as throwing errors or returning different values under specific conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Mocks offer the most control over the behavior of the test double. You can define expectations for how they should be called and verify if those expectations are met during the test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use case:&lt;/strong&gt; Mocks are useful for complex scenarios where you need to control the behavior of a dependency in different ways and ensure the component reacts appropriately.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; You are testing a component that interacts with a database. You can use a mock to simulate different database responses (success, failure) and verify the component handles them correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the Right Double:
&lt;/h3&gt;

&lt;p&gt;The choice between stubs, spies, and mocks depends on your specific testing needs and the complexity of the dependency being replaced.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;stubs&lt;/strong&gt; for simple scenarios where you only need to control the output.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;spies&lt;/strong&gt; to verify how the component interacts with the dependency.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;mocks&lt;/strong&gt; when you need complex behavior control and verification of interactions.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Dynamic Readme image based on GitHub Activity</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Mon, 29 Jan 2024 12:38:44 +0000</pubDate>
      <link>https://forem.com/teyim/dynamic-readme-image-based-on-github-activity-2ac3</link>
      <guid>https://forem.com/teyim/dynamic-readme-image-based-on-github-activity-2ac3</guid>
      <description>&lt;p&gt;GitHub readme files are an excellent way to convey information about a project's structure, help, patches, updates, and more. But readme images can be used for more fun stuff like the GitHub profiles you see are readme files with some rich and often dynamic text and images.&lt;/p&gt;

&lt;p&gt;Now that you have an idea of what readme images are, let's look at how I created a dynamic readme image that changes based on my GitHub activity. this article looks at the technical aspects and is not a step-by-step guide on creating it. with that said, the source code is on my &lt;a href="https://github.com/teyim/Walle"&gt;GitHub&lt;/a&gt; in case you want to have a deeper dive into how things work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Idea
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KBiJP4Nf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1675590769964/97bf55c6-5653-447b-9916-a59dfb2cfe73.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBiJP4Nf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1675590769964/97bf55c6-5653-447b-9916-a59dfb2cfe73.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="project image" width="400" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My idea for this project was to create customized Github weekly stats, or some sort of fun, gamified way of displaying my GitHub activity. The aim was to encourage me to push more code to GitHub while having fun and a feeling of achievement in the whole process. When I commit code and do activities on GitHub, all of this is used to generate weekly points which increase the percentage on each level and will eventually help me clear a level. when I don't commit code, the percentage for a level drops by a given amount and I eventually drop in levels. this gave me the motivation to do more activities on GitHub, with me often being excited to see how much my percentage has advanced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l3i-yEZm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1675595860977/bec77fe6-33f9-43a6-abc8-b15e142bef61.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l3i-yEZm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1675595860977/bec77fe6-33f9-43a6-abc8-b15e142bef61.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="arhcitecture" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Nodejs&lt;/strong&gt; script that fetches commits from the last 1 week from the GitHub API(excluding Github action commits) and updates the health and current level based on that. This script then runs every 7 days using GitHub action.&lt;/p&gt;

&lt;p&gt;To generate the image, there is a template built with Tailwindcss in a Nextjs API route that uses &lt;strong&gt;_vercel-og _&lt;/strong&gt; to generate the image. it simply uses query params passed, to get the current level, health, and commits and give back the corresponding image&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;one major challenge I faced was the image failing to load or loading very late after the readme has displayed. the initial approach I took was to embed the link to the Nextjs backend directly in the readme file, hence when I fetch updated GitHub data, I just update the query params, coursing a new image to be fetched..this coursed a major problem since the image usually delays to fetch and might often fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;I fixed this by fetching and storing the image locally in the project repo and just referencing this in my readme file. this reduced the delay of waiting for my image to fetch after my readme file had been fully rendered.&lt;/p&gt;

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

&lt;p&gt;With this, I can generate my weekly Github stats as a dynamic readme image, making it a more exciting experience to commit code to GitHub. below are links to the Nextjs API route repo and the main repo which contains the majority of the logic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/teyim/Walle"&gt;Walle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/teyim/Facile"&gt;Facile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>github</category>
      <category>node</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>A Deep dive into CSR, SSR, SSG and ISR</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Mon, 29 Jan 2024 12:21:27 +0000</pubDate>
      <link>https://forem.com/teyim/a-deep-dive-into-csr-ssr-ssg-and-isr-3513</link>
      <guid>https://forem.com/teyim/a-deep-dive-into-csr-ssr-ssg-and-isr-3513</guid>
      <description>&lt;p&gt;The evolution of the web has given rise to many innovative ways through which servers render and deliver web pages to the browser. some of which include &lt;strong&gt;server-side rendering(SSR)&lt;/strong&gt;, &lt;strong&gt;client-side rendering(CSR)&lt;/strong&gt;, &lt;strong&gt;static site generation(SSG)&lt;/strong&gt;, and &lt;strong&gt;incremental Static regeneration/revalidation(SSR)&lt;/strong&gt;. While most of these techniques have brought immense improvement in website load/delivery speed, they also have their use cases, pros, and cons. In this article, we dive a little bit deeper, taking a behind-the-scenes look at how these techniques work and when to use them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hi, I am a senior frontend Engineer currently in search of freelance work (&lt;a href="https://www.upwork.com/freelancers/~01ce6f5158288fda46" rel="noopener noreferrer"&gt;upwork&lt;/a&gt;), and full-time roles (&lt;a href="https://github.com/teyim" rel="noopener noreferrer"&gt;Github&lt;/a&gt;), Will appreciate any referrals or opportunities you bring my way. Thanks&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-side rendering (CSR)
&lt;/h2&gt;

&lt;p&gt;With client-side rendering, when a request is made to the server, the server responds with a blank HTML file and a javascript bundle. the javascript now builds the &lt;strong&gt;Dom&lt;/strong&gt;(HTML elements) from scratch based on some virtual Dom(React virtual Dom) in a case where the app is built with React. hence depending on the bundle size returned to the browser, the user usually will see a blank screen for some seconds before the website information is then displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  steps involved in client-side rendering
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The user makes a request to the server&lt;/li&gt;
&lt;li&gt;The server returns a blank HTML page&lt;/li&gt;
&lt;li&gt;The browser fetches and downloads the JS bundle&lt;/li&gt;
&lt;li&gt;JS constructs HTML UI and makes it interactive(attaches necessary event handlers)&lt;/li&gt;
&lt;li&gt;Any request to 3rd party APIs are made&lt;/li&gt;
&lt;li&gt;Users can now fully interact with the web page&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  When to use client-side rendering
&lt;/h3&gt;

&lt;p&gt;Client-side rendering is best used in web applications with large complex features and pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reduced server-side workload&lt;/li&gt;
&lt;li&gt;Reduced server cost&lt;/li&gt;
&lt;li&gt;A better client experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Poor SEO, since the initial page sent to the browser, is empty, web crawlers can't index the content of the page&lt;/li&gt;
&lt;li&gt;Slow initial load since the dom is being constructed on the client side.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Server-side rendering(SSR)
&lt;/h2&gt;

&lt;p&gt;With Server-side rendering, when a request is made to the server for a page, the server fetches relevant data for that page from the backend, constructs the full HTML page, and sends it back to the browser which will now become visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: even if javascript is disabled in our browser, we still see the full page, but can't interact with it.&lt;/p&gt;

&lt;p&gt;Hence with server-side rendering, we construct the full HTML page on the server and return it to the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use server-side rendering
&lt;/h3&gt;

&lt;p&gt;SSR can be used on websites in which &lt;strong&gt;SEO&lt;/strong&gt; is crucial. or in situations where each user has unique content shown on the initial load&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easy indexing by search engines&lt;/li&gt;
&lt;li&gt;Fast load times especially in case of a slow connection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;High server load, since the server has to build the page on every request&lt;/li&gt;
&lt;li&gt;Slower transition between pages in the application&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Static site generation(SSG)
&lt;/h3&gt;

&lt;p&gt;with &lt;strong&gt;SSG&lt;/strong&gt;, the server builds our website page together with its static assets once and serves it on a CDN(content delivery network). any subsequent request for a web page is served from the CDN. In SSG, pages are usually generated during application build time&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use server-side generation
&lt;/h3&gt;

&lt;p&gt;SSG can be used for websites whose content does change very often e.g blogs&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;High-performance thanks to CDN caching&lt;/li&gt;
&lt;li&gt;The static HTML page will always be served even if the backend is down&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The content of the page served by the CDN can easily get outdated in case of rapidly changing content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Incremental static regeneration(ISR)
&lt;/h2&gt;

&lt;p&gt;This concept is mostly popular with &lt;strong&gt;Nextjs&lt;/strong&gt;. &lt;strong&gt;ISR&lt;/strong&gt; is very similar to &lt;strong&gt;SSG&lt;/strong&gt;, they work basically the same, but &lt;strong&gt;ISR&lt;/strong&gt; goes an extra step. After the static page is served on the &lt;strong&gt;CDN&lt;/strong&gt;, and after some interval, the page on the &lt;strong&gt;CDN&lt;/strong&gt; is then invalidated and a new version of the page is regenerated with fresh data. with this, we can use &lt;strong&gt;SSG&lt;/strong&gt; with data that is frequently changing.&lt;/p&gt;

&lt;p&gt;when to use incremental static regeneration&lt;br&gt;
we can use &lt;strong&gt;ISR&lt;/strong&gt; with websites where we want to get fresh updates delivered to the customer per page basis without having to redeploy the site and with the benefits of &lt;strong&gt;SSG&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Less server load&lt;/li&gt;
&lt;li&gt;Content delivery is very fast&lt;/li&gt;
&lt;li&gt;Rebuilding/deploying the website to display fresh content is not needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Might need extra configuring steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These page rendering techniques are used in different situations and have several pros and cons. Most modern applications will often make use of more than one e.g SSG + CSR or ISR + CSR.combining these techniques provide an amazing way of getting the best of both worlds, be it website performance or &lt;strong&gt;SEO&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>codenewbie</category>
      <category>programming</category>
    </item>
    <item>
      <title>Vite js: The Lightweight and Lightning-fast Build Tool for Your Next Web Project</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Mon, 29 Jan 2024 12:02:30 +0000</pubDate>
      <link>https://forem.com/teyim/vite-js-the-lightweight-and-lightning-fast-build-tool-for-your-next-web-project-541i</link>
      <guid>https://forem.com/teyim/vite-js-the-lightweight-and-lightning-fast-build-tool-for-your-next-web-project-541i</guid>
      <description>&lt;p&gt;Vitejs is a fantastic build tool, that aims at providing a faster and better development experience for web projects. This article aims at taking an in-depth look at what Vitejs is, how it works, and its benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Vitejs
&lt;/h2&gt;

&lt;p&gt;Vite js is a fantastic frontend build tool that boosts the frontend development experience by providing :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast server start times by serving files to the browser via native ECMAScript modules (ESM).&lt;/li&gt;
&lt;li&gt;Optimized builds using Rollup and Esbuild for generating static assets for productions&lt;/li&gt;
&lt;li&gt;Super fast hot module replacement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we look into detail how Vitejs accomplishes these, let's have a look at what modules are in Javascript and why exactly we need a module bundler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Modules
&lt;/h2&gt;

&lt;p&gt;Most often, when building small web pages, putting all our functions in one javascript file, let's say &lt;strong&gt;&lt;em&gt;main.js&lt;/em&gt;&lt;/strong&gt; would be fine. This is not the case when building larger javascript applications where separation of concern is key. we need a way to separate different files based on the functionality of what they do. This is where Javascript modules come in. A module is simply a file that carries out specific functionality and allows you to share its functionality with other parts of your application. To allow a module to be used elsewhere in our application, we use the export statement, and to use that module in another file, we use the import statement. the most common module systems are &lt;strong&gt;ECMAScript modules(ESM)&lt;/strong&gt; and &lt;strong&gt;CommonJs(CJS)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the browser fetch and evaluate modules?
&lt;/h2&gt;

&lt;p&gt;The web browser fetches and evaluates imported modules via &lt;strong&gt;HTTP&lt;/strong&gt; get request. it also fetches and evaluates the module's imports when needed. let's see an example.&lt;/p&gt;

&lt;p&gt;Below is a simple javascript application that uses modules:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="nx"&gt;DOCTYPE&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/head&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/html&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;we import our &lt;strong&gt;&lt;em&gt;main.js&lt;/em&gt;&lt;/strong&gt; file here, which simply imports a constant from the &lt;strong&gt;&lt;em&gt;hello.js&lt;/em&gt;&lt;/strong&gt; module and sets that as the &lt;strong&gt;&lt;em&gt;H1&lt;/em&gt;&lt;/strong&gt; text.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hello&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./hello.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and our &lt;strong&gt;&lt;em&gt;hello.js&lt;/em&gt;&lt;/strong&gt; is a simple module that exports a constant&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;now let's head to our web browser and see how it works.&lt;/p&gt;

&lt;p&gt;When we open the webpage, we see a &lt;strong&gt;Hello&lt;/strong&gt; message, which is expected. now let's look at our network tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1677403206791%2F89a884b0-133b-4604-9eb8-ca2c49972c99.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1677403206791%2F89a884b0-133b-4604-9eb8-ca2c49972c99.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="network tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 2 files that are of interest to us here are the &lt;strong&gt;&lt;em&gt;main.js&lt;/em&gt;&lt;/strong&gt; file and &lt;strong&gt;&lt;em&gt;hello.js&lt;/em&gt;&lt;/strong&gt; file which has been fetched by our browser. But hold on a minute, why are we having a &lt;strong&gt;304&lt;/strong&gt; status code? well here is the thing, when the browser first makes a request for a module, the browser gets back a status code &lt;strong&gt;200&lt;/strong&gt; which is fine.. on subsequent requests, if the requested module has not changed, we get a &lt;strong&gt;304&lt;/strong&gt; response status code which means not modified.&lt;/p&gt;

&lt;p&gt;Note: This concept will serve as a good foundation for understanding how &lt;strong&gt;Vitejs&lt;/strong&gt; serves our source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding module bundlers
&lt;/h2&gt;

&lt;p&gt;Module bundlers bundle multiple js files into a single file, that can be downloaded and executed by the browser. Not only javascript files but also bundling images and CSS files into static assets. Now imagine you are building a very large application, and you have multiple modules and also utilize multiple 3rd party libraries. You can see how inefficient this will quickly become in terms of managing all these dependencies, and also for N number of modules you use in your application, your browser will have to make N HTTP requests for those various modules, which is not very ideal. A module bundler will get an entry point, in our case, &lt;strong&gt;&lt;em&gt;main.js&lt;/em&gt;&lt;/strong&gt; generate a dependency graph by looking through the dependencies of its dependencies, and bundle that into one javascript file our browser can execute.&lt;/p&gt;

&lt;p&gt;Module bundlers also provide functionality like dev servers, Hot module replacement, and more. Examples of some module bundlers are &lt;strong&gt;Webpack&lt;/strong&gt;, &lt;strong&gt;Rollup&lt;/strong&gt;, &lt;strong&gt;Parcel&lt;/strong&gt;, and &lt;strong&gt;Vitejs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We have gotten an idea of how JavaScript modules work, and we have also seen what module bundlers are and how they work. Now that the basic foundation is laid, let's move to understand how Vitejs operates and how it improves your development experience&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Vitejs
&lt;/h2&gt;

&lt;p&gt;With traditional bundlers, they build and serve files during development and create optimized builds for production when needed. This is fine and is what most devs have been comfortable with for years, but the benefits of building files during development and production weigh more on the production side and could be very uncomfortable to deal with during development. Other Bundlers often face the following problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Slow server start in large apps since app modules/components need to be built and served&lt;/li&gt;
&lt;li&gt;Slow updates to reflect changes since the whole application has to be rebuilt and served&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vite js provides the best of both worlds(optimization for development and production) by serving our files/components via native ES modules during development and creating static assets for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Vitejs solves slow server start time
&lt;/h2&gt;

&lt;p&gt;With other module bundlers like &lt;strong&gt;Webpack&lt;/strong&gt;, during server start, the module bundler traverses the dependencies of dependencies(generates dependency graph), creates a bundle, and then serves this bundle. This is the typical behavior with applications with no code splitting, hence components that are not currently being displayed or not necessary are also bundled, causing very large applications with many modules/components to take some time to be bundled and served.&lt;/p&gt;

&lt;p&gt;Vite js solves this by dividing our application modules into 2 categories: dependencies, and source code. The dependencies are 3rd party libraries that don't change during development and are quite large and expensive to process. Vitejs prebuilds these dependencies using &lt;strong&gt;ESbuild&lt;/strong&gt;, a module bundler written in &lt;strong&gt;Go&lt;/strong&gt; which is &lt;strong&gt;10x&lt;/strong&gt; faster than other javascript-based module bundlers. The source code is non-plain components/files that need to be transformed to plain JS (JSX components and CSS ) and are often edited and changed during the development process. So in summary, during the initial server start, only our main entry file and its dependencies are transformed to Js and served to our browser via native ESM.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Vitejs solves slow updates
&lt;/h2&gt;

&lt;p&gt;Most modern module bundlers support &lt;strong&gt;Hot Module Replacement(HMR)&lt;/strong&gt;, which is simply updating just the edited module and reflecting its changes in the browser without rebuilding the entire application and causing the browser to reload which will lead to the application state being lost. But even &lt;strong&gt;HMR&lt;/strong&gt; could be slow for most very large applications, hence they might be a slight delay for changes to be reflected. Vitejs solve this by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performing HMR via native Es module&lt;/strong&gt;: when a file is updated, a single &lt;strong&gt;HTTP&lt;/strong&gt; request is made to download just the updated file/module with the help of &lt;strong&gt;304&lt;/strong&gt; not modified status code as we saw earlier, making updates consistently fast regardless of the size of your application. &lt;/li&gt;
&lt;li&gt;Vitejs Caches dependencies are initially pre-built with &lt;strong&gt;ESbuild&lt;/strong&gt; in our browser, hence preventing unnecessary dev-server requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bundling for production
&lt;/h2&gt;

&lt;p&gt;Vite js uses &lt;strong&gt;Rollup&lt;/strong&gt; under the hood to create optimized production-ready static assets. Hence Vitejs uses ESbuild to pre-build dependencies during development and Rollup for production builds.&lt;/p&gt;

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

&lt;p&gt;Vite js is an amazing tool, which will help to improve your development experience and also create optimized production builds. Vitejs should be the go-to bundler for your next front-end dev project.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Create reusable button Components with React,Typescript , Tailwind and Tailwind-variants 2025</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Mon, 29 Jan 2024 11:34:14 +0000</pubDate>
      <link>https://forem.com/teyim/create-reusable-button-components-with-reacttypescript-tailwind-and-tailwind-variants-2j7d</link>
      <guid>https://forem.com/teyim/create-reusable-button-components-with-reacttypescript-tailwind-and-tailwind-variants-2j7d</guid>
      <description>&lt;p&gt;So, You write a few components here and there and have written some button components of your own. But then your button components are not flexible and reusable enough, and you end up rewriting a lot of logic and styles within your react components trying to make it fit different cases in different parts of your application,&lt;/p&gt;

&lt;p&gt;Well, you are in the right place. This article serves as an easy-to-follow guide on how to create a reusable and flexible button component using &lt;strong&gt;React, Typescript, Tailwind CSS&lt;/strong&gt;, and the &lt;strong&gt;Tailwind-variant&lt;/strong&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hi, I am a senior frontend Engineer currently in search of freelance work (&lt;a href="https://www.upwork.com/freelancers/~01ce6f5158288fda46" rel="noopener noreferrer"&gt;upwork&lt;/a&gt;), and full-time roles (&lt;a href="https://github.com/teyim" rel="noopener noreferrer"&gt;Github&lt;/a&gt;), Will appreciate any referrals or opportunities you bring my way. Thanks&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;As mentioned above, we will be using React, Typecript, and Tailwind CSS when building our components, so basic knowledge of creating components using these will help you better understand how things work together. of course, if u have not used Tailwind CSS before, you are welcome to follow along as Tailwind is straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Now let's set things up so we can start building our component. we will start by creating a simple React + Typescript + Tailwind CSS project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a simple React project with Vite:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts to finish the setup process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup Tailwind in the project:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Visit the Tailwind website and follow the simple installation guide &lt;a href="https://tailwindcss.com/docs/guides/create-react-app" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install tailwind-variants:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install tailwind-variants
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tailwind Variants&lt;/strong&gt; is a first-class variant API library for Tailwind CSS. it helps us create variants of UI components that could be based on a design system easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Button Styles
&lt;/h2&gt;

&lt;p&gt;Now that we are done setting everything up, we can go ahead and start setting the base styles for the button component. Create a components folder, in the src folder of your application. Add a button folder and add 2 files in this folder: a &lt;em&gt;Button.tsx&lt;/em&gt; file which is our main button component and a &lt;em&gt;ButtonStyles.ts&lt;/em&gt; file which will hold all our button style variants. You should end up with something like this:&lt;/p&gt;

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

&lt;p&gt;Great. now let's dive into the &lt;em&gt;ButtonStyles.ts&lt;/em&gt; components and add some basic button styles.&lt;/p&gt;

&lt;p&gt;We are going to create a &lt;em&gt;&lt;strong&gt;baseButton&lt;/strong&gt;&lt;/em&gt; variant, this variant will contain all the base styles that every button should have. We do this by importing &lt;em&gt;&lt;strong&gt;tv&lt;/strong&gt;&lt;/em&gt; from tailwind-variants, passing it an object containing our styles, and assigning that to the &lt;em&gt;&lt;strong&gt;baseButton&lt;/strong&gt;&lt;/em&gt; constant&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tv&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwind-variants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-center relative font-semibold whitespace-nowrap align-middle outline-none inline-flex items-center justify-center select-none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-xs py-1 px-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-sm py-2 px-4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-base py-3 px-6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;xl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-lg py-4 px-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;xxl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-xl py-5 px-10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;square_xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-xs h-4 w-4 p-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;square_sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-sm h-6 w-6 p-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;square_md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-base h-8 w-8 p-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;square_lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-lg h-10 w-10 p-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;square_xl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-xl h-12 w-12 p-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;vPadding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;py-[4px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;py-[8px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;py-[12px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;py-[16px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;vSpace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;HSpace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mx-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mx-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mx-4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mx-6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mx-auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ml-auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mr-auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb-auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mt-auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;none&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded-none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded-[2px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded-[4px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded-[8px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded-[12px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;full&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rounded-full&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;w-full&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice!!, we now have some base styles for our buttons. let's go ahead and create a solid button variant with 4 colors: &lt;strong&gt;Green&lt;/strong&gt;, &lt;strong&gt;Teal&lt;/strong&gt;, &lt;strong&gt;Yellow&lt;/strong&gt;, and &lt;strong&gt;Gray&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// create solid button styles&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solidButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-[#58cc02] text-gray-100 shadow active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;teal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-[#0D9488] text-gray-100 shadow-teal active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-[#FFC700] text-gray-100 shadow-yellow active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-[#64748B] text-gray-100 shadow-blueGray active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We extend all the variants and styles from the base button and then add custom variants that will apply to solid buttons. this means a solid button will have a color, and size, can be rounded, and have all the other properties from the &lt;strong&gt;&lt;em&gt;baseButton&lt;/em&gt;&lt;/strong&gt;. For the custom shadow, I added new box-shadow variables in my tailwind config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.{js,ts,jsx,tsx}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 3px 0 #57a300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;teal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 3px 0 #0F766E&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 3px 0 #E49E00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;blueGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 3px 0 #475569&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;none&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when creating button components, there are several things we need to in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want our button to extend the attributes of the native HTML button tag&lt;/li&gt;
&lt;li&gt;We want the variant styles to be applied to our button&lt;/li&gt;
&lt;li&gt;We might want to be able to override button variant styles with custom styles via the &lt;strong&gt;&lt;em&gt;className&lt;/em&gt;&lt;/strong&gt; attribute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all these in mind, our Button component will look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* eslint-disable react/jsx-props-no-spreading */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;forwardRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;VariantProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tailwind-variants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TbLoader&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-icons/tb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;outlineButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;solidButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;ghostButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ButtonStyles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// define all the button attributes&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// define the ref type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// extend the base button attributes&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonAttributes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;VariantProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;solidButton&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;outlineButton&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ghostButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;forwardRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// destructure neccesary props&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// determine icon placement&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;newIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iconPlacement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;newIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TbLoader&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;animate-spin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;newIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;iconPlacement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderButtonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;solidButton&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;renderButtonVariant&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
      &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/** render icon before */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;iconPlacement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`inline-flex shrink-0 self-center &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mr-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/** hide button text during loading state */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/** render icon after */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;iconPlacement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`inline-flex shrink-0 self-center  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ml-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// set default props&lt;/span&gt;
&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;buttonVariant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that's a lot of code, but don't worry, let's get a closer look at some parts of this Button component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Button Props
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// define all the button attributes&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// extend the base button attributes&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonAttributes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;VariantProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;solidButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we define the basic props of the component. our &lt;strong&gt;&lt;em&gt;ButtonProps&lt;/em&gt;&lt;/strong&gt; is an interface that extends all the attributes of a button e.g. &lt;strong&gt;&lt;em&gt;onClick&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;ref&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;type&lt;/em&gt;&lt;/strong&gt;, and more. taking a closer look at this we see the &lt;strong&gt;&lt;em&gt;buttonStyle&lt;/em&gt;&lt;/strong&gt; prop and &lt;strong&gt;&lt;em&gt;buttonVariant&lt;/em&gt;&lt;/strong&gt; prop. The buttonStyle prop is used to set various styles for a button which we defined in the ButtonStyles.ts file e.g. &lt;strong&gt;size&lt;/strong&gt;, &lt;strong&gt;color&lt;/strong&gt;, &lt;strong&gt;rounded&lt;/strong&gt;, and more. Thanks to Typescript, we get amazing intellisence for this when working with our Button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rendering Icons
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// determine icon placement&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;newIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iconPlacement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;newIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TbLoader&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;animate-spin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;newIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;iconPlacement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we use the &lt;strong&gt;&lt;em&gt;useMemo&lt;/em&gt;&lt;/strong&gt; hook to run an anonymous arrow function that returns the icon passed to our button and its placement.&lt;/p&gt;

&lt;p&gt;Great!!, now we have set up our Button component and we are ready to call it in another component and render some buttons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering Button
&lt;/h2&gt;

&lt;p&gt;In my &lt;strong&gt;&lt;em&gt;App.tsx&lt;/em&gt;&lt;/strong&gt; component I import my Button component and call it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-6 space-x-4 flex-wrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;results:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Nice!! , we have rendered 4 buttons with different colors. now let's create more Button variants called an &lt;strong&gt;Outline Button&lt;/strong&gt; and &lt;strong&gt;Ghost Button&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our &lt;strong&gt;&lt;em&gt;ButtonStyles.ts&lt;/em&gt;&lt;/strong&gt; we will add these button variants&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//create outline button styles&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outlineButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-[#58cc02] text-[#58cc02] shadow active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;teal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-[#0D9488] text-[#0D9488] shadow-teal active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-[#FFC700] text-[#FFC700] shadow-yellow active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-[#64748B] text-[#64748B] shadow-blueGray active:shadow-none active:translate-y-[5px]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//create ghost button styles&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ghostButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tv&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-[#58cc02]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;teal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-[#0D9488]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-[#FFC700]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-[#64748B]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, this is very similar to our &lt;strong&gt;Solid Button&lt;/strong&gt; variant. But our styles for the various colors have changed. With this, we can make use of these new variants in our &lt;strong&gt;&lt;em&gt;buttonStyle&lt;/em&gt;&lt;/strong&gt; prop, and also modify our buttonVariant prop to have our new variants&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// extend the base button attributes&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseButtonAttributes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;leftIcon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;rightIcon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;VariantProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;solidButton&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;outlineButton&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ghostButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and our &lt;strong&gt;&lt;em&gt;renderButtonVariant&lt;/em&gt;&lt;/strong&gt; function now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderButtonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;solidButton&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;outlineButton&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ghostButton&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's render some new Button variants in our App.tsx component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-6 space-x-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-6 space-x-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;leftIcon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TbAdFilled&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;rightIcon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TbAdFilled&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;buttonVariant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;rightIcon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TbAdFilled&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;        &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;results:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9b38mna52c6x0auc79w8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9b38mna52c6x0auc79w8.png" alt="buttons" width="567" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also try out different button states like the &lt;strong&gt;loading state&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Button&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;results:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubse48n477hqgcl4uz1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubse48n477hqgcl4uz1e.png" alt="loading state" width="111" height="161"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Overriding Styles
&lt;/h2&gt;

&lt;p&gt;There might be situations where we might need to override our button styles, maybe we need that button just 2px wider or some other edge case has come up and you just need to modify a button without touching the base styles we set. We can easily do that with the &lt;strong&gt;&lt;em&gt;className&lt;/em&gt;&lt;/strong&gt; attribute&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex p-6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px-[100px]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;buttonStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
            &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the button looks like this&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wppc2mqpdcatjrjaro9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wppc2mqpdcatjrjaro9.png" alt="custom button" width="770" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Tailwind-variants&lt;/em&gt;&lt;/strong&gt; provide a flexible approach to building modern components for our Tailwind CSS with Typescript projects. This is great, especially when building design systems using Tailwind CSS.&lt;/p&gt;

&lt;p&gt;live code -&lt;a href="https://stackblitz.com/edit/vitejs-vite-gd692j?file=src%2Fcomponents%2Fbutton%2FButton.tsx" rel="noopener noreferrer"&gt;code link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Effortless Testing Setup for React with Vite, TypeScript, Jest, and React Testing Library 2025</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Sun, 28 Jan 2024 09:00:55 +0000</pubDate>
      <link>https://forem.com/teyim/effortless-testing-setup-for-react-with-vite-typescript-jest-and-react-testing-library-1c48</link>
      <guid>https://forem.com/teyim/effortless-testing-setup-for-react-with-vite-typescript-jest-and-react-testing-library-1c48</guid>
      <description>&lt;p&gt;Recently, I have been learning to write some tests in React using &lt;strong&gt;Jest&lt;/strong&gt; and &lt;strong&gt;RTL(React Testing Library)&lt;/strong&gt;, which has been a fun and eye-opening adventure so far. Setting up testing in a Nextjs app which I used to practise testing was not too complicated, and I managed to get things working. I recently wanted to start writing some tests for a project I am working on which is a React+Vitejs, and Typescript application. This proved challenging to set up, resulting in me looking through a few outdated articles here and there, pulling a hair or 2, and almost crying about why the gods of testing were against me. Thankfully, I finally resolved my errors and had my tests running smoothly.&lt;/p&gt;

&lt;p&gt;This post therefore serves as an easy-to-follow, updated guide on how to set up testing with Jest and React-testing-library in your Vite + Typescript application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hi, I am a senior frontend Engineer currently in search of freelance work (&lt;a href="https://www.upwork.com/freelancers/~01ce6f5158288fda46" rel="noopener noreferrer"&gt;upwork&lt;/a&gt;), and full-time roles (&lt;a href="https://github.com/teyim" rel="noopener noreferrer"&gt;Github&lt;/a&gt;), Will appreciate any referrals or opportunities you bring my way. Thanks&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges faced
&lt;/h2&gt;

&lt;p&gt;While setting up the test in my Vite project, the 2 major problems that are commonly faced are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuring Jest to handle SVG images&lt;/li&gt;
&lt;li&gt;Handling path aliases (absolute imports) with Jest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we solve the above problems and set up testing in our application. Without wasting much time, let's jump right in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a Vite project
&lt;/h2&gt;

&lt;p&gt;Let's get started by creating a simple Vite project. Run the following command to create a React application using Vite: &lt;code&gt;npm create vite@latest&lt;/code&gt; . Follow the instructions and select the necessary dependencies&lt;/p&gt;

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

&lt;p&gt;Now, cd into your project directory and run npm install to install all dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Jest and React testing library
&lt;/h2&gt;

&lt;p&gt;Now let's install Jest and RTL and other related dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D jest @testing-library/react ts-jest @types/jest ts-node @testing-library/jest-dom jest-environment-jsdom @testing-library/user-event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and wait for the packages to finish installing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo9e6apycla2bt2dm1gz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo9e6apycla2bt2dm1gz.png" alt="screenshot" width="800" height="108"&gt;&lt;/a&gt;&lt;br&gt;
Great, now if you open your package.json file, you should see all of these packages as dev dependencies.&lt;/p&gt;

&lt;p&gt;Now create a &lt;em&gt;jest.setup.ts&lt;/em&gt; file with the following code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "@testing-library/jest-dom";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, create a &lt;em&gt;jest.config.js&lt;/em&gt; file with the following configuration code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },
  setupFilesAfterEnv: ["&amp;lt;rootDir&amp;gt;/jest.setup.ts"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Styles and SVG's
&lt;/h2&gt;

&lt;p&gt;Now we need 2 more packages for our test to run correctly, the first package is &lt;strong&gt;identity-obj-proxy&lt;/strong&gt; which will help jest transform files with the extension .css, .less, .sass, or .scss. When a file with any of these extensions is imported into your code during testing, Jest will substitute the import with the identity-obj-proxy module. This is a common technique for mocking stylesheets in Jest tests.&lt;/p&gt;

&lt;p&gt;The second package we need is &lt;strong&gt;jest-transformer-svg&lt;/strong&gt;. When an SVG file is imported into your code during testing, Jest will substitute the import with the jest-transformer-svg module. This suggests that there might be a custom transformer (jest-transformer-svg) configured for transforming SVG files during the testing process.&lt;/p&gt;

&lt;p&gt;To install this package, run:&lt;br&gt;
&lt;code&gt;npm install -D identity-obj-proxy jest-transformer-svg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After successfully installing, modify your &lt;em&gt;jest.config.js&lt;/em&gt; file to look like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },

  moduleNameMapper: {
    "\\.(css|less|sass|scss)$": "identity-obj-proxy",
    "^.+\\.svg$": "jest-transformer-svg",
  },

  setupFilesAfterEnv: ["&amp;lt;rootDir&amp;gt;/jest.setup.ts"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great!!, we have done a lot of configuration, let's write a test and see if things work correctly. Firstly, head over to your package.json and add the following script : &lt;code&gt;"test": "jest"&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;Let's create a simple component that renders a list of users, with some tests for that component. Create a components folder in the src directory and create users folder with a &lt;em&gt;Users.tsx&lt;/em&gt; file and a &lt;em&gt;Users.spec.tsx&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;the &lt;em&gt;Users.tsx&lt;/em&gt; component looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Users = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Users&amp;lt;/h1&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;name 1&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;name 2&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Users;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test for this component in the &lt;em&gt;Users.spec.tsx&lt;/em&gt; file looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { render, screen } from "@testing-library/react";
import Users from "./Users";

describe("User", () =&amp;gt; {
  test("renders heading", async () =&amp;gt; {
    render(&amp;lt;Users /&amp;gt;);
    expect(screen.getByRole("heading", { name: "Users" })).toBeInTheDocument();
  });

  test("renders a list of users", async () =&amp;gt; {
    render(&amp;lt;Users /&amp;gt;);
    const users = await screen.findAllByRole("listitem");
    expect(users).toHaveLength(2);
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a simple component with some test setup. let's try to run our test using the &lt;code&gt;npm run test&lt;/code&gt; command.&lt;/p&gt;

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

&lt;p&gt;Our test passes..in some cases, this will fail because of the following Typescript error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39ervmfpxp8dazjfejgq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39ervmfpxp8dazjfejgq.png" alt="screenshot" width="800" height="62"&gt;&lt;/a&gt;&lt;br&gt;
We fix this by including our jest.setup.ts file in our &lt;em&gt;tsconfig.json&lt;/em&gt; file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src", "./jest.setup.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifically, we add &lt;em&gt;./jest.setup.ts&lt;/em&gt; as an array value of the include property. Our Typescript error now disappears and our test runs smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling absolute imports
&lt;/h2&gt;

&lt;p&gt;When setting up our applications, some of our applications might be making use of absolutes imports for clean and readable import statements. However, using absolute imports with Jest will need tiny adjustments, Firstly let's set up absolute import or path aliases in our Vite app and then make it work with Jest. To do that, we need to install the following package&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -D vite-tsconfig-paths&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After successful installation, we need to modify the &lt;em&gt;vite.config.ts&lt;/em&gt; file and the &lt;em&gt;tsconfig.json&lt;/em&gt; file. add the following code as part of the &lt;em&gt;&lt;strong&gt;compilerOptions&lt;/strong&gt;&lt;/em&gt; property&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//absolute import
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"]
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;em&gt;tsconfig.json&lt;/em&gt; file looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    //absolute import
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["src", "./jest.setup.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also modify our &lt;em&gt;vite.config.ts&lt;/em&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths()],
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your &lt;em&gt;jest.config.ts&lt;/em&gt; file, add &lt;code&gt;"^@/(.*)$": "/src/$1"&lt;/code&gt; to the &lt;em&gt;&lt;strong&gt;moduleNameMapper&lt;/strong&gt;&lt;/em&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },

  moduleNameMapper: {
    "\\.(css|less|sass|scss)$": "identity-obj-proxy",
    "^.+\\.svg$": "jest-transformer-svg",
    "^@/(.*)$": "&amp;lt;rootDir&amp;gt;/src/$1",
  },

  setupFilesAfterEnv: ["&amp;lt;rootDir&amp;gt;/jest.setup.ts"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing!! now you can import components like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Counter from "@/components/counter-two/Counter-two";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;using the @ path alias and use them in your test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting test coverage report
&lt;/h2&gt;

&lt;p&gt;Most often when writing tests, we need to know the test coverage. Code coverage reports provide insights into how much of your code is covered by tests. We need a command to Instruct Jest to generate code coverage reports after running the tests. Add the following command to your script in the &lt;em&gt;package.json&lt;/em&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"coverage": "npm test --coverage --watchAll --collectCoverageFrom='src/components/**/*.{ts,tsx}'",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then run &lt;code&gt;npm run coverage&lt;/code&gt; . This runs Jest in watch mode and provides the coverage report after every test run.&lt;/p&gt;

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

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

&lt;p&gt;Setting up tests in React+Vite and Typescript applications is not that straightforward, as we often face a lot of challenges while setting it up. This post serves as an updated, step-by-step, easy-to-follow guide on setting up tests in your React+Vite and Typescript applications.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/teyim/Vite-test-setup" rel="noopener noreferrer"&gt;Github link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Using Zustand to Manage State in React App</title>
      <dc:creator>Teyim Asobo</dc:creator>
      <pubDate>Wed, 07 Sep 2022 13:15:09 +0000</pubDate>
      <link>https://forem.com/teyim/using-zustand-to-manage-state-in-react-app-2iia</link>
      <guid>https://forem.com/teyim/using-zustand-to-manage-state-in-react-app-2iia</guid>
      <description>&lt;p&gt;Building modern web applications often implies our application is made up of several components, which we need to keep in sync by sharing some state between them. State management libraries like Zustand, provide an intuitive way to share state through out our applications, by providing a centralized store for our state ,solving issues like prop drilling. This article aims to guide the user on how to get started with Zustand in their react application. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Zustand
&lt;/h2&gt;

&lt;p&gt;Zustand is a small, fast and scalable state management solution that uses simplified flux principles, is based on hooks and has little to no boilerplate code. Having about &lt;strong&gt;20.6k&lt;/strong&gt; stars on github, Zustand is becoming one of the most loved state management solutions thanks to its simplicity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- small&lt;/strong&gt;&lt;br&gt;
Zustand is one of the smallest state management libraries with a bundle size of just &lt;strong&gt;1.16kb&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- fast&lt;/strong&gt;&lt;br&gt;
Zustand is fast, thanks to its small size and little boilerplate code, making us have instantaneous UI updates on changes to our state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- scalable&lt;/strong&gt;&lt;br&gt;
With Zustand, we can create a store, which is a central point of truth where all our global app state resides in. Our store is simply a hook, which we can easily import and use where needed. making Zustand very scalable.&lt;/p&gt;

&lt;p&gt;Enough talk, lets get out hands dirty by building a simple todo application and use Zustand to manage state, so you get to see how things work&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Getting started with React and Zustand
&lt;/h2&gt;

&lt;p&gt;Setting up Zustand in our react app is as easy as setting up our react app with any other npm dependency. To get things started, run the following command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npx create-react-app todo-app
cd todo-app
npm install zustand


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;or if you already have an existing react app, simply run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm install zustand
yarn add zustand


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that our state management library is all setup in our react app, we can start working with it. My &lt;em&gt;App.js&lt;/em&gt; file is very simple &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function App() {

  const inputRef = useRef();

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div style={{ justifyContent: 'center', display: 'flex', padding: '15px' }}&amp;gt;
        &amp;lt;input type={'text'} placeholder='enter user name' style={{ padding: '10px,15px' }} ref={inputRef} /&amp;gt;
        &amp;lt;button &amp;gt;Add&amp;lt;/button&amp;gt;
        &amp;lt;button &amp;gt;fetch&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px', borderTop: '1px solid black' }}&amp;gt;
        &amp;lt;ul style={{ textAlign: 'center', }}&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A Very simple UI for our todo app. An input box, 2 buttons, and an unordered list to render todo elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating and Accessing our Store
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;creating store&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our &lt;em&gt;src&lt;/em&gt; folder, we create a &lt;em&gt;store.js&lt;/em&gt; file which will contain our global state. Our &lt;em&gt;store.js&lt;/em&gt; file looks like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import create from 'zustand';

let store = () =&amp;gt; ({
    users: [{ id: 1, name: 'Teyim Asobo' }, { id: 2, name: 'Fru Brian' }],
})

export const useStore = create(store);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, we have created a simple store using the &lt;strong&gt;create&lt;/strong&gt; function imported from Zustand. Our store is a hook (&lt;strong&gt;useStore&lt;/strong&gt;), and can be accessed any where in our application via a simple import. Our store currently contains an array of users, with 2 users initialiased.&lt;/p&gt;

&lt;p&gt;Now that our store is created, lets try to access our state from our &lt;em&gt;App.js&lt;/em&gt; component.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Accessing store&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can bind our components to our state, by calling our hooks from anywhere in our applications&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


import { useStore } from './store'
import { useRef } from 'react';

function App() {
  const inputRef = useRef();
  const users = useStore(state =&amp;gt; state.users)
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div style={{ justifyContent: 'center', display: 'flex', padding: '15px' }}&amp;gt;
        &amp;lt;input type={'text'} placeholder='enter user name' style={{ padding: '10px,15px' }} ref={inputRef} /&amp;gt;
        &amp;lt;button &amp;gt;Add&amp;lt;/button&amp;gt;
        &amp;lt;button &amp;gt;fetch&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px', borderTop: '1px solid black' }}&amp;gt;
        &amp;lt;ul style={{ textAlign: 'center', }}&amp;gt;
          {users?.map(user =&amp;gt; (
            &amp;lt;li key={user.id}&amp;gt;
              {user.name}
              &amp;lt;button style={{ marginLeft: '15px' }}&amp;gt;delete&amp;lt;/button&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}

        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We have imported our &lt;strong&gt;useStore&lt;/strong&gt; hook from our store, used it to store the users state in a &lt;strong&gt;users&lt;/strong&gt; variable, which we then  render out as list of users &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Updating our State
&lt;/h2&gt;

&lt;p&gt;To be able to update our state, we will create 2 new properties in our store, &lt;strong&gt;addUser&lt;/strong&gt; and &lt;strong&gt;deleteUser&lt;/strong&gt; which we will use to update our state immutably. The &lt;em&gt;store.js&lt;/em&gt; file now looks like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import create from 'zustand';

let store = (set) =&amp;gt; ({
    users: [{ id: 1, name: 'Teyim Asobo' }, { id: 2, name: 'Fru Brian' }],
    addUser: (user) =&amp;gt; set((state) =&amp;gt; ({ users: [...state.users, user] })),
    deleteUser: (userId) =&amp;gt; set((state) =&amp;gt; ({ users: state.users.filter(user =&amp;gt; user.id !== userId) }))
})

export const useStore = create(store);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, back in our App.js component, we store the &lt;strong&gt;addUser&lt;/strong&gt; and &lt;strong&gt;deleteUser&lt;/strong&gt; store properties in variables.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import shallow from 'zustand/shallow'
import { useStore } from './store'

const { addUser, deleteUser, users} = useStore(
    (state) =&amp;gt; ({ users: state.users, addUser: state.addUser, deleteUser: state.deleteUser}),
    shallow
  )


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, we construct a single object with multiple state-picks for a cleaner and better looking code, rather than using atomic state picks. &lt;strong&gt;Shallow&lt;/strong&gt;  is a function we import from Zustand. by default, when using atomic state picks, Zustand detects changes with strict-equality (old === new). when constructing a single object with multiple picks, this is not efficient because the object will always be recreated and hence (oldObject !== newObject) therefore will not trigger a UI update. The &lt;strong&gt;Shallow&lt;/strong&gt; function tells Zustand to compare state values by moving into the object itself and comparing its keys, if any one is different, then it triggers an update. &lt;br&gt;
our &lt;em&gt;App.js&lt;/em&gt; file now looks like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function App() {
  const { addUser, deleteUser, users } = useStore(
    (state) =&amp;gt; ({ users: state.users, addUser: state.addUser, deleteUser: state.deleteUser}),
    shallow
  )
  const inputRef = useRef();

  const addUserHandler = () =&amp;gt; {
    addUser({ id: users.length + 1, name: inputRef.current.value })
    inputRef.current.value = ''

  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div style={{ justifyContent: 'center', display: 'flex', padding: '15px' }}&amp;gt;
        &amp;lt;input type={'text'} placeholder='enter user name' style={{ padding: '10px,15px' }} ref={inputRef} /&amp;gt;
        &amp;lt;button onClick={addUserHandler}&amp;gt;Add&amp;lt;/button&amp;gt;
        &amp;lt;button&amp;gt;fetch&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px', borderTop: '1px solid black' }}&amp;gt;
        &amp;lt;ul style={{ textAlign: 'center', }}&amp;gt;
          {users?.map(user =&amp;gt; (
            &amp;lt;li key={user.id}&amp;gt;
              {user.name}
              &amp;lt;button onClick={() =&amp;gt; { deleteUser(user.id) }} style={{ marginLeft: '15px' }}&amp;gt;delete&amp;lt;/button&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}

        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;addUser&lt;/strong&gt; and &lt;strong&gt;deleteUser&lt;/strong&gt; can be used as normal functions to add and remove users from state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Async actions
&lt;/h2&gt;

&lt;p&gt;Zustand makes storing async data in state very easy. To demonstrate this, we are going to use &lt;a href="//mockapi.io"&gt;mockapi.io&lt;/a&gt; to create some dummy data which we can access via an API which &lt;a href="//mockapi.io"&gt;mockapi.io&lt;/a&gt; provides.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

let store = (set) =&amp;gt; ({
    users: [{ id: 1, name: 'Teyim Asobo' }, { id: 2, name: 'Fru Brian' }],
    addUser: (user) =&amp;gt; set((state) =&amp;gt; ({ users: [...state.users, user] })),
    deleteUser: (userId) =&amp;gt; set((state) =&amp;gt; ({ users: state.users.filter(user =&amp;gt; user.id !== userId) })),
    fetchUser: async (endpoint) =&amp;gt; {
        const reponse = await fetch(endpoint)
        const userData = await reponse.json()
        set({ users: userData })
    }
})


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We recieve an api endpoint via the fetchUser property, fetch the data from the endpoint, convert this data to JSON and then use the &lt;strong&gt;set&lt;/strong&gt; function to set this data to our state.&lt;br&gt;
We can then call and use &lt;strong&gt;fetchUser&lt;/strong&gt; in our &lt;em&gt;App.js&lt;/em&gt; component.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function App() {
  const { addUser, deleteUser, users, fetchUser } = useStore(
    (state) =&amp;gt; ({ users: state.users, addUser: state.addUser, deleteUser: state.deleteUser, fetchUser: state.fetchUser }),
    shallow
  )
  const inputRef = useRef();

  const addUserHandler = () =&amp;gt; {
    addUser({ id: users.length + 1, name: inputRef.current.value })
    inputRef.current.value = ''

  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div style={{ justifyContent: 'center', display: 'flex', padding: '15px' }}&amp;gt;
        &amp;lt;input type={'text'} placeholder='enter user name' style={{ padding: '10px,15px' }} ref={inputRef} /&amp;gt;
        &amp;lt;button onClick={addUserHandler}&amp;gt;Add&amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; fetchUser('https://62fa5e3affd7197707eb05e4.mockapi.io/users')}&amp;gt;fetch&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div style={{ display: 'flex', justifyContent: 'center', marginTop: '20px', borderTop: '1px solid black' }}&amp;gt;
        &amp;lt;ul style={{ textAlign: 'center', }}&amp;gt;
          {users?.map(user =&amp;gt; (
            &amp;lt;li key={user.id}&amp;gt;
              {user.name}
              &amp;lt;button onClick={() =&amp;gt; { deleteUser(user.id) }} style={{ marginLeft: '15px' }}&amp;gt;delete&amp;lt;/button&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}

        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The API endpoint gives us a list of 50 users, which we then store in our state. The output looks like this&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Persisting data and using Devtools
&lt;/h2&gt;

&lt;p&gt;Zustand provides easy to use middlewares, which we can use to persist data, and to track state via redux devtools. by default, Zustand persist data using &lt;strong&gt;localstorage&lt;/strong&gt; hence state is not lost after page refresh. A common use case may be when dealing with Auth state, or a simple cart functionality in your E-commerce application where the user still sees a list of cart items even after re-visting the site. To persist our app state, we import the persist middleware from Zustand&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import { persist} from 'zustand/middleware'

let store = (set) =&amp;gt; ({
    users: [],
    addUser: (user) =&amp;gt; set((state) =&amp;gt; ({ users: [...state.users, user] })),
    deleteUser: (userId) =&amp;gt; set((state) =&amp;gt; ({ users: state.users.filter(user =&amp;gt; user.id !== userId) })),
    fetchUser: async (endpoint) =&amp;gt; {
        const reponse = await fetch(endpoint)
        const userData = await reponse.json()
        set({ users: userData })
    }
})

store = persist(store, { name: 'user' })
export const useStore = create(store);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When we now click on the fetch button to fetch a list of users from our API endpoint which is then stored in state, our state is persisted in &lt;strong&gt;local storage&lt;/strong&gt; with key &lt;em&gt;users&lt;/em&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Using Devtools
&lt;/h2&gt;

&lt;p&gt;Zustand provides a middleware called &lt;em&gt;devtools&lt;/em&gt;, which we can import and use to track our state changes using the &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en" rel="noopener noreferrer"&gt;Redux Dev tools&lt;/a&gt; browser extension. you might be familiar with this if you have worked with Redux before. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

store = persist(store, { name: 'users' })
store = devtools(store);
export const useStore = create(store);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Above, we have persisted our store in local storage and passed our store to our Devtools middleware. We can now monitor and track our state in the browser.&lt;/p&gt;

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

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

&lt;p&gt;Zustand is a state management library that helps developers to manage application state, with its strong holds being it having a small size and easy to setup. Relative to other libraries like Redux, Zustand helps developers manage complex application state using hooks. After reading this guide, you should be able to :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup Zustand in your React application&lt;/li&gt;
&lt;li&gt;Create a store&lt;/li&gt;
&lt;li&gt;Access and modify state in store&lt;/li&gt;
&lt;li&gt;Store data in state via Async request&lt;/li&gt;
&lt;li&gt;Persist state in local storage and use Redux dev tools to track and monitor state.&lt;/li&gt;
&lt;/ul&gt;

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