<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ vite - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ vite - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 10:49:34 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/vite/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build Micro Frontends in React with Vite and Module Federation ]]>
                </title>
                <description>
                    <![CDATA[ Micro Frontend Architecture has become increasingly popular in recent years, as teams look to re-use parts of their existing applications in new projects rather than rebuilding everything from scratch. Micro frontends also allow large teams to share ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-micro-frontends-in-react-with-vite-and-module-federation/</link>
                <guid isPermaLink="false">68ae1d943a7c9745e83e7797</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ module federation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microfrontend ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 26 Aug 2025 20:48:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756231755011/283b9e67-9a09-4241-9d90-701cb075084d.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Micro Frontend Architecture has become increasingly popular in recent years, as teams look to re-use parts of their existing applications in new projects rather than rebuilding everything from scratch.</p>
<p>Micro frontends also allow large teams to share common components – such as headers, footers, and logins – across multiple applications, ensuring consistency and keeping their projects DRY (Don’t Repeat Yourself).</p>
<p>In this article, you will learn:</p>
<ul>
<li><p>How to set up a project and folder structure that implements a Micro Frontend (MFE) Infrastructure</p>
</li>
<li><p>How to use and configure the <code>@originjs/vite-plugin-federation</code> to allow Module Federation (MF) usage with Vite projects.</p>
</li>
<li><p>How to share and consume React components between apps.</p>
</li>
<li><p>How to run and test your app with shared components locally</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-pre-requisites">Pre-Requisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-module-federation">What Is Module Federation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-module-federation">Benefits of Module Federation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-structure">Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-projects">How to Create the Projects</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-dependencies">How to Install Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-the-remote-app">How to Configure the Remote App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-vite-module-federation-plugin">The Vite Module Federation Plugin</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-constraints-on-exported-modules">Key Constraints On Exported Modules</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-the-remote-components">How to Create the Remote Components</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-consume-the-remote-components-within-the-host">How to Consume the Remote Components Within the Host</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-typescript-errors">How to Handle TypeScript Errors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-remotewrappercomponent-to-apptsx">How to Add RemoteWrapperComponent to App.tsx</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-serve-the-remote-app-and-run-your-host">How to Serve the Remote App and Run Your Host</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-true-power-of-micro-frontends">The True Power of Micro Frontends</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-pre-requisites">Pre-Requisites</h2>
<ul>
<li><p>Node installed on your machine – you can get your download of Node <a target="_blank" href="https://nodejs.org/en/download">here</a></p>
</li>
<li><p>Familiarity with JS / TS and React</p>
</li>
<li><p>Familiarity with Command Line / Terminal</p>
</li>
</ul>
<h2 id="heading-what-is-module-federation">What Is Module Federation?</h2>
<p>Before we go any further, let’s talk about Module Federation (MF).</p>
<p>Module Federation is a technique in web development that allows multiple separate builds to work together as a single application. It enables the sharing of code between different, independent applications at runtime, rather than at build time. This means a host application can dynamically load and execute code from a remote application.</p>
<p>At its core, Module Federation uses a <strong>"host"</strong> and <strong>"remote"</strong> architecture. A <strong>host</strong> application is the main app that consumes shared code / components. A <strong>remote</strong> application is the one that exposes code to be consumed.</p>
<p>The remote app specifies <strong>which</strong> parts of its code, or "modules," are available for others to use. The host then references these modules and loads them as needed.</p>
<h2 id="heading-benefits-of-module-federation">Benefits of Module Federation</h2>
<h3 id="heading-independent-deployment">Independent Deployment</h3>
<p>Module Federation lets teams build and deploy their micro-frontends separately. This eliminates the need for full application redeployments, accelerating development and reducing risk.</p>
<h3 id="heading-efficient-code-sharing">Efficient Code Sharing</h3>
<p>MF provides a native way to share code and dependencies between micro-frontends. This prevents code duplication, reducing bloat and ensuring consistency.</p>
<h3 id="heading-performance-gains">Performance Gains</h3>
<p>By sharing dependencies, Module Federation reduces the overall bundle size and improves initial load times, as each micro-frontend doesn't download its own copy of common libraries.</p>
<h3 id="heading-scalability-and-maintainability">Scalability and Maintainability</h3>
<p>MF enables a scalable architecture by breaking down large applications into smaller, manageable micro-frontends. This makes the codebase easier to maintain and allows teams to work independently.</p>
<h3 id="heading-analogy">Analogy</h3>
<p>Imagine building an online store. Traditionally, you’d create the entire site – homepage, product pages, cart, user profile – as one big application. A small cart change would require rebuilding and redeploying everything.</p>
<p>With Micro Frontends and Module Federation, the shopping cart can be its own app, built and maintained by a dedicated team. The main site simply imports it, allowing the cart team to release updates independently, speeding up development and improving focus.</p>
<p>This also works for organisations with multiple sites that need a consistent look. Shared components like a header, footer, or product card can be reused across sites with different purposes, such as hiring vehicles or selling furniture, ensuring visual consistency while keeping functionality unique.</p>
<h2 id="heading-project-structure">Project Structure</h2>
<p>You will need to create two projects:</p>
<ul>
<li><p><strong>host</strong> – this will act as your host application</p>
</li>
<li><p><strong>remote</strong> – this will expose the components you want to share</p>
</li>
</ul>
<h2 id="heading-how-to-create-the-projects">How to Create the Projects</h2>
<p>Run the following commands in your terminal to create your root project folder and your two Vite projects:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># create micro-frontends directory for two vite projects, and navigate to folder</span>
mkdir micro-frontends; <span class="hljs-built_in">cd</span> micro-frontends
</code></pre>
<h3 id="heading-create-a-git-repository-optional">Create a Git Repository (Optional)</h3>
<p>Using the below command you can create a Git repository for source control.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># initiate git repo</span>
git init
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># create host application vite app</span>
npm create vite@latest host-app

<span class="hljs-comment"># once submitted the command, select React and press Enter, </span>
Select a framework:
│  ○ Vanilla
│  ○ Vue
│  ● React
│  ○ Preact
│  ○ Lit
│  ○ Svelte
│  ○ Solid
│  ○ Qwik
│  ○ Angular
│  ○ Marko
│  ○ Others

<span class="hljs-comment"># select Typescript + SWC and again press Enter</span>
Select a variant:
│  ○ TypeScript
│  ● TypeScript + SWC
│  ○ JavaScript
│  ○ JavaScript + SWC
│  ○ React Router v7 
│  ○ TanStack Router
│  ○ RedwoodSDK 
│  ○ RSC
</code></pre>
<p>Once this is done, navigate back to the root project folder (<code>micro-frontends</code>):</p>
<pre><code class="lang-bash"><span class="hljs-comment"># navigate back</span>
<span class="hljs-built_in">cd</span> ../

<span class="hljs-comment"># create remote-app </span>
npm create vite@latest remote-app

<span class="hljs-comment"># follow instructions as before to select React, Typescript + SWC</span>
</code></pre>
<p>You now have your two projects, <code>host-app</code> and <code>remote-app</code>.</p>
<h2 id="heading-how-to-install-dependencies">How to Install Dependencies</h2>
<p>Open the <code>micro-frontends</code> folder in your preferred IDE / Code Editor. In this tutorial I’ll be using VS Code.</p>
<p>Tip: You can open the current folder in VS Code via your terminal using the following command <code>code .</code> if you’ve already added <code>code</code> to you PATH.</p>
<p>Once you’ve opened VS Code, open the terminal and run the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> host-app &amp;&amp; npm install -D @originjs/vite-plugin-federation
</code></pre>
<p>and then run:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ../remote-app &amp;&amp; npm install -D @originjs/vite-plugin-federation
</code></pre>
<h3 id="heading-styling">Styling</h3>
<p>To see styling like my examples, you need to add Tailwind CSS to both your remote and host applications. You can find instructions on how to do so <a target="_blank" href="https://tailwindcss.com/docs/installation/using-vite">here</a></p>
<h2 id="heading-how-to-configure-the-remote-app">How to Configure the Remote App</h2>
<p>In order to be able to utilise modules and components from your remote applications, you need to configure your application to expose those modules, and your host app to consume them.</p>
<p>Use the following configurations in your apps to allow your components to be exposed and consumed – don’t worry about the components just yet, you’ll create them soon.</p>
<h3 id="heading-host-app-viteconfigts">Host App – <code>Vite.config.ts</code></h3>
<p>In the <code>host</code> app, open <code>vite.config.js</code> and add the following configuration:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//host - vote.config.js</span>
<span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react-swc"</span>;
<span class="hljs-keyword">import</span> federation <span class="hljs-keyword">from</span> <span class="hljs-string">"@originjs/vite-plugin-federation"</span>;


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  plugins: [
    react(),
    federation({
      name: <span class="hljs-string">"host_app"</span>,
      remotes: {
        remote_app: <span class="hljs-string">"http://localhost:5001/assets/remoteEntry.js"</span>,
      },
      shared: [<span class="hljs-string">"react"</span>, <span class="hljs-string">"react-dom"</span>],
    }),
  ],
  build: {
    modulePreload: <span class="hljs-literal">false</span>,
    target: <span class="hljs-string">"esnext"</span>,
    minify: <span class="hljs-literal">false</span>,
    cssCodeSplit: <span class="hljs-literal">false</span>,
  },
});
</code></pre>
<h3 id="heading-remote-app-viteconfigts">Remote App – <code>Vite.config.ts</code></h3>
<p>In the <code>remote</code> app, open <code>vite.config.js</code> and add the following configuration:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - vite.config.js</span>
<span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react-swc"</span>;
<span class="hljs-keyword">import</span> federation <span class="hljs-keyword">from</span> <span class="hljs-string">"@originjs/vite-plugin-federation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  plugins: [
    react(),
    federation({
      name: <span class="hljs-string">"remote_app"</span>,
      filename: <span class="hljs-string">"remoteEntry.js"</span>,
      exposes: {
        <span class="hljs-string">"./Button"</span>: <span class="hljs-string">"./src/components/Button"</span>,
        <span class="hljs-string">"./Header"</span>: <span class="hljs-string">"./src/components/Header"</span>,
      },
      shared: [<span class="hljs-string">"react"</span>, <span class="hljs-string">"react-dom"</span>],
    }),
  ],
  build: {
    modulePreload: <span class="hljs-literal">false</span>,
    target: <span class="hljs-string">"esnext"</span>,
    minify: <span class="hljs-literal">false</span>,
    cssCodeSplit: <span class="hljs-literal">false</span>,
  },
  preview: {
    port: <span class="hljs-number">5001</span>,
    strictPort: <span class="hljs-literal">true</span>,
    cors: <span class="hljs-literal">true</span>,
  },
});
</code></pre>
<h4 id="heading-explanation-of-vite-configuration">Explanation of Vite Configuration:</h4>
<p>1. <code>plugins</code>: array where you tell Vite which <strong>plugins</strong> to use when it:</p>
<ul>
<li><p>Runs your dev server</p>
</li>
<li><p>Builds your production bundle</p>
</li>
</ul>
<p>Each plugin is basically a little (or big) piece of code that hooks into Vite’s build pipeline to add extra features – for example:</p>
<ul>
<li><p>Adding React JSX/TSX support (<code>@vitejs/plugin-react</code>)</p>
</li>
<li><p>Enabling Module Federation (<code>@originjs/vite-plugin-federation</code>)</p>
</li>
</ul>
<p>2. <code>build</code>: Controls how Vite produces the production build. You don’t need to worry too much about this for the purpose of this tutorial.</p>
<p>3. <code>preview</code>: Controls how the application is served / previewed:</p>
<ul>
<li><p>Useful in microfrontend setups where a fixed port and CORS enabled are needed so other apps can fetch your remote modules.</p>
</li>
<li><p><code>strictPort: true</code> ensures predictable networking – avoids “it works on my machine” issues with random ports.</p>
</li>
</ul>
<h2 id="heading-the-vite-module-federation-plugin">The Vite Module Federation Plugin</h2>
<p>You need to configure your Vite Module Federation plugin to inform it what components are to be consumed and exposed. Let’s take a look at the configured properties:</p>
<ul>
<li><p><code>name</code>: The unique identifier for your remote application in a Module Federation setup. This is the name other applications (hosts) will use when they declare your app as a remote.</p>
</li>
<li><p><code>filename</code>: This is the name of the file that your host will load when it tries to retrieve your exposed modules.</p>
</li>
<li><p><code>exposes</code>: A mapping of public module names → local file paths. This is how you decide which parts of your code are available to be consumed remotely.</p>
</li>
</ul>
<pre><code class="lang-typescript">exposes: {
  <span class="hljs-string">"./Button"</span>: <span class="hljs-string">"./src/components/Button"</span>,
  <span class="hljs-string">"./Header"</span>: <span class="hljs-string">"./src/components/Header"</span>
}
</code></pre>
<p>The <strong>key</strong> (<code>"./Button"</code>) is the <strong>public module name</strong> – the name that other apps (the host) will use when importing the module from your remote.</p>
<h4 id="heading-how-it-works">How It Works:</h4>
<ul>
<li><p><strong>Key</strong> (<code>"./Button"</code>) is the exposed identifier other apps can request.</p>
</li>
<li><p><strong>Value</strong> (<code>"./src/components/Button"</code>) is the actual file path inside your project.</p>
</li>
</ul>
<p>For example, if your remote’s <code>name</code> is <code>"remote_app"</code>, the host can import like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"remote_app/Button"</span>;
</code></pre>
<p>Under the hood, <code>"remote_app"</code> matches the <strong>remote’s</strong> <code>name</code> in its <code>federation()</code> config and <code>Button</code> matches the <code>"./Button"</code> key in <code>exposes</code>.</p>
<ul>
<li><p><code>shared</code>: dependencies that should be <strong>shared</strong> between the host and remote. It avoids shipping duplicate copies of large libraries (like React), ensuring the host and remote use the same instance – you can read more <a target="_blank" href="https://module-federation.io/configure/shared">here</a> about the possible configurations.</p>
</li>
<li><p><code>remotes</code>: A mapping of remote app names → the URL to their <code>remoteEntry.js</code> file. This tells the host where to fetch the exposed modules at runtime.</p>
</li>
</ul>
<pre><code class="lang-typescript">remotes: { remote_app: <span class="hljs-string">"http://localhost:5001/assets/remoteEntry.js"</span> }
</code></pre>
<p>The key <code>remote_app</code> must match the <code>name</code> in the remote’s config.</p>
<p>The value is the full URL to the remote’s entry file (served in dev or deployed in prod). Remember we set <code>strictPort:true</code> earlier – this is why. We need to ensure we’re pointing at the correct domain &amp; port.</p>
<h2 id="heading-key-constraints-on-exported-modules">Key Constraints On Exported Modules</h2>
<h3 id="heading-naming-constraints-and-rules">Naming constraints and rules</h3>
<p><strong>The key in</strong> <code>exposes</code> (<code>"./Button"</code>):</p>
<ul>
<li><p>Must start with <code>./</code> (per Module Federation spec).</p>
</li>
<li><p>Must be unique within the remote.</p>
</li>
<li><p>Is case-sensitive.</p>
</li>
<li><p>This is the <strong>public module path</strong> the host will request.</p>
</li>
<li><p>Doesn’t have to match the filename, but matching is a good convention for ease of reading</p>
</li>
</ul>
<p><strong>The file you point to (</strong><code>"./src/components/Button"</code>):</p>
<ul>
<li><p>Can export default, named exports, or both.</p>
</li>
<li><p>The host can import default or named exports, same as any ES module:</p>
<pre><code class="lang-typescript">  <span class="hljs-comment">// Default export</span>
  <span class="hljs-keyword">import</span> MyButton <span class="hljs-keyword">from</span> <span class="hljs-string">'remote_app/Button'</span>;

  <span class="hljs-comment">// Named export</span>
  <span class="hljs-keyword">import</span> { SpecialButton } <span class="hljs-keyword">from</span> <span class="hljs-string">'remote_app/Button'</span>;
</code></pre>
</li>
</ul>
<p><strong>The import name:</strong></p>
<ul>
<li><p>Completely up to the host developer when importing a <strong>default</strong> export.</p>
</li>
<li><p>Must match exactly for <strong>named</strong> exports.</p>
</li>
</ul>
<h2 id="heading-how-to-create-the-remote-components">How to Create the Remote Components</h2>
<p>Ok, so you’ve created your project structure, and you’ve setup your <code>vite.config.ts</code> file to allow for exposing and consuming your shared assets. Next you will create the remote components.</p>
<h3 id="heading-button-component">Button Component</h3>
<p>Let’s say you want to create a button component which will be shared across all your host applications, as you want to keep consistency. You can do this as below:</p>
<p>Navigate to the <code>remote-app</code> folder and create a new file called <code>Button.tsx</code> in <code>src/components</code> this will ensure it matches the configured federation plugin.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - ./src/components/Button.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> ButtonProps {
  text: <span class="hljs-built_in">string</span>;
  onClick?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> Button: React.FC&lt;ButtonProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ text, onClick }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;button
      onClick={onClick}
      className=<span class="hljs-string">"px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 hover:cursor-pointer"</span>
    &gt;
      {text}
    &lt;/button&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>You now have a re-usable <code>Button</code> component which has some base styling but allows for configuration of what the button does by passing in a <code>onClick()</code> argument.</p>
<h3 id="heading-header-component">Header Component</h3>
<p>Sticking with the theme of consistency, you want to create a <code>&lt;header/&gt;</code> component which you can use on all your organisation’s websites, ensuring a themed appearance on all applications.</p>
<p>Like before, create a <code>Header.tsx</code> file within <code>src/components/</code>, and paste in the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - .src/components/Header.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Header: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;header className=<span class="hljs-string">"bg-gray-800 text-white p-4"</span>&gt;
      &lt;h1 className=<span class="hljs-string">"text-2xl"</span>&gt;Remote App Header&lt;/h1&gt;
      &lt;p className=<span class="hljs-string">"text-white"</span>&gt;Hi, Grant&lt;/p&gt;
    &lt;/header&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
</code></pre>
<p>I’ve kept it simple, as this tutorial is for proof of concept purposes rather than aesthetic / real-world components.</p>
<h2 id="heading-how-to-consume-the-remote-components-within-the-host">How to Consume the Remote Components Within the Host</h2>
<p>You have your remote components created, so next you need to get them into your host application and begin using them. This is quite simple now that you’ve already setup your <code>vite.config.ts</code>.</p>
<p>You <strong>could</strong> import the components into your <code>App.tsx</code>, but this is not best practice as it can bloat your App.tsx (entry point component). I’ve opted to create a <code>RemoteWrapperComponent</code> which pulls in the remote components and handles the business logic.</p>
<p><code>RemoteComponentWrapper</code><strong>:</strong></p>
<p>Create a file called <code>RemoteComponentWrapper.tsx</code> in <code>src/components</code>, pasting the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// host - ./src/components/RemoteComponentWrapper.tsx</span>
<span class="hljs-keyword">import</span> React, { Suspense } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> RemoteHeader = React.lazy(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"remote_app/Header"</span>));
<span class="hljs-keyword">const</span> RemoteButton = React.lazy(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">"remote_app/Button"</span>));

<span class="hljs-keyword">const</span> LoadingSpinner = <span class="hljs-function">() =&gt;</span> (
  &lt;div className=<span class="hljs-string">"flex justify-center p-4"</span>&gt;
    &lt;div className=<span class="hljs-string">"animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"</span>&gt;&lt;/div&gt;
  &lt;/div&gt;
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> RemoteComponentWrapper = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"p-4"</span>&gt;
      &lt;Suspense fallback={&lt;LoadingSpinner /&gt;}&gt;
        &lt;RemoteHeader /&gt;
      &lt;/Suspense&gt;

      &lt;div className=<span class="hljs-string">"mt-4"</span>&gt;
        &lt;Suspense fallback={&lt;LoadingSpinner /&gt;}&gt;
          &lt;RemoteButton
            text=<span class="hljs-string">"Remote Button"</span>
            onClick={<span class="hljs-function">() =&gt;</span>
              alert(
                <span class="hljs-string">"Well done you've imported the MF remote component successfully"</span>
              )
            }
          /&gt;
        &lt;/Suspense&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>This component acts as a wrapper, handling some very simple business logic such as loading your remote components, handling a loading spinner whilst waiting for the remote, and passing your <code>onClick</code> event to the remote button.</p>
<h3 id="heading-why-use-reactlazy">Why Use <code>React.lazy()</code>?</h3>
<p>The import with <code>React.lazy</code> isn’t required for Module Federation components – it’s more of a best practice for React apps when the remote module is:</p>
<ul>
<li><p>Loaded asynchronously at runtime, which Module Federation remotes almost always are</p>
</li>
<li><p>You want React to handle the loading state and code-splitting gracefully – shown using the <code>Suspense</code> component</p>
</li>
</ul>
<p><code>React.lazy</code> + <code>&lt;Suspense&gt;</code> gives React a built-in way to pause rendering until the component is ready. Without it, you’d need manual loading state handling.</p>
<p>It also keeps your components looking “normal.” With React.Lazy, <code>&lt;RemoteHeader/&gt;</code> is just another component in your JSX.</p>
<p>Without it, you’d need something like:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [Header, setHeader] = useState(<span class="hljs-literal">null</span>);

useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">import</span>(<span class="hljs-string">"remote_app/Header"</span>).then(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> setHeader(<span class="hljs-function">() =&gt;</span> m.default));
}, []);

<span class="hljs-keyword">return</span> Header ? &lt;Header /&gt; : &lt;LoadingSpinner /&gt;;
</code></pre>
<p>…which is messier and repeats for every remote component.</p>
<h2 id="heading-how-to-handle-typescript-errors">How to Handle TypeScript Errors</h2>
<p>Inside your <code>RemoteWrapperComponent</code> you’re going to see the following error on your <code>Button</code> and <code>Header</code> imports.</p>
<blockquote>
<p>Cannot find module 'remote_app/Button' or its corresponding type declarations.ts (2307)</p>
</blockquote>
<p>You get this error because the remote modules are not defined with types, so both your remote and your host doesn’t know what this imported component is, nor does it know its structure (a key part to TypeScript development).</p>
<p>To fix this you will need to provide your host app with custom types.</p>
<h3 id="heading-add-a-type-declaration-file">Add a Type Declaration File</h3>
<p>A type declaration file (if you’re unaware of them) has a <code>.d.ts</code> suffix.</p>
<p>Within your host app, create a file in <code>src/types</code> called <code>remote-app.d.ts</code>. Naming the file in this way lets us know the declarations within are related to the <em>remote-app</em>. This is useful especially when consuming multiple remotes.</p>
<p>Copy and paste the following declarations into your <code>remote-app.d.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// host - ./src/types/remote.d.ts</span>
<span class="hljs-keyword">declare</span> <span class="hljs-keyword">module</span> "remote_app/Button" {
  <span class="hljs-keyword">const</span> Button: React.FC&lt;{
    text: <span class="hljs-built_in">string</span>;
    onClick?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  }&gt;;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
}

<span class="hljs-keyword">declare</span> <span class="hljs-keyword">module</span> "remote_app/Header" {
  <span class="hljs-keyword">const</span> Header: React.FC;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
}
</code></pre>
<p>Now if you return to your <code>RemoteWrapperComponent</code> your errors should be gone. If they aren’t, restart your IDE (in VS Code you can open your command palette and select <code>Restart Typescript Server</code>).</p>
<h2 id="heading-how-to-add-remotewrappercomponent-to-apptsx">How to Add <code>RemoteWrapperComponent</code> to App.tsx</h2>
<p>Import the <code>RemoteWrapperComponent</code> into App.tsx.</p>
<p>I’ve removed all the boilerplate code and replaced with some basic styling to allow us to easily see what is the host, and what are the remote components.</p>
<p>Copy and paste the following code into your host <code>App.tsx</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// host - ./src/App.tsx</span>
<span class="hljs-keyword">import</span> viteLogo <span class="hljs-keyword">from</span> <span class="hljs-string">"/vite.svg"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> { RemoteComponentWrapper } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/RemoteComponentWrapper"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;div className=<span class="hljs-string">"px-6 border-2"</span>&gt;
        &lt;div className=<span class="hljs-string">"flex justify-center items-center"</span>&gt;
          &lt;img src={viteLogo} alt=<span class="hljs-string">"Example"</span> /&gt;
        &lt;/div&gt;
        &lt;h1 className=<span class="hljs-string">"text-2xl"</span>&gt;Host Application&lt;/h1&gt;
        &lt;p&gt;
          {<span class="hljs-string">" "</span>}
          Welcome to the Host application, below are the components pulled <span class="hljs-keyword">from</span>
          the remote application
        &lt;/p&gt;
        &lt;RemoteComponentWrapper /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h2 id="heading-how-to-serve-the-remote-app-and-run-your-host">How to Serve the Remote App and Run Your Host</h2>
<p>Due to how Vite works, you need to build the application before you preview / serve the application.</p>
<p>Make sure that your <code>package.json</code> file’s scripts block looks like this:</p>
<pre><code class="lang-json"># remote-app
<span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc -b &amp;&amp; vite build"</span>,
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ."</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview --port 5001 --strictPort"</span>,
    <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"npm run build &amp;&amp; npm run preview"</span>
  },

# host-app
<span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc -b &amp;&amp; vite build"</span>,
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ."</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview --port 5000 --strictPort"</span>,
    <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"npm run build &amp;&amp; npm run preview"</span>
  },
</code></pre>
<p>In your terminal run:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ./remote-app

<span class="hljs-comment"># run a build, and run / load the app on port 5000</span>
npm run serve
</code></pre>
<p>You should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755207471886/c64b29ef-3f95-44e5-903b-896f7a3a36fc.png" alt="screenshot showing output from terminal, port 5001 running remote app" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now you can run the host application by doing the same:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># change to the host-app in another terminal </span>
<span class="hljs-built_in">cd</span> ../host-app

<span class="hljs-comment"># build and run the application</span>
npm run serve
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755207573717/77ab133f-ac2e-4f51-8152-3de7200c067c.png" alt="screenshot showing the host-app running on port 5000" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>If you open the <code>localhost:5000</code> you should now see your host application with the remote components:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756078239732/775ab6ec-4566-44cd-ac85-66c37a9001ca.png" alt="image: shows the finished host app consuming the remote components" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Next, if you click the button, you can see that it shows the message you configured from within the <code>RemoteWrapperComponent</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756078270491/65a72ab8-1cfa-4298-be02-68d0528fd50f.png" alt="image: shows alert modal with provided message after clicking remote button" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-the-true-power-of-micro-frontends">The True Power of Micro Frontends</h2>
<p>The true power of micro frontends lies in their ability to update the remote components, without the need to rebuild the host. To fully demonstrate this, keep the host running, and update the <code>Button</code> component on your <code>remote-app</code>.</p>
<p>Let’s update the remote components. Use the below code to update both the <code>Button</code> and <code>Header</code> components:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// remote - ./src/components/Header.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Header: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;header className=<span class="hljs-string">"bg-gray-800 text-white p-4"</span>&gt;
      &lt;h1 className=<span class="hljs-string">"text-2xl"</span>&gt;Updated Remote App Header&lt;/h1&gt;
      &lt;p className=<span class="hljs-string">"text-white"</span>&gt;Hi, Grant&lt;/p&gt;
    &lt;/header&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;

<span class="hljs-comment">// remote - ./src/components/Button.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> ButtonProps {
  text: <span class="hljs-built_in">string</span>;
  onClick?: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> Button: React.FC&lt;ButtonProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ text, onClick }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;button
      onClick={onClick}
      className=<span class="hljs-string">"px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 hover:cursor-pointer"</span>
    &gt;
      {text}
    &lt;/button&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>Once you’ve updated the remote components, run the following command in your <code>remote-app</code> folder:</p>
<pre><code class="lang-bash">npm run serve
</code></pre>
<p>Next, refresh your host app in the browser and you will see the updated app:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756078980661/02ba0655-a18c-460c-939b-6fa95b4149b7.png" alt="image: shows host application with updated remote compnents" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The update to the remote components is visible immediately, without restarting or rebuilding the host app. This highlights a key benefit of micro frontends: shared components are fetched from their own server via the <code>remote.js</code> file, enabling independent updates.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>You've successfully built and deployed a micro frontend architecture – congrats! This basic implementation demonstrates the true power of Module Federation and the ability to update shared components without needing to rebuild and redeploy the entire host application.</p>
<p>This independence can dramatically accelerate development cycles and empower teams to work more autonomously.</p>
<p>I hope you’ve learned something from this article, and as always for more tutorials and discussions, connect with me on <a target="_blank" href="https://x.com/grantdotdev">twitter/x</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Vite for a Better Web Development Workflow ]]>
                </title>
                <description>
                    <![CDATA[ Speed and simplicity are at the heart of modern web development. As applications grow more complex and the demand for rapid iteration increases, developers are constantly searching for tools that minimize friction and maximize productivity. Tradition... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-vite-for-a-better-web-development-workflow/</link>
                <guid isPermaLink="false">6824ae178a2b26e9d2d0fe4c</guid>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 14 May 2025 14:52:07 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747234316384/0c05bb05-f21e-418e-9e07-7e30df74f674.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Speed and simplicity are at the heart of modern web development. As applications grow more complex and the demand for rapid iteration increases, developers are constantly searching for tools that minimize friction and maximize productivity. Traditional build tools like Webpack, while powerful, often come with steep learning curves, slow startup times, and tedious configurations. That’s where Vite enters the scene. It offers a new approach to building web applications that’s not only fast but also easy to use.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that will teach you all about Vite, the speed-focused build tool that's designed for today's modern frameworks. This beginner-friendly course will walk you through everything you need to know to get up and running with Vite, from creating your first project to deploying it for production. With clear explanations and hands-on demonstrations, you'll gain a solid understanding of what makes Vite so fast and why developers everywhere are making the switch.</p>
<p>So, what exactly is Vite? Vite is a next-generation frontend build tool that focuses on performance and developer experience. Created by Evan You (the creator of Vue.js), Vite leverages modern browser capabilities and JavaScript features to deliver near-instant project startup and lightning-fast hot module replacement (HMR). Unlike traditional bundlers that pre-bundle your entire project before serving it, Vite serves source files as native ES modules during development. This drastically reduces startup time, especially in large projects, and ensures that changes you make are reflected immediately in the browser without a full page reload.</p>
<p>This course covers the core use cases and benefits of Vite, including how to:</p>
<ul>
<li><p>Build React apps quickly and efficiently using Vite’s minimal setup.</p>
</li>
<li><p>Integrate TypeScript seamlessly into your development workflow.</p>
</li>
<li><p>Manage environment variables and static assets effectively.</p>
</li>
<li><p>Configure and extend Vite with custom options and plugins.</p>
</li>
<li><p>Optimize your app for production with Vite’s powerful build tools.</p>
</li>
</ul>
<p>You’ll start by creating your first React project with Vite in just minutes. From there, you’ll dive into essential topics like handling static files, using environment variables (including those built into platforms like Scrimba), and setting up a TypeScript-powered development environment. You’ll also learn how to build your app for production and explore Vite’s flexible configuration system, which allows you to tailor your setup to match your project’s specific needs.</p>
<p>One of the standout segments of this course is the deep dive into what makes Vite fast. You'll learn about the technical innovations behind Vite’s performance, such as native ES modules, optimized dependency pre-bundling, and intelligent caching. You’ll also see how Vite compares to traditional tools like Webpack in terms of both speed and developer experience.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/do62-z3z6FM">the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/do62-z3z6FM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Your Own Wheel of Names with React and TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ A while ago, I stumbled upon a website listing various coding challenges, and I decided to give some a try. Last week, I came across one that involved building a "Wheel of Names." It reminded me of a similar project I built years ago using Flash and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-your-own-wheel-of-names/</link>
                <guid isPermaLink="false">67190fa49a9d435e2ceeafb6</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Bun ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Wheel of Names ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Wed, 23 Oct 2024 15:00:52 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729695428229/56ac185a-bed1-4bbc-ab6d-a12f2ac5adee.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A while ago, I stumbled upon a website listing various coding challenges, and I decided to give some a try.</p>
<p>Last week, I came across one that involved building a "Wheel of Names." It reminded me of a similar project I built years ago using Flash and ActionScript 3—technologies that have since fallen out of use. So, I thought it would be fun to recreate the wheel, but this time using a modern tech stack.</p>
<p>In this tutorial, I’ll walk you through how I built it from scratch.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-description">Project Description</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-would-i-need-a-wheel-of-names">Why Would I Need a Wheel of Names?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-plan-for-the-app">The Plan for the App</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-application-features">Application Features</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tech-stack">The Tech Stack</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-build-the-app">Let’s Build the App</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-structure">Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-components">How to Build the Components</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deploy-the-app-to-vercel">How to Deploy the App to Vercel</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-project-description">Project Description</h2>
<p>This will be an app that I presume is inspired by the TV show Wheel of Fortune. In the TV show, contestants try to figure out a short phrase by guessing letters. If they guess correctly, the letter will be revealed. They spin the wheel to determine how much money each correct letter is worth.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729622283461/8fde5307-eadd-4b4a-8f39-fe8e47478bd3.png" alt="The wheel in Wheel of Fortune displaying the different dollar amounts/prizes contestants can get" class="image--center mx-auto" width="894" height="894" loading="lazy"></p>
<p>Wheel of Names is similar, but allows us to create a virtual wheel, putting our own names on it. We can then virtually spin it to determine a winner.</p>
<h3 id="heading-github-repo"><strong>GitHub Repo</strong></h3>
<p>If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names">here</a> 💁 is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names/blob/main/README.md">README</a> 🙌. <a target="_blank" href="https://wheel-of-names-three.vercel.app/">Here</a> you can see the live demo.</p>
<h2 id="heading-why-would-i-need-a-wheel-of-names">Why Would I Need a Wheel of Names?</h2>
<p>First of all, it’s a lot of fun to build your own! A practical, real-life use case would be for running lottery-style games where you need to pick a random winner.</p>
<p>For example, imagine you’re part of an agile team that holds retrospectives every two weeks, and you need to randomly choose a team member to lead each session. Just add everyone’s name to the participants list, spin the wheel, and let it decide for you! 🎡</p>
<h2 id="heading-the-plan-for-the-app">The Plan for the App</h2>
<p>The app is made up of several components, with the main feature being the spinning wheel. The wheel will have a section for each participant, and each section will be uniquely coloured, with its size proportionally calculated based on the number of participants. Once the spinning animation finishes, the winner will be revealed with a fun, confetti-style popup.</p>
<p>Other parts of the app include a section to enter the question or phrase that the spin is for, as well as controls for adding participant names and displaying them in a neatly organised list.</p>
<p>The list will offer options to sort and shuffle the names. The sorting will arrange the names alphabetically, while the shuffle option will randomise them. You can also delete any previously added participant.</p>
<p>All of these changes are dynamically reflected on the wheel component, ensuring that the wheel stays up-to-date with the latest participant list.</p>
<p>Here are a few screenshots that showcase what the app will look like once it's complete.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729578916704/0634a255-99a3-4a9c-8d64-6468cd732d40.png" alt="Wheel of Names app - initial empty state" class="image--center mx-auto" width="1302" height="790" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729578959007/33b8c9a5-73d0-4c61-bdad-1006107358a8.png" alt="Wheel of Names app - adding question" class="image--center mx-auto" width="1292" height="826" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729578989621/fa954e01-52be-4e60-8a01-27a7a2b12a70.png" alt="Wheel of Names app - adding participants" class="image--center mx-auto" width="1288" height="881" loading="lazy"></p>
<p>Here are some YouTube videos I recorded after completing the app, showcasing its features in action.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/sugUnci1Rlw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gIc6wtH9fK8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-application-features"><strong>Application features:</strong></h3>
<p><strong>I. Question</strong></p>
<ol>
<li><p>This is where users can submit a question or phrase that will determine the focus of the spins.</p>
</li>
<li><p>Any changes made in the input field are saved when the user clicks outside of it (on focus out).</p>
</li>
</ol>
<p><strong>II. Wheel</strong></p>
<ol>
<li><p>The wheel component spins with an easing animation and determines the winner.</p>
</li>
<li><p>The spin direction can be adjusted using the buttons, for either clockwise or counterclockwise rotation.</p>
</li>
<li><p>Each adjacent sector is uniquely coloured, and their sizes are calculated proportionally to the number of participants.</p>
</li>
</ol>
<p><strong>III. Add Participants</strong></p>
<ol>
<li><p>The participant entry area includes an input field for entering a participant's name and an 'ADD' button to add it to the participants list.</p>
</li>
<li><p>To add participants more quickly, the user can press the ENTER key on the keyboard.</p>
</li>
</ol>
<p><strong>IV. Participants List</strong></p>
<ol>
<li><p>This section displays all the participants' names.</p>
</li>
<li><p>The list offers options to sort the names alphabetically or shuffle them randomly, with both actions dynamically updating the wheel component.</p>
</li>
</ol>
<h3 id="heading-the-tech-stack">The Tech Stack</h3>
<p>Here’s a list of the main technologies we’ll be using:</p>
<ul>
<li><p><strong>Bun</strong> – A fast JavaScript bundler and package manager, known for its speed and simplicity.</p>
</li>
<li><p><strong>Vite</strong> – A build tool that provides a fast development environment, particularly optimised for modern web projects.</p>
</li>
<li><p><strong>React</strong> – A popular JavaScript library for building user interfaces, enabling efficient rendering and state management.</p>
</li>
<li><p><strong>TypeScript</strong> – A superset of JavaScript that adds static typing, improving code quality and maintainability.</p>
</li>
<li><p><strong>styled-components</strong> – A library for writing CSS-in-JS, allowing styles to be scoped to components and providing a more dynamic approach to styling.</p>
</li>
<li><p><strong>canvas</strong> – A powerful HTML element used to draw graphics, animations, and other dynamic content directly on the web page.</p>
</li>
<li><p><strong>canvas-confetti</strong> – A JavaScript library for adding fun, celebratory confetti animations to the canvas, perfect for announcing winners.</p>
</li>
</ul>
<h2 id="heading-lets-build-the-app"><strong>Let’s Build the App</strong></h2>
<p>From this point onward I will guide you through the process I followed when building this app.</p>
<h3 id="heading-project-structure"><strong>Project Structure</strong></h3>
<p>The project structure is quite straightforward, thanks to React and styled-components, which make this modular approach easy to implement. You can check out the project structure in my GitHub repo.</p>
<p>Below, I’ll walk you through the reasoning behind the structure and explain the decisions I made for each part.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729579163492/2b406e15-c8f6-4533-9a93-4c6e7b1435f4.png" alt="Project Structure" class="image--center mx-auto" width="319" height="726" loading="lazy"></p>
<ul>
<li><p><strong>main.tsx</strong>: The entry point of the React app created with Vite.</p>
</li>
<li><p><strong>App.tsx</strong>: The parent component that includes all other components and handles participant name management (adding, removing, sorting, shuffling).</p>
</li>
<li><p><strong>Header.tsx</strong>: Top part of the app, which renders the app title.</p>
</li>
<li><p><strong>Participants.tsx</strong>: Renders the controls for adding and displaying participants. It includes a validation function to prevent empty or invalid names.</p>
</li>
<li><p><strong>Question.tsx</strong>: Displays the question section, managing state and basic keyboard and click functionality.</p>
</li>
<li><p><strong>Wheel.tsx</strong>: The core component containing the animation logic, sector size/colouring, and rendering participant names. It uses the <code>canvas</code> element for smooth spinning and integrates <code>confetti</code> to announce the winner.</p>
</li>
<li><p><strong>utils.ts</strong>: A file with helper functions used across the app.</p>
</li>
<li><p><strong>styles.ts</strong>: Contains shared styled components, exported for use throughout the app.</p>
</li>
</ul>
<h4 id="heading-css-files-and-configs">CSS Files and Configs</h4>
<p>The remaining files in the project include standard boilerplate CSS styles from the initial Vite setup, along with configuration files for Vite, TypeScript, Prettier, and ESLint. These configurations are commonly used in modern projects and are not specific to this app, so I won't dive into them here. You can easily find documentation for each online.</p>
<h3 id="heading-how-to-build-the-components">How to Build the Components</h3>
<p>In this section, we will go through the process of building each component of the application, step by step. By the end, you’ll have a fully functional app with modular, self-contained components.</p>
<h4 id="heading-1-app-component">1. App component</h4>
<p>The App component serves as the central container for the entire application. It encapsulates all the core building blocks and is responsible for managing the state of the participants' names. Beyond rendering the UI, it handles key application logic, such as adding, removing, sorting, and shuffling participants.</p>
<p>The component uses local state to hold the list of names. This state is updated through callback functions that are triggered by interactions in the child components — specifically, the <code>Participants</code> and <code>Wheel</code> components.</p>
<p>The primary handler functions, <code>handleAddName</code> and <code>handleRemoveName</code>, manage adding and removing names from the list. Additionally, there are two other handlers dedicated to manipulating the order of the names: one for sorting (<code>handleSortNames</code>) and one for shuffling (<code>handleShuffleNames</code>). These handlers provide flexibility in how the list of participants is displayed and interacted with in the app.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [names, setNames] = useState&lt;<span class="hljs-built_in">string</span>[]&gt;([]);

  <span class="hljs-keyword">const</span> handleAddName = <span class="hljs-function">(<span class="hljs-params">name: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (names.length &lt; MAX_PARTICIPANTS) {
      setNames([...names, name]);
    }
  };

  <span class="hljs-keyword">const</span> handleRemoveName = <span class="hljs-function">(<span class="hljs-params">index: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
    setNames(names.filter(<span class="hljs-function">(<span class="hljs-params">_, i</span>) =&gt;</span> i !== index));
  };

  <span class="hljs-keyword">const</span> shuffleNames = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> shuffledNames = [...names].sort(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Math</span>.random() - <span class="hljs-number">0.5</span>);
    setNames(shuffledNames);
  };

  <span class="hljs-keyword">const</span> sortNames = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> sortedNames = [...names].sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a.localeCompare(b));
    setNames(sortedNames);
  };
</code></pre>
<p>A crucial part of the component is the <code>MAX_PARTICIPANTS</code> constant, which sets a limit on the number of participants allowed. This ensures that the app doesn't exceed a certain number of entries, maintaining performance and usability.</p>
<p>The rendering structure of this component looks as follows:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;Header /&gt;
      &lt;Question /&gt;
      &lt;Main&gt;
        &lt;Participants
          handleAddName={handleAddName}
          handleRemoveName={handleRemoveName}
          shuffleNames={shuffleNames}
          sortNames={sortNames}
          names={names}
        /&gt;
        &lt;Wheel participants={names} /&gt;
      &lt;/Main&gt;
    &lt;/&gt;
  );
</code></pre>
<h4 id="heading-2-header-component">2. Header component</h4>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names/blob/main/src/Header.tsx">Header component</a> is the simplest part of the application. Its primary role is to display the title at the top of the page. This component is essential for setting the tone and branding of the application. Despite its simplicity, it lays the foundation for structuring the UI and can be easily customised or extended in the future.</p>
<p>Here is how it looks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729581820870/9384834a-8657-435d-89f5-e404a67d6ac0.png" alt="Header Component" class="image--center mx-auto" width="1281" height="202" loading="lazy"></p>
<h4 id="heading-3-question-component">3. Question component</h4>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names/blob/main/src/Question.tsx">component</a> that displays the input for entering a question or phrase is relatively simple. It renders a text field and utilises a few handler functions to enhance the user experience. These handlers manage focus behaviour: setting focus when the input field is clicked, removing focus when the user clicks outside the field, and allowing the user to use the ENTER or ESCAPE keys to submit or cancel their input, respectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729581877713/94fa416b-48e0-4a33-8ed4-1db975ce7542.png" alt="Question Component on Focus" class="image--center mx-auto" width="810" height="63" loading="lazy"></p>
<h4 id="heading-4-participants">4. Participants</h4>
<p>In this part of the app, we render the list of all added participants. The component includes a local validation function that runs each time before adding a new participant, ensuring that the input meets the necessary criteria (for example, no duplicates or empty names).</p>
<p>We also leverage built-in HTML attributes to dynamically enable or disable buttons based on the state of the participants list. For example, the "Sort" and "Shuffle" buttons are disabled when the list is empty, while the "Add" button is disabled once the maximum participant limit (<code>MAX_PARTICIPANTS</code>) has been reached. This ensures a smooth and intuitive user experience by preventing invalid actions.</p>
<p>You’ve probably already noticed how we use a utility function from the <code>utils.ts</code> file to capitalise the participant names before displaying them. This ensures that all names are presented in a consistent and user-friendly format.</p>
<p>This happens inside a <code>map()</code> loop, where we iterate over the <code>names</code> data structure and display each participant's name in a separate row within the list component. The utility function is applied during this iteration to ensure that the names are properly capitalised before rendering.</p>
<h4 id="heading-5-wheel-component">5. Wheel component</h4>
<p>This is the largest component in our app. At the top, you'll find the styles required to position the winning popup, which is accompanied by confetti when a winner is selected. Below that, we define an array containing all the possible colours used to color the wheel sectors. Afterward, we move into the component code itself.</p>
<p>The component utilises several states to ensure the spinning animation behaves as expected. Additionally, it manages when to trigger and display the winner popup, with the winner's name shown inside. These states and handlers work together to create a smooth and interactive experience.</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> [spinning, setSpinning] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [rotation, setRotation] = useState(<span class="hljs-number">0</span>);
  <span class="hljs-keyword">const</span> [spinDirection, setSpinDirection] = useState&lt;
    <span class="hljs-string">'clockwise'</span> | <span class="hljs-string">'counterclockwise'</span>
  &gt;(<span class="hljs-string">'clockwise'</span>);
  <span class="hljs-keyword">const</span> [showPopup, setShowPopup] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [popupWinner, setPopupWinner] = useState&lt;<span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
</code></pre>
<p>The <code>drawWheel()</code> method is responsible for rendering the wheel with the specified number of sectors on the canvas. This method relies heavily on the <code>canvas</code> element and its associated API to draw each sector and participant's name. We also use our helper function to capitalise the participants' names in the wheel, ensuring consistency with the list component.</p>
<p>When the "Spin" button is clicked, the <code>startSpin()</code> method is triggered. This is where the animation logic is implemented. We generate a random number of rotations, ranging from 5 to 10 full rotations, to make the spin feel unpredictable.</p>
<p>The direction of the spin is determined by the user's selection, allowing the wheel to spin either clockwise or counterclockwise. We also set the spin duration to 6000ms (6 seconds) for a smooth and engaging animation.</p>
<p>To enhance the realism of the animation, we apply an easing function that implements the "Ease-out cubic" effect, which causes the wheel to gradually slow down as it reaches the end of the spin.</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> easing = <span class="hljs-function">(<span class="hljs-params">t: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
      <span class="hljs-comment">// Ease-out cubic</span>
      <span class="hljs-keyword">return</span> <span class="hljs-number">1</span> - <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">1</span> - t, <span class="hljs-number">3</span>);
    };
</code></pre>
<p>The animation is handled by an inner function called <code>animate()</code>, which utilises the <code>requestAnimationFrame</code> API, a feature supported by all modern browsers for smooth, high-performance animations. Inside this function, we calculate the elapsed time and the current rotation, updating the component's state accordingly to ensure the wheel spins smoothly.</p>
<p>During each animation frame, we also invoke the <code>determineWinner()</code> function, which is defined below. This function calculates the winning sector by determining which sector the wheel lands on at the end of the spin. It then updates the popup state to display the winner’s name inside the popup.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> determineWinner = <span class="hljs-function">(<span class="hljs-params">finalRotation: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> sliceAngle = <span class="hljs-number">360</span> / numSectors;
    <span class="hljs-keyword">const</span> normalizedRotation = ((finalRotation % <span class="hljs-number">360</span>) + <span class="hljs-number">360</span>) % <span class="hljs-number">360</span>;
    <span class="hljs-keyword">const</span> winningSector = <span class="hljs-built_in">Math</span>.floor(normalizedRotation / sliceAngle);

    setPopupWinner(participants[winningSector]);
    setShowPopup(<span class="hljs-literal">true</span>);
  };
</code></pre>
<p>Changing the direction of the spin is straightforward. We simply update the component’s state based on the value of the button’s label, which toggles between "Clockwise" and "Counterclockwise." By setting the state accordingly, we can easily control the spin direction with a single click of the button.</p>
<p>The remaining code before the rendering part of this component includes an effect that controls the visibility of the confetti popup. The <code>startConfetti</code> function is responsible for initiating the confetti animation when a winner is selected. This effect ensures that the confetti animation is triggered and displayed at the right moment, adding a celebratory touch to the experience.</p>
<p>And with all that we are ready to render our Wheel component as it follows:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;canvas
        ref={canvasRef}
        width={<span class="hljs-number">400</span>}
        height={<span class="hljs-number">400</span>}
        style={{ borderRadius: <span class="hljs-string">'50%'</span>, border: <span class="hljs-string">'2px solid black'</span> }}
      /&gt;
      &lt;ButtonsContainer&gt;
        &lt;Button
          onClick={changeSpinDirection}
          disabled={participants.length === <span class="hljs-number">0</span> || spinning}
        &gt;
          {capitalize(spinDirection)}
        &lt;/Button&gt;
        &lt;Button
          onClick={startSpin}
          disabled={participants.length === <span class="hljs-number">0</span> || spinning}
        &gt;
          Spin
        &lt;/Button&gt;
      &lt;/ButtonsContainer&gt;
      {showPopup &amp;&amp; popupWinner &amp;&amp; (
        &lt;Popup&gt;
          &lt;h2&gt;Congratulations!&lt;/h2&gt;
          &lt;h3&gt;{capitalize(popupWinner)}&lt;/h3&gt;
        &lt;/Popup&gt;
      )}
    &lt;/div&gt;
</code></pre>
<h2 id="heading-how-to-deploy-the-app-to-vercel"><strong>How to Deploy the App to Vercel</strong></h2>
<p>Finally 🎉 we’re ready to deploy our app.</p>
<p>I used Vercel for this deployment because it offers a fast, free, and easy way to deploy web apps. If you'd like a more detailed guide on how to deploy with Vercel, check out my <a target="_blank" href="https://www.mihailgaberov.com/build-a-real-time-order-book-application-with-react-and-websockets">previous tutorial</a> for step-by-step instructions.</p>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>I hope you found this process as interesting and enjoyable to follow as it was for me to create!</p>
<p>Now, let’s take a moment to reflect on what we’ve accomplished and highlight a few key takeaways that could prove useful for future projects.</p>
<ol>
<li><p><strong>Modular Design</strong>: Breaking the app into small, manageable components made it easier to maintain and scale.</p>
</li>
<li><p><strong>React and Styled-Components</strong>: These tools streamlined the development, allowing for dynamic styling and efficient UI management.</p>
</li>
<li><p><strong>Canvas for Animations</strong>: Leveraging the <code>canvas</code> element enabled smooth and visually appealing animations.</p>
</li>
<li><p><strong>Vercel Deployment</strong>: Vercel's simplicity and speed made it the ideal choice for quickly deploying the app.</p>
</li>
</ol>
<p>This project highlighted the power of modern tools like React, TypeScript, and canvas, all while ensuring the app stayed modular and easy to maintain.</p>
<p>Thanks for reading! 🙏</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use React.js with Laravel to Build a Draggable Tasklist App ]]>
                </title>
                <description>
                    <![CDATA[ You may have seen tutorials that help you build a simple React.js app that use some third-party API or a Node.js server as a backend. You could also use Laravel for this purpose and integrate it with React. As a backend framework, Laravel actually of... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-react-with-laravel/</link>
                <guid isPermaLink="false">66ba2afae059e7f2fa5d3276</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ San B ]]>
                </dc:creator>
                <pubDate>Fri, 26 Jan 2024 00:09:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/freecodecamp-boolfalse-laravel-react-vite-draggable-tasklist.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You may have seen tutorials that help you build a simple React.js app that use some third-party API or a Node.js server as a backend. You could also use Laravel for this purpose and integrate it with React.</p>
<p>As a backend framework, Laravel actually offers a tool to help you do this, called <a target="_blank" href="https://laravel.com/docs/10.x/frontend#inertia">Inertia</a>. Here's what the docs say about it:</p>
<blockquote>
<p>It bridges the gap between your Laravel application and your modern Vue or React frontend, allowing you to build full-fledged, modern frontends using Vue or React while leveraging Laravel routes and controllers for routing, data hydration, and authentication — all within a single code repository.</p>
</blockquote>
<p>But what if you don't want to use such a tool? And instead, you just want to use React.js as a frontend library and have a simple Laravel-powered backend?</p>
<p>Well, in this article, you will learn how to use React.js with Laravel as a backend by building a draggable tasklist app.</p>
<p>For this full-stack single-page app, you'll use <a target="_blank" href="https://vitejs.dev/">Vite.js</a> as your frontend build tool and the <a target="_blank" href="https://www.npmjs.com/package/react-beautiful-dnd">react-beautiful-dnd</a> package for draggable items.</p>
<p>By the end of this article, you will have a single-page app for managing tasks, which will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/tasklist.png" alt="Image" width="600" height="400" loading="lazy">
<em>Captured from a local working project</em></p>
<p>In this article, we'll create a dynamic page that will have a list of tasks, each of which will belong to a specific project. This way, the user will be able to select a project, and only the tasks of the selected project will be shown on the page. The user can also create a new task for the current project, as well as edit, delete and reorder tasks by dragging and dropping them.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-the-backend-how-to-install-laravel">The Backend: How to Install Laravel</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-models-and-migrations">How to Create Models and Migrations</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-seeders">How to Create Seeders</a></li>
<li><a class="post-section-overview" href="#heading-how-to-connect-to-the-mysql-database">How to Connect to the MySQL Database</a></li>
<li><a class="post-section-overview" href="#heading-service-injection">Service Injection</a></li>
<li><a class="post-section-overview" href="#heading-web-and-api-routes-in-laravel">Web and API Routes in Laravel</a></li>
<li><a class="post-section-overview" href="#heading-validation-requests-in-laravel">Validation Requests in Laravel</a></li>
<li><a class="post-section-overview" href="#heading-how-to-write-a-controller-that-uses-services">How to Write a Controller that Uses Services</a></li>
<li><a class="post-section-overview" href="#heading-how-to-test-the-api-routes">How to Test the API Routes</a></li>
<li><a class="post-section-overview" href="#heading-the-frontend-how-to-install-the-packages">The Frontend: How to Install the Packages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-vitejs">How to Configure Vite.js</a></li>
<li><a class="post-section-overview" href="#heading-reactjs-initial-integration">React.js – Initial Integration</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-css">How to Add CSS</a></li>
<li><a class="post-section-overview" href="#heading-a-service-for-the-api-requests">A Service for the API requests</a></li>
<li><a class="post-section-overview" href="#heading-reactjs-components">React.js Components</a></li>
<li><a class="post-section-overview" href="#heading-final-results">Final Results</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before following along, it would be helpful to have a basic understanding of React.js, Laravel, and familiarity with fundamental web development concepts.</p>
<p>You'll need the following tools for the app that we'll build in this article:</p>
<ul>
<li><strong>PHP</strong> 8.1 or above (run <code>php -v</code> to check the version)</li>
<li><strong>Composer</strong> (run <code>composer</code> to check that it exists)</li>
<li><strong>Node.js</strong> 18 or above (run <code>node -v</code> to check the version)</li>
<li><strong>MySQL</strong> 5.7 or above (run <code>mysql --version</code> to check if it exists, or follow the <a target="_blank" href="https://dev.mysql.com/doc/mysql-windows-excerpt/5.7/en/windows-testing.html">docs</a>)</li>
</ul>
<p>Additional (optional) tools that you can use:</p>
<ul>
<li><strong>Postman</strong> – a program with a UI for testing the API routes</li>
<li><strong>curl</strong> – a CLI command for testing the API routes</li>
</ul>
<p>We'll start by building out the backend, and then move to the frontend.</p>
<h2 id="heading-the-backend-how-to-install-laravel">The Backend: How to Install Laravel</h2>
<p>First, if you don't have it already, you'll need to install the Laravel framework on your local machine.</p>
<p>One way to install Laravel is by using a popular dependency manager for PHP called Composer. Here's the command to do so:</p>
<pre><code class="lang-shell">composer create-project laravel/laravel tasklist
</code></pre>
<p>This will install the latest stable version of Laravel in your local machine (currently it's version 10).</p>
<p>The <em>tasklist</em> in the command is the app's root folder name, which you can set to whatever you want.</p>
<p>At this point, you can <code>cd</code> into the project's folder and run the backend app without needing to have a virtual server set up:</p>
<pre><code class="lang-shell">cd tasklist/ &amp;&amp; php artisan serve
</code></pre>
<p>The <code>artisan</code> in the above command is a CLI tool included in Laravel. It exists at the root of your Laravel application as the <code>artisan</code> script file, which provides a number of helpful commands that can assist you while you build your application.<br>We'll use it in this article often.</p>
<p>Visit <a target="_blank" href="http://127.0.0.1:8000"><code>http://127.0.0.1:8000</code></a> in your browser to see the default page. It should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/laravel.png" alt="Image" width="600" height="400" loading="lazy">
<em>Laravel welcome page</em></p>
<h2 id="heading-how-to-create-models-and-migrations">How to Create Models and Migrations</h2>
<p>Now, let's create <em>Project</em> and <em>Task</em> models, as well as migrations for them.</p>
<p>Models are the way your app entities should be defined, and migrations are like schema definitions for storing the records of those entities in the database. </p>
<p>You can create model and migration files manually as well as generate them using the <code>artisan</code> command:</p>
<pre><code class="lang-shell">php artisan make:model Project -m
php artisan make:model Task -m
</code></pre>
<p>The <code>-m</code> argument will automatically generate a migration file using the provided model name.</p>
<p>Keep the command execution sequence as it is, so the Project's migration later can run before the Task's migration.</p>
<p>This is important, because the <code>projects</code> and <code>tasks</code> tables should have a one-to-many relationship (1-N): each task will refer to a single project, or, in other words, each project can have multiple tasks.</p>
<p>Set the <code>Project</code> model's <code>$fillable</code> fields and the <code>task()</code> relationship method as below:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">HasFactory</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Relations</span>\<span class="hljs-title">HasMany</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Project</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">HasFactory</span>;

    <span class="hljs-keyword">protected</span> $table = <span class="hljs-string">'projects'</span>;
    <span class="hljs-keyword">public</span> $timestamps = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">protected</span> $fillable = [
        <span class="hljs-string">'id'</span>, <span class="hljs-comment">// primary key, auto-increment, integer</span>
        <span class="hljs-string">'name'</span>, <span class="hljs-comment">// string</span>
    ];

    <span class="hljs-comment">// a project can have multiple tasks</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tasks</span>(<span class="hljs-params"></span>): <span class="hljs-title">HasMany</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;hasMany(Task::class);
    }
}
</code></pre>
<p>By default, the <code>$timestamps</code> public property has a <code>true</code> value, which is coming from the parent <code>Model</code> class. This means that the <code>created_at</code> and <code>updated_at</code> columns in your database table will be maintained automatically by <em>Eloquent</em> (the ORM included in Laravel).</p>
<p>But you can customize it by changing its value to <code>false</code>. We don't need to have <code>created_at</code> and <code>updated_at</code> fields in the <code>projects</code> table, so we'll set the <code>$timestamps</code> to <code>false</code>.</p>
<p>Set the <code>Task</code> model's <code>$fillable</code> fields, <code>project()</code> relationship method, and <code>created</code> accessor. An <a target="_blank" href="https://laravel.com/docs/10.x/eloquent-mutators#defining-an-accessor">accessor</a> in Laravel is like a function between the database and your code, that can access the already fetched database record and modify it.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">HasFactory</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Relations</span>\<span class="hljs-title">BelongsTo</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">SoftDeletes</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">HasFactory</span>, <span class="hljs-title">SoftDeletes</span>;

    <span class="hljs-keyword">protected</span> $table = <span class="hljs-string">'tasks'</span>;
    <span class="hljs-keyword">protected</span> $fillable = [
        <span class="hljs-string">'id'</span>, <span class="hljs-comment">// primary key, auto-increment, integer</span>
        <span class="hljs-string">'project_id'</span>, <span class="hljs-comment">// foreign key, integer</span>

        <span class="hljs-string">'priority'</span>, <span class="hljs-comment">// integer</span>
        <span class="hljs-string">'title'</span>, <span class="hljs-comment">// string</span>
        <span class="hljs-string">'description'</span>, <span class="hljs-comment">// text</span>
    ];
    <span class="hljs-keyword">protected</span> $appends = [
        <span class="hljs-string">'created'</span>,
    ];

    <span class="hljs-comment">// each task belongs to a single project</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">project</span>(<span class="hljs-params"></span>): <span class="hljs-title">BelongsTo</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;belongsTo(Project::class);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCreatedAttribute</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;created_at-&gt;diffForHumans();
    }
}
</code></pre>
<p>Above, in the <code>Task</code> model, there is an accessor called <code>created</code>. For having an accessor, we have the <code>created</code> field in the <code>$appends</code> array, and also a public function <code>getCreatedAttribute()</code>.</p>
<p>In <code>get**&lt;WordsInCamelCase&gt;**Attribute()</code> function there is logic that will run to modify the already fetched database record.</p>
<p>In our case the <code>getCreatedAttribute()</code> function will return a human-friendly and readable time difference between the current time and the given time. For example, <em>"3 mins ago"</em> or <em>"4 months ago"</em>.</p>
<p>Now that the models are ready, let's set up the migrations.</p>
<p>First, set up a migration for the <code>projects</code> table:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Migrations</span>\<span class="hljs-title">Migration</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Schema</span>\<span class="hljs-title">Blueprint</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Schema</span>;

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Migration</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        Schema::create(<span class="hljs-string">'projects'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();
            $table-&gt;string(<span class="hljs-string">'name'</span>);
        });
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        Schema::dropIfExists(<span class="hljs-string">'projects'</span>);
    }
};
</code></pre>
<p>Then set up a migration for the <code>tasks</code> table:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Migrations</span>\<span class="hljs-title">Migration</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Schema</span>\<span class="hljs-title">Blueprint</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Schema</span>;

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Migration</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        Schema::create(<span class="hljs-string">'tasks'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();

            $table-&gt;foreignId(<span class="hljs-string">'project_id'</span>)-&gt;nullable()-&gt;constrained();

            $table-&gt;integer(<span class="hljs-string">'priority'</span>);
            $table-&gt;string(<span class="hljs-string">'title'</span>);
            $table-&gt;text(<span class="hljs-string">'description'</span>)-&gt;nullable();

            $table-&gt;timestamps();
            $table-&gt;softDeletes();
        });
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-comment">// drop existing foreign keys</span>
        Schema::table(<span class="hljs-string">'tasks'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            <span class="hljs-keyword">if</span> (Schema::hasColumn(<span class="hljs-string">'tasks'</span>, <span class="hljs-string">'project_id'</span>)) {
                $table-&gt;dropForeign([<span class="hljs-string">'project_id'</span>]);
            }
        });

        <span class="hljs-comment">// drop the table</span>
        Schema::dropIfExists(<span class="hljs-string">'tasks'</span>);
    }
};
</code></pre>
<p>The <code>tasks</code> table has a foreign key <code>project_id</code>, which is a reference to the <code>projects</code> table. So it's a good practice to update the <code>down()</code> method too, to be sure that the <code>project_id</code> foreign will be dropped before dropping the actual <code>projects</code> table.</p>
<p>There is also a <code>priority</code> field, which will be a non-nullable natural number for ordering the tasks. And optionally, you can add a soft deletion feature to the <code>Task</code> model.</p>
<h2 id="heading-how-to-create-seeders">How to Create Seeders</h2>
<p>Now we need to add dummy data to the <code>projects</code> and <code>tasks</code> tables. To seed some data in the database, you can use Laravel <em>seeders</em>. This allows you to create dummy data to use in your database. </p>
<p>If you want to read more about how this works, you can <a target="_blank" href="https://laravel.com/docs/10.x/seeding">check out the docs here</a>.</p>
<p>Laravel provides a way to generate those files by using <code>make:seeder</code> artisan command:</p>
<pre><code class="lang-shell">php artisan make:seeder ProjectsSeeder
php artisan make:seeder TasksSeeder
</code></pre>
<p>So with the above commands, you'll have <code>database/seeders/ProjectsSeeder.php</code> and <code>database/seeders/TasksSeeder.php</code> files created.</p>
<p>At first, you'll need to set up the <code>ProjectsSeeder</code> to add a few projects to the <code>projects</code> table. Then you can set up the <code>TasksSeeder</code> to add tasks to the <code>tasks</code> table.</p>
<p>As I mentioned at the beginning, each task will belong to a specific project. From a relational database perspective, this means that each entry in the <code>tasks</code> table will link to a specific entry in the <code>projects</code> table. Here's the importance of having a <code>project_id</code> foreign key in the <code>tasks</code> table to be able to relate each task to a specific project as well as retrieve the specific project's tasks.</p>
<p>You can imagine the database structure by looking at the following visuals:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/laravel_react_tasklist-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>generated by PHPStorm IDE</em></p>
<p>Using the example below, you can generate 3 projects:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Seeders</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Project</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProjectsSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">1</span>; $i &lt;= <span class="hljs-number">3</span>; $i++) {
            Project::create([
                <span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">"Project <span class="hljs-subst">$i</span>"</span>,
            ]);
        }
    }
}
</code></pre>
<p>Next, set up <code>TasksSeeder</code>. You'll run all the seeder files after setting them up, and they will run one by one. That being said, at this point your <code>ProjectsSeeder</code> is ready to create a few projects.</p>
<p>By imagining it, the next step will be generating the tasks, each of them will have a reference to one of the already existing projects by its <code>project_id</code> field.</p>
<p>Using the example below, you can generate 10 projects:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Seeders</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Project</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Task</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TasksSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        $project_ids = Project::all()-&gt;pluck(<span class="hljs-string">'id'</span>)-&gt;toArray();

        $now = now();
        $tasks = [];
        $project_priorities = [];
        <span class="hljs-keyword">foreach</span> ($project_ids <span class="hljs-keyword">as</span> $project_id) {
            $project_priorities[$project_id] = <span class="hljs-number">0</span>;
        }

        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">1</span>; $i &lt;= <span class="hljs-number">10</span>; $i++) {
            $project_id = $project_ids[array_rand($project_ids)];
            $project_priorities[$project_id]++;

            $tasks[] = [
                <span class="hljs-string">'project_id'</span> =&gt; $project_id,
                <span class="hljs-string">'priority'</span> =&gt; $project_priorities[$project_id],
                <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">"Task "</span> . $project_priorities[$project_id],
                <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">"Description for Task "</span> . $project_priorities[$project_id],

                <span class="hljs-string">'created_at'</span> =&gt; $now,
                <span class="hljs-string">'updated_at'</span> =&gt; $now,
            ];
        }

        Task::insert($tasks);
    }
}
</code></pre>
<p>The above code just grabs all the project IDs, then randomly chooses a project for each task. In the end, it inserts all the tasks into the <code>tasks</code> table. </p>
<p>As you may have noticed, we're inserting <code>$tasks</code> into the <code>tasks</code> table using the <code>insert()</code> static function, which allows us to insert all the items into the database table with a single query. </p>
<p>But it has a downside as well: it doesn't manage <code>created_at</code> and <code>updated_at</code> fields. That's why there's a need to set up those fields manually by assigning them the same <code>$now</code> timestamp.</p>
<p>Now, when you have all the seeders ready, you need to register them into the <code>DatabaseSeeder</code>.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Seeders</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;call([
            ProjectsSeeder::class,
            TasksSeeder::class,
        ]);
    }
}
</code></pre>
<h2 id="heading-how-to-connect-to-the-mysql-database">How to Connect to the MySQL Database</h2>
<p>Before running migrations and seeds, create a MySQL database and set up the appropriate credentials in the <code>.env</code> file. If there is not a <code>.env</code>, then create it and paste the <code>.env.example</code> file's content into it.</p>
<p>After setting up the database credentials, you'll have these kinds of environment variables:</p>
<pre><code class="lang-env">DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE="&lt;DATABASE_NAME&gt;"
DB_USERNAME="&lt;USERNAME&gt;"
DB_PASSWORD="&lt;PASSWORD&gt;"
</code></pre>
<p>After setting up environment variables, optimize the cache:</p>
<pre><code class="lang-shell">php artisan optimize
</code></pre>
<p>Now you'll be able to create <code>projects</code> and <code>tasks</code> tables in the MySQL database, setup their structure, and add initial records with a single command:</p>
<pre><code class="lang-shell">php artisan migrate:fresh --seed
</code></pre>
<p>In the above command, the <code>migrate:fresh</code> argument will drop all tables from the database. Then it will execute the <code>migrate</code> command, which will run your migrations to create <code>projects</code> and <code>tasks</code> tables appropriately. </p>
<p>With the <code>--seed</code> argument, it will run <code>ProjectsSeeder</code> and <code>TasksSeeder</code> after the migrations. That being said, it will empty your database for you, and will create all the tables and fill all the necessary dummy data.</p>
<p>After running the command, you'll have these kinds of database records:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-74.png" alt="Image" width="600" height="400" loading="lazy">
<em>A screenshot from the PHPStorm IDE</em></p>
<h2 id="heading-service-injection">Service Injection</h2>
<p>Now let's create a controller and a service classes to manage all the task features, such as listing, creating, updating, deleting, and reordering the tasks.</p>
<p>At first, use the below command to generate a controller.</p>
<pre><code class="lang-shell">php artisan make:controller TaskController
</code></pre>
<p>In order not to place all the code in the controller, you can keep only the main logic in it, and move the other logic implementations to another class file. </p>
<p>Those classes are generally called <em>services</em>, and using service implementations in a controller method is called <strong>service injection</strong> (it comes from the term <em>dependency injection</em>).</p>
<p>One of the main advantages of using services is that it helps you create a maintainable codebase.</p>
<p>You can inject your service class into the controller's construction method as an argument, so after each controller execution (when a controller's <code>__construct()</code> method runs) you can initialize an object of service. This means that you can access your service's functions right in your controller.</p>
<p>Now, let's create two separate service classes, which will be used in the <code>TaskController</code>.</p>
<p>Manually create a <code>app/Services/ProjectService.php</code> service class, which will be responsible for the project-related logic.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Project</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Collection</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProjectService</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAll</span>(<span class="hljs-params"></span>): <span class="hljs-title">Collection</span>
    </span>{
        <span class="hljs-keyword">return</span> Project::all();
    }
}
</code></pre>
<p>The second service class will be the <code>app/Services/TaskService.php</code>, which will be responsible for doing task manipulations:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Task</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">DB</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskService</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">list</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $projectId</span>)
    </span>{
        <span class="hljs-keyword">return</span> Task::with(<span class="hljs-string">'project'</span>)-&gt;where(<span class="hljs-string">'project_id'</span>, $projectId)
            -&gt;orderBy(<span class="hljs-string">'priority'</span>)-&gt;get();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getById</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>)
    </span>{
        <span class="hljs-keyword">return</span> Task::where(<span class="hljs-string">'id'</span>, $id)-&gt;with(<span class="hljs-string">'project'</span>)-&gt;first();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">$data</span>): <span class="hljs-title">void</span>
    </span>{
        $count = Task::where(<span class="hljs-string">'project_id'</span>, $data[<span class="hljs-string">'project_id'</span>])-&gt;count();
        $data[<span class="hljs-string">'priority'</span>] = $count + <span class="hljs-number">1</span>;

        Task::create($data);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id, <span class="hljs-keyword">array</span> $data</span>): <span class="hljs-title">void</span>
    </span>{
        $task = <span class="hljs-keyword">$this</span>-&gt;getById($id);
        <span class="hljs-keyword">if</span> (!$task) { <span class="hljs-keyword">return</span>; }

        $task-&gt;update($data);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">delete</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">void</span>
    </span>{
        $task = <span class="hljs-keyword">$this</span>-&gt;getById($id);
        <span class="hljs-keyword">if</span> (!$task) { <span class="hljs-keyword">return</span>; }

        $task-&gt;delete();

        $tasks = Task::where(<span class="hljs-string">'project_id'</span>, $task-&gt;project_id)
            -&gt;where(<span class="hljs-string">'priority'</span>, <span class="hljs-string">'&gt;'</span>, $task-&gt;priority)-&gt;get();
        <span class="hljs-keyword">if</span> ($tasks-&gt;isEmpty()) {
            <span class="hljs-keyword">return</span>;
        }

        $when_then = <span class="hljs-string">""</span>;
        $where_in = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">foreach</span> ($tasks <span class="hljs-keyword">as</span> $task) {
            $when_then .= <span class="hljs-string">"WHEN "</span>.$task-&gt;id
                .<span class="hljs-string">" THEN "</span>.($task-&gt;priority - <span class="hljs-number">1</span>).<span class="hljs-string">" "</span>;
            $where_in .= $task-&gt;id.<span class="hljs-string">","</span>;
        }

        $table_name = (<span class="hljs-keyword">new</span> Task())-&gt;getTable();
        $bulk_update_query = <span class="hljs-string">"UPDATE `"</span>.$table_name
            .<span class="hljs-string">"` SET `priority` = (CASE `id` "</span>.$when_then.<span class="hljs-string">"END)"</span>
            .<span class="hljs-string">" WHERE `id` IN("</span>.substr($where_in, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>).<span class="hljs-string">");"</span>;

        <span class="hljs-comment">// there is no way to be SQL injected here</span>
        <span class="hljs-comment">// because all the values are not provided by the user</span>
        DB::update($bulk_update_query);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reorder</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $project_id, <span class="hljs-keyword">int</span> $start, <span class="hljs-keyword">int</span> $end</span>): <span class="hljs-title">void</span>
    </span>{
        $items = Task::where(<span class="hljs-string">'project_id'</span>, $project_id)
            -&gt;orderBy(<span class="hljs-string">'priority'</span>)-&gt;pluck(<span class="hljs-string">'priority'</span>, <span class="hljs-string">'id'</span>)-&gt;toArray();

        <span class="hljs-keyword">if</span> ($start &gt; count($items) || $end &gt; count($items)) {
            <span class="hljs-keyword">return</span>;
        }

        $ids = [];
        $priorities = [];
        <span class="hljs-keyword">foreach</span> ($items <span class="hljs-keyword">as</span> $id =&gt; $priority) {
            $ids[] = $id;
            $priorities[] = $priority;
        }

        $out_priority = array_splice($priorities, $start - <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
        array_splice($priorities, $end - <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, $out_priority);

        $when_then = <span class="hljs-string">""</span>;
        $where_in = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">foreach</span> ($priorities <span class="hljs-keyword">as</span> $out_k =&gt; $out_v) {
            $id = $ids[$out_v - <span class="hljs-number">1</span>];
            $when_then .= <span class="hljs-string">"WHEN "</span>.$id.<span class="hljs-string">" THEN "</span>.($out_k + <span class="hljs-number">1</span>).<span class="hljs-string">" "</span>;
            $where_in .= $id.<span class="hljs-string">","</span>;
        }

        $table_name = (<span class="hljs-keyword">new</span> Task())-&gt;getTable();
        $bulk_update_query = <span class="hljs-string">"UPDATE `"</span>.$table_name
            .<span class="hljs-string">"` SET `priority` = (CASE `id` "</span>.$when_then.<span class="hljs-string">"END)"</span>
            .<span class="hljs-string">" WHERE `id` IN("</span>.substr($where_in, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>).<span class="hljs-string">")"</span>
            .<span class="hljs-string">" AND `deleted_at` IS NULL;"</span>; <span class="hljs-comment">// soft delete</span>

        DB::update($bulk_update_query);
    }
}
</code></pre>
<p>In the above <code>TaskService</code> class, you'll use the following functions in the <code>TaskController</code>.</p>
<ul>
<li><strong>list</strong>: fetches tasks for a given project ID, including the related project, and orders them by <em>priority</em>.</li>
<li><strong>getById</strong>: retrieves a specific task by its ID, including the related project.</li>
<li><strong>store</strong>: stores a new task, calculating the <em>priority</em> based on existing tasks for the same project.</li>
<li><strong>update</strong>: updates an existing task by its ID.</li>
<li><strong>delete</strong>: deletes a task by its ID and adjusts the priorities of remaining tasks in the same project.</li>
<li><strong>reorder</strong>: changes the priorities of tasks within a project, (handles soft delete as well with <code>deleted_at IS NULL</code>).</li>
</ul>
<h2 id="heading-web-and-api-routes-in-laravel">Web and API Routes in Laravel</h2>
<p>Now you can add routes to test the methods you've already written. In this project, we have a stateless app on the frontend which requests API routes for getting JSON data, so it will follow RESTful principles (GET, POST, PUT, DELETE methods). Only the initial HTML page will be retrieved as a whole web page.</p>
<p>So now, set up a route in <code>routes/web.php</code> for the initial single-page:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Route</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>\<span class="hljs-title">TaskController</span>;

Route::group([<span class="hljs-string">'prefix'</span> =&gt; <span class="hljs-string">'/'</span>, <span class="hljs-string">'as'</span> =&gt; <span class="hljs-string">'tasks.'</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    Route::get(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'index'</span>])-&gt;name(<span class="hljs-string">'index'</span>);
});
</code></pre>
<p>Set up API routes in <code>routes/api.php</code> like this:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Route</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>\<span class="hljs-title">TaskController</span>;

Route::group([<span class="hljs-string">'prefix'</span> =&gt; <span class="hljs-string">'/tasks'</span>, <span class="hljs-string">'as'</span> =&gt; <span class="hljs-string">'tasks.'</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    Route::get(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'list'</span>]);
    Route::get(<span class="hljs-string">'/{id}'</span>, [TaskController::class, <span class="hljs-string">'get'</span>])
        -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'[1-9][0-9]*'</span>);
    Route::post(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'store'</span>]);
    Route::put(<span class="hljs-string">'/{id}'</span>, [TaskController::class, <span class="hljs-string">'update'</span>])
        -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'[1-9][0-9]*'</span>);
    Route::delete(<span class="hljs-string">'/{id}'</span>, [TaskController::class, <span class="hljs-string">'delete'</span>])
        -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'[1-9][0-9]*'</span>);
    Route::put(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'reorder'</span>]);
});
</code></pre>
<p>We have all the API routes in the <code>routes/api.php</code> instead of the <code>routes/web.php</code> because in the <em>web.php</em> file, all the routes by default are <a target="_blank" href="https://laravel.com/docs/10.x/routing#csrf-protection">CSRF protected</a>. So, in a stateless app, usually you won't need that – that's why <em>api.php</em> was invented in Laravel.</p>
<p>As you can see, there is a <em>"task"</em> prefix for all API routes. It's optional to have a prefix, but it's just a good practice. And for the specific API routes, there are regex validations for accepting only natural numbers as project IDs.</p>
<p>Don't forget to refresh route caches after the above changes. It's important to remember that Laravel (version 10 in this case) reads routes from the cached <code>bootstrap/cache/routes-v7.php</code> file, and they won't be updated automatically right after your changes. It just generates one if it hasn't cached yet.</p>
<p>Use the below command to refresh Laravel caches as well as the route caches:</p>
<pre><code class="lang-shell">php artisan optimize
</code></pre>
<h2 id="heading-validation-requests-in-laravel">Validation Requests in Laravel</h2>
<p>Before writing controller methods, you'll need to add some validation request files. You can do that manually or by just using the <code>artisan</code> command:</p>
<pre><code class="lang-shell">php artisan make:request Task/CreateTaskRequest
php artisan make:request Task/ListTasksRequest
php artisan make:request Task/ReorderTasksRequest
php artisan make:request Task/UpdateTaskRequest
</code></pre>
<p>After creating them, you'll need to set up validation rules for each request.</p>
<p>Validation rules in Laravel are a way to describe how to expect to get incoming HTTP data. If the data matches the rules, then it passes the validation – otherwise, Laravel will return a failure.</p>
<p>Laravel provides a <a target="_blank" href="https://laravel.com/docs/10.x/validation#available-validation-rules">set of rules</a> you can use to check incoming data. A field of the incoming request can have multiple rules. </p>
<p>One way to write validation rules for a single field is concatenating those rules by a "|" character.</p>
<p>Below are the validation rules for creating a new task:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'project_id'</span> =&gt; <span class="hljs-string">'required|integer|exists:projects,id'</span>,
    <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'required|string|max:255'</span>,
    <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'nullable|string'</span>,
];
</code></pre>
<p>Below is the validation rule for listing project tasks:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'project_id'</span> =&gt; <span class="hljs-string">'required|integer|exists:projects,id'</span>,
];
</code></pre>
<p>Below are the validation rules for tasks reordering:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'project_id'</span> =&gt; <span class="hljs-string">'required|integer|exists:projects,id'</span>,
    <span class="hljs-string">'start'</span> =&gt; <span class="hljs-string">'required|integer'</span>,
    <span class="hljs-string">'end'</span> =&gt; <span class="hljs-string">'required|integer|different:start'</span>,
];
</code></pre>
<p>Below are the validation rules for updating a task:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'required|string|max:255'</span>,
    <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'nullable|string'</span>,
];
</code></pre>
<p>Don't forget to return <code>true</code> in the <code>authorize()</code> method in all validation classes:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">authorize</span>(<span class="hljs-params"></span>): <span class="hljs-title">bool</span>
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>This function is usually designed to determine if the user is authorized to make the request. As we don't use authentication as well as authorization stuff in the app, it should return <code>true</code> for all the cases.</p>
<h2 id="heading-how-to-write-a-controller-that-uses-services">How to Write a Controller that Uses Services</h2>
<p>As the last step in the backend part, it's time to write controller methods for each API route, which will use service functions.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">CreateTaskRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">ListTasksRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">ReorderTasksRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">UpdateTaskRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>\<span class="hljs-title">ProjectService</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>\<span class="hljs-title">TaskService</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">JsonResponse</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">protected</span> ?TaskService $taskService = <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">TaskService $taskService</span>)
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService = $taskService;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
    </span>{
        $projects = (<span class="hljs-keyword">new</span> ProjectService())-&gt;getAll();

        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'tasks.index'</span>, [
            <span class="hljs-string">'projects'</span> =&gt; $projects,
        ]);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">list</span>(<span class="hljs-params">ListTasksRequest $request</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        $tasks = <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;list($request-&gt;get(<span class="hljs-string">'project_id'</span>));

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'tasks'</span> =&gt; $tasks,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Tasks retrieved successfully."</span>,
        ]); <span class="hljs-comment">// 200</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">CreateTaskRequest $request</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;store($request-&gt;all());

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task created successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        $task = <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;getById($id);

        <span class="hljs-keyword">if</span> ($task) {
            <span class="hljs-keyword">return</span> response()-&gt;json([
                <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
                <span class="hljs-string">'task'</span> =&gt; $task,
                <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task retrieved successfully."</span>,
            ]); <span class="hljs-comment">// 200</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> response()-&gt;json([
                <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">false</span>,
                <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task not found!"</span>,
            ], <span class="hljs-number">404</span>);
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params">UpdateTaskRequest $request, <span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;update($id, $request-&gt;all());

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task updated successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">delete</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;delete($id);

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task deleted successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reorder</span>(<span class="hljs-params">ReorderTasksRequest $request</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;reorder(
            $request-&gt;get(<span class="hljs-string">'project_id'</span>),
            $request-&gt;get(<span class="hljs-string">'start'</span>),
            $request-&gt;get(<span class="hljs-string">'end'</span>)
        );

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Tasks reordered successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }
}
</code></pre>
<p>As you can see in the <code>TaskController:</code></p>
<ul>
<li><code>TaskService</code> is injected into the constructor method as an argument. In the constructor body, an instance of the <code>TaskService</code> class is created, and the <code>$taskService</code> property is initialized. So in the custom methods, you'll be able to access that <code>$taskService</code> and its functions.</li>
<li>The <code>index</code> method is for returning the HTML.</li>
<li>All the other custom methods ( <code>list</code>, <code>store</code>, <code>get</code>, <code>update</code>, <code>delete</code>, <code>reorder</code>) are using the <code>TaskService</code> functions through the already initialized <code>$taskService</code> property. So, all the logic implementation goes to the service, and this way, you just call a service function and return the response.</li>
</ul>
<h2 id="heading-how-to-test-the-api-routes">How to Test the API Routes</h2>
<p>At this point, you can test the API routes by requesting them via Postman or any similar tool. Just run (or rerun) the backend:</p>
<pre><code class="lang-shell">php artisan serve
</code></pre>
<p>Here's the published <a target="_blank" href="https://documenter.getpostman.com/view/1747137/2s9YsJArg7">Postman collection</a> with all the requests.</p>
<p>Instead of using Postman, you can use a command line tool such as <em>curl</em> right from your terminal.</p>
<p>Below are all the sample commands that you can run to test out the API routes:</p>
<ul>
<li>Create a new task for a specific project:</li>
</ul>
<pre><code class="lang-shell">curl --location '127.0.0.1:8000/api/tasks?project_id=1' \
--header 'Content-Type: application/json' \
--data '{
    "title": "Title",
    "description": "Description"
}'
</code></pre>
<ul>
<li>List project tasks:</li>
</ul>
<pre><code class="lang-shell">curl --location 'http://127.0.0.1:8000/api/tasks?project_id=1'
</code></pre>
<ul>
<li>Get a task by ID:</li>
</ul>
<pre><code class="lang-shell">curl --location 'http://127.0.0.1:8000/api/tasks/1'
</code></pre>
<ul>
<li>Update a task by ID:</li>
</ul>
<pre><code class="lang-shell">curl --location --request PUT 'http://127.0.0.1:8000/api/tasks/11' \
--header 'Content-Type: application/json' \
--data '{
    "title": "Title edited",
    "description": "Description edited"
}'
</code></pre>
<ul>
<li>Reorder project tasks:</li>
</ul>
<pre><code class="lang-shell">curl --location --request PUT 'http://127.0.0.1:8000/api/tasks' \
--header 'Content-Type: application/json' \
--data '{
    "project_id": "1",
    "start": "1",
    "end": "2"
}'
</code></pre>
<ul>
<li>Delete a task by ID:</li>
</ul>
<pre><code class="lang-shell">curl --location --request DELETE 'http://127.0.0.1:8000/api/tasks/11'
</code></pre>
<p>In the below screenshot, there is an example of getting project tasks by its ID using the <em>curl</em> command (at the bottom right):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-84.png" alt="Image" width="600" height="400" loading="lazy">
<em>An example of using <strong>curl</strong> command to retrieve the existing tasks from the database</em></p>
<h2 id="heading-the-frontend-how-to-install-the-packages">The Frontend: How to Install the Packages</h2>
<p>Now it's time to switch to the frontend. We'll use TypeScript for React.js. After completing this part, you'll be able to integrate React.js (with Vite) in your Laravel app.</p>
<p>First, make sure you have Node.js version 18 or above by using this command:</p>
<pre><code class="lang-shell">node -v
</code></pre>
<p>Install these necessary npm packages:</p>
<pre><code class="lang-shell">npm i react-dom dotenv react-beautiful-dnd react-responsive-modal react-toastify @vitejs/plugin-react
</code></pre>
<ul>
<li><strong><code>react-dom</code></strong> is a library from the React team for rendering React components in the DOM (Document Object Model)</li>
<li><strong><code>dotenv</code></strong> is for loading environment variables from the <code>.env</code> file into the process environment</li>
<li><strong><code>react-beautiful-dnd</code></strong> is a library from Atlassian for creating drag-and-drop interfaces with animations</li>
<li><strong><code>react-responsive-modal</code></strong> is for creating simple and responsive modal dialogs</li>
<li><strong><code>react-toastify</code></strong> is for displaying notifications or toasts</li>
<li><strong><code>@vitejs/plugin-react</code></strong> is a plugin for the Vite build tool that enables seamless integration of React with fast development and optimized production builds</li>
</ul>
<p>Install the development dependencies with this command:</p>
<pre><code class="lang-shell">npm i -D @types/react-dom @types/react-beautiful-dnd
</code></pre>
<ul>
<li><strong><code>@types/react-dom</code></strong> is TypeScript type definitions for the <code>react-dom</code> package</li>
<li><strong><code>@types/react-beautiful-dnd</code></strong> is TypeScript type definitions for the <code>react-beautiful-dnd</code> package</li>
</ul>
<h2 id="heading-how-to-configure-vitejs">How to Configure Vite.js</h2>
<p>As Laravel v10 already has <code>vite.config.js</code>, you'll want to set up any React-related stuff there. Or if you still don't have this file, create one like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>;
<span class="hljs-keyword">import</span> laravel <span class="hljs-keyword">from</span> <span class="hljs-string">'laravel-vite-plugin'</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dotenv/config'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
    <span class="hljs-attr">build</span>: {
        <span class="hljs-attr">minify</span>: process.env.APP_ENV === <span class="hljs-string">'production'</span> ? <span class="hljs-string">'esbuild'</span> : <span class="hljs-literal">false</span>,
        <span class="hljs-attr">cssMinify</span>: process.env.APP_ENV === <span class="hljs-string">'production'</span>,
    },
    <span class="hljs-attr">plugins</span>: [
        laravel({
            <span class="hljs-attr">input</span>: [<span class="hljs-string">'resources/react/app.tsx'</span>],
            <span class="hljs-attr">refresh</span>: <span class="hljs-literal">true</span>,
        }),
        react(),
    ],
});
</code></pre>
<p>As you can see in the Vite configuration file, there is a reference to the <code>resources/react/app.tsx</code>, which will be the entry point for Laravel to use React resources.</p>
<p>For the initial HTML page, create a <code>resources/views/tasks/index.blade.php</code> blade file, so all the frontend assets will be injected there in the <code>div</code> with ID <code>app</code>:</p>
<pre><code class="lang-php">&lt;!DOCTYPE html&gt;
&lt;html lang=<span class="hljs-string">"{{ str_replace('_', '-', app()-&gt;getLocale()) }}"</span>&gt;
&lt;head&gt;
    &lt;meta charset=<span class="hljs-string">"utf-8"</span>&gt;
    &lt;meta name=<span class="hljs-string">"viewport"</span> content=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;
    &lt;title&gt;{{ config(<span class="hljs-string">"app.name"</span>) }}&lt;/title&gt;
    &lt;link rel=<span class="hljs-string">"shortcut icon"</span> href=<span class="hljs-string">"{{ asset('favicon.ico') }}"</span> /&gt;
    &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"https://fonts.googleapis.com/css?family=Montserrat"</span>&gt;
    &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"https://fonts.googleapis.com/icon?family=Material+Icons"</span>&gt;
    @viteReactRefresh
    @vite(<span class="hljs-string">'resources/react/app.tsx'</span>)
&lt;/head&gt;
&lt;body&gt;
&lt;div id=<span class="hljs-string">"app"</span> data-projects=<span class="hljs-string">"{{ json_encode(<span class="hljs-subst">$projects</span>) }}"</span>&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>As you can see in the blade file, there is a <code>$projects</code> variable passed from the backend. It's the whole project data that will be used to filter tasks in the frontend.</p>
<h2 id="heading-reactjs-initial-integration">React.js – Initial Integration</h2>
<p>In this article, we'll just have a basic React.js app working with Laravel.</p>
<p>At first, it's a good idea to delete unnecessary resources, like default <code>resources/css</code> and <code>resources/js</code> directories.</p>
<p>Create a <code>resources/react/app.tsx</code> file like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/client'</span>;
<span class="hljs-keyword">import</span> Main <span class="hljs-keyword">from</span> <span class="hljs-string">"./Main"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./index.css'</span>

ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'app'</span>)).render(
    &lt;Main /&gt;
);
</code></pre>
<p>So, the <code>resources/react</code> folder will be the root directory for all the upcoming React stuff.</p>
<p>Create an <code>index.css</code> with some temporary content:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.test-class</span> {
  <span class="hljs-attribute">color</span>: red;
}
</code></pre>
<p>Also create a <code>Main.tsx</code> with some temporary content:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            &lt;h2 className=<span class="hljs-string">"test-class"</span>&gt;React App&lt;/h2&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Main;
</code></pre>
<p>To check the result in the browser, make sure you have backend running and build the assets via the <code>vite</code> tool:</p>
<pre><code class="lang-shell">npm run build
</code></pre>
<p>Or, if you want to watch React file changes and automatically build assets, you can keep this command running:</p>
<pre><code class="lang-shell">npm run dev
</code></pre>
<p>The two <code>npm run</code> commands above refer to <code>vite</code>, which builds the assets.<br>You can see this by checking the <code>package.json</code> file, <em>"scripts"</em> field:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"vite build"</span>
}
</code></pre>
<p>Now you can open <em><a target="_blank" href="http://localhost:8000">http://localhost:8000</a></em> to see the initial rendered view:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-85.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot from browser</em></p>
<h2 id="heading-how-to-add-css">How to Add CSS</h2>
<p>Now, once you've set up Vite and have React integrated into your Laravel app, you can work on the React part.</p>
<p>We won't spend too much time on styles, so you can paste this CSS into your <code>index.css</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> { <span class="hljs-attribute">background-color</span>: whitesmoke; <span class="hljs-attribute">color</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.7</span>); <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Montserrat"</span>, sans-serif; <span class="hljs-attribute">cursor</span>: default; <span class="hljs-attribute">margin</span>: auto <span class="hljs-number">0</span>; }

<span class="hljs-comment">/* MODAL start */</span>
<span class="hljs-selector-class">.modal-content</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">flex-direction</span>: column; <span class="hljs-attribute">justify-content</span>: center; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">width</span>: <span class="hljs-number">500px</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; }
<span class="hljs-selector-class">.modal-header</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.modal-input-header</span> { <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>; }
<span class="hljs-selector-class">.modal-input</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.modal-textarea</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">resize</span>: vertical; }
<span class="hljs-selector-class">.modal-actions</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; }
<span class="hljs-selector-class">.modal-btn</span> { <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">cursor</span>: pointer; }
<span class="hljs-selector-class">.modal-btn-cancel</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e53e3e</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.modal-btn-cancel</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#c53030</span>; }
<span class="hljs-selector-class">.modal-btn-submit</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.modal-btn-submit</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4a5568</span>; }
<span class="hljs-selector-class">.modal-question</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-comment">/* MODAL end */</span>

<span class="hljs-comment">/* LEFT &amp; RIGHT SIDE start */</span>
<span class="hljs-selector-class">.left-side</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">50%</span>; <span class="hljs-attribute">float</span>: left; }
<span class="hljs-selector-class">.right-side</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">50%</span>; <span class="hljs-attribute">float</span>: left; }
<span class="hljs-comment">/* LEFT &amp; RIGHT SIDE end */</span>

<span class="hljs-comment">/* LEFT SIDE start */</span>
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.no-tasks</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">text-align</span>: center; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-item</span> { <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>; <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span>; <span class="hljs-attribute">min-height</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">min-width</span>: <span class="hljs-number">200px</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">list-style-type</span>: none; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-item-content</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-project</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">flex-direction</span>: column; <span class="hljs-attribute">justify-content</span>: center; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-project-name</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-time</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.8rem</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-title</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-actions</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-edit-btn</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>; <span class="hljs-attribute">cursor</span>: pointer; <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-edit-btn</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4a5568</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-delete-btn</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e53e3e</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>; <span class="hljs-attribute">cursor</span>: pointer; <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-delete-btn</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#c53030</span>; }
<span class="hljs-comment">/* LEFT SIDE end */</span>

<span class="hljs-comment">/* RIGHT SIDE start */</span>
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.projects</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.projects-select</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.no-project-selected</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.right-side-wrapper</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">flex-direction</span>: column; <span class="hljs-attribute">justify-content</span>: center; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-header</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-input-header</span> { <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-input</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-textarea</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">resize</span>: vertical; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-actions</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn</span> { <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">cursor</span>: pointer; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-cancel</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e53e3e</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-cancel</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#c53030</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-submit</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-submit</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4a5568</span>; }
<span class="hljs-comment">/* RIGHT SIDE end */</span>
</code></pre>
<p>Later you'll attach the <code>index.css</code> file in your main component.</p>
<h2 id="heading-a-service-for-the-api-requests">A Service for the API Requests</h2>
<p>As you did in backend, here in the frontend you also can move all the logic implementations into a different file, so your code will be more readable and maintainable. We can name that file <code>utils.ts</code>, as there will be utilities in it we need.</p>
<p>Before that, just create <code>axiosConfig.ts</code> for the global Axios configuration, which you'll use in <code>utils.ts</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> axios.create({ baseURL: <span class="hljs-string">'/api'</span> });
</code></pre>
<p>Using the above setup, you can be sure that all the HTTP requests will have the <code>/api</code> prefix.</p>
<p>For example, if you use <code>axiosConfig.get('/example')</code>, it will send a GET request to the <code>/api/example</code>. This is an optional configuration, but it's a recommended way to have non-repetitive code.</p>
<p>As you'll have a few use cases for sending HTTP requests to the server, you can have separate utilities file for those operations:</p>
<ul>
<li>Create a new task for a project</li>
<li>Update a task</li>
<li>List project's tasks</li>
<li>Delete a task</li>
<li>Reorder project's tasks</li>
</ul>
<p>So below is the <code>utils.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> axiosConfig <span class="hljs-keyword">from</span> <span class="hljs-string">'./axiosConfig'</span>;
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-toastify'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getErrorMessage = <span class="hljs-function">(<span class="hljs-params">error: unknown</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) <span class="hljs-keyword">return</span> error.message;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">String</span>(error)
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getTasks = <span class="hljs-keyword">async</span> (projectId) =&gt; {
    <span class="hljs-keyword">if</span> (!projectId) {
        toast.error(<span class="hljs-string">"Project is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.get(<span class="hljs-string">`/tasks?project_id=<span class="hljs-subst">${projectId}</span>`</span>);
        <span class="hljs-keyword">const</span> { success, tasks, message } = response.data;

        <span class="hljs-keyword">if</span> (success) {
            <span class="hljs-keyword">return</span> tasks;
        } <span class="hljs-keyword">else</span> {
            toast.error(message);
            <span class="hljs-keyword">return</span> [];
        }
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
        <span class="hljs-keyword">return</span> [];
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> reorderTasks = <span class="hljs-keyword">async</span> (projectId, start, end) =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.put(<span class="hljs-string">'/tasks'</span>, {
            project_id: projectId,
            start,
            end,
        });
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> editTask = <span class="hljs-keyword">async</span> (task) =&gt; {
    <span class="hljs-keyword">if</span> (!task.id) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!task.title) {
        toast.error(<span class="hljs-string">"Title is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.put(<span class="hljs-string">`/tasks/<span class="hljs-subst">${task.id}</span>`</span>, {
            title: task.title,
            description: task.description,
        });
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteTask = <span class="hljs-keyword">async</span> (id) =&gt; {
    <span class="hljs-keyword">if</span> (!id) {
        toast.error(<span class="hljs-string">"Invalid task!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.delete(<span class="hljs-string">`/tasks/<span class="hljs-subst">${id}</span>`</span>);
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createTask = <span class="hljs-keyword">async</span> (task, projectId) =&gt; {
    <span class="hljs-keyword">if</span> (!projectId) {
        toast.error(<span class="hljs-string">"Project is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">if</span> (!task.title) {
        toast.error(<span class="hljs-string">"Title is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.post(<span class="hljs-string">`/tasks?project_id=<span class="hljs-subst">${projectId}</span>`</span>, {
            title: task.title,
            description: task.description,
        });
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}
</code></pre>
<p>In the above file, you'll find the following functions:</p>
<ul>
<li><strong>getErrorMessage</strong>: Returns the error message if the input is an instance of Error – otherwise, converts it to a string.</li>
<li><strong>getTasks</strong>: Retrieves tasks for a given project ID using Axios. Displays an error toast if the project ID is missing or if the API request is unsuccessful.</li>
<li><strong>reorderTasks</strong>: Sends a PUT request to reorder tasks within a project based on start and end positions. Displays a success or error toast based on the API response.</li>
<li><strong>editTask</strong>: Sends a PUT request to update task information. Validates that the task has an ID and a title before making the request. Displays a success or error toast based on the API response.</li>
<li><strong>deleteTask</strong>: Sends a DELETE request to delete a task by its ID. Displays a success or error toast based on the API response.</li>
<li><strong>createTask</strong>: Sends a POST request to create a new task for a given project ID. Validates that the project ID is present, and the task has a title before making the request. Displays a success or error toast based on the API response.</li>
</ul>
<h2 id="heading-reactjs-components">React.js Components</h2>
<p>Now, since you have utilities ready, in the <code>resources/react/components</code> folder, you can create the components you need to use in your <code>Main.tsx</code>.</p>
<p>First, create <code>SelectProject.tsx</code>, which will be responsible for choosing the current project:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {getTasks} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SelectProject</span>(<span class="hljs-params">{projectId, projects, setProjectId, setTasks}</span>) </span>{
    <span class="hljs-keyword">const</span> selectProject = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> value = e.target.value;
        setProjectId(value);
        <span class="hljs-keyword">if</span> (value === <span class="hljs-string">''</span>) {
            setTasks([]);
        } <span class="hljs-keyword">else</span> {
            getTasks(value).then(<span class="hljs-function">(<span class="hljs-params">tasksData</span>) =&gt;</span> setTasks(tasksData));
        }
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"projects"</span>&gt;
            &lt;select className=<span class="hljs-string">"projects-select"</span>
                    value={projectId}
                    onChange={selectProject}&gt;
                &lt;option value=<span class="hljs-string">""</span> defaultValue&gt;Choose a project&lt;/option&gt;
                {projects.map(<span class="hljs-function">(<span class="hljs-params">project</span>) =&gt;</span> (
                    &lt;option key={project.id}
                            value={project.id}&gt;{project.name}&lt;/option&gt;
                ))}
            &lt;/select&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SelectProject;
</code></pre>
<p>The <code>SelectProject</code> component renders a dropdown menu allowing the user to select a project. When a project is selected, it updates the state with the selected project ID, fetches tasks for that project using the <code>getTasks</code> utility function, and updates the state with the retrieved tasks, providing dynamic interaction with project selection and task loading.</p>
<p>Then create <code>TaskList.tsx</code>, which will be responsible for rendering the project's tasks and for their drag and drop manipulations:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {DragDropContext, Draggable, Droppable} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-beautiful-dnd"</span>;
<span class="hljs-keyword">import</span> Task <span class="hljs-keyword">from</span> <span class="hljs-string">"./Task"</span>;
<span class="hljs-keyword">import</span> {reorderTasks} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-keyword">const</span> getItemStyle = <span class="hljs-function">(<span class="hljs-params">isDragging, draggableStyle</span>) =&gt;</span> ({
    background: isDragging ? <span class="hljs-string">'lightgreen'</span> : <span class="hljs-string">'grey'</span>,
    ...draggableStyle,
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TaskList</span>(<span class="hljs-params">{ tasks, setIsModalEditOpen, setModalEditTask, setIsModalDeleteOpen, setModalDeleteTaskId, projectId, setTasks }</span>)
</span>{
    <span class="hljs-keyword">const</span> handleDragEnd = <span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (!result.destination || result.destination.index === result.source.index) {
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> items = <span class="hljs-built_in">Array</span>.from(tasks);
        <span class="hljs-keyword">const</span> [reorderedItem] = items.splice(result.source.index, <span class="hljs-number">1</span>);
        items.splice(result.destination.index, <span class="hljs-number">0</span>, reorderedItem);
        reorderTasks(projectId, result.source.index + <span class="hljs-number">1</span>, result.destination.index + <span class="hljs-number">1</span>);

        setTasks(items);
    };

    <span class="hljs-keyword">return</span> (
        &lt;DragDropContext onDragEnd={handleDragEnd}&gt;
            &lt;Droppable droppableId=<span class="hljs-string">"droppable"</span>&gt;
                {<span class="hljs-function">(<span class="hljs-params">provided</span>) =&gt;</span> (
                    &lt;ul {...provided.droppableProps} ref={provided.innerRef}&gt;
                        {tasks.map(<span class="hljs-function">(<span class="hljs-params">task, index</span>) =&gt;</span> (
                            &lt;Draggable key={task.id.toString()} draggableId={task.id.toString()} index={index}&gt;
                                {<span class="hljs-function">(<span class="hljs-params">provided, snapshot</span>) =&gt;</span> (
                                    &lt;li ref={provided.innerRef}
                                        {...provided.draggableProps}
                                        {...provided.dragHandleProps}
                                        className=<span class="hljs-string">"task-item"</span>
                                        style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
                                    &gt;
                                        &lt;Task task={task}
                                              setIsModalEditOpen={setIsModalEditOpen}
                                              setModalEditTask={setModalEditTask}
                                              setIsModalDeleteOpen={setIsModalDeleteOpen}
                                              setModalDeleteTaskId={setModalDeleteTaskId}
                                        /&gt;
                                    &lt;/li&gt;
                                )}
                            &lt;/Draggable&gt;
                        ))}
                        {provided.placeholder}
                    &lt;/ul&gt;
                )}
            &lt;/Droppable&gt;
        &lt;/DragDropContext&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TaskList;
</code></pre>
<p>The <code>TaskList</code> component utilizes the <code>react-beautiful-dnd</code> library to implement a draggable task list. It renders a list of tasks, allowing users to drag and drop tasks to reorder them, with drag-and-drop functionality triggering a function (<code>handleDragEnd</code>) that updates the task order both visually and in the backend using the <code>reorderTasks</code> utility function.</p>
<p>Now, create <code>Task.tsx</code>, which will be responsible for a single Task from a list:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Task</span>(<span class="hljs-params">{ task, setIsModalEditOpen, setModalEditTask, setIsModalDeleteOpen, setModalDeleteTaskId }</span>)
</span>{
    <span class="hljs-keyword">const</span> handleEdit = <span class="hljs-function">() =&gt;</span> {
        setModalEditTask(task);
        setIsModalEditOpen(<span class="hljs-literal">true</span>);
    };

    <span class="hljs-keyword">const</span> handleDelete = <span class="hljs-function">() =&gt;</span> {
        setModalDeleteTaskId(task.id);
        setIsModalDeleteOpen(<span class="hljs-literal">true</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"task-item-content"</span>&gt;
            &lt;span className=<span class="hljs-string">"task-project"</span>&gt;
                &lt;span className=<span class="hljs-string">"task-project-name"</span>&gt;
                    {task.project.name}
                &lt;/span&gt;
                &lt;span className=<span class="hljs-string">"task-time"</span>&gt;
                    Created {task.created}
                &lt;/span&gt;
               &lt;/span&gt;
            &lt;span className=<span class="hljs-string">"task-title"</span>&gt;{task.title}&lt;/span&gt;
            &lt;div className=<span class="hljs-string">"task-actions"</span>&gt;
                &lt;button className=<span class="hljs-string">"task-edit-btn"</span>
                    onClick={handleEdit}&gt;Edit&lt;/button&gt;
                &lt;button className=<span class="hljs-string">"task-delete-btn"</span>
                    onClick={handleDelete}&gt;Delete&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Task;
</code></pre>
<p>The <code>Task</code> component represents a single task item. It displays task details including the project name, creation time, and title, and provides buttons to trigger actions such as editing and deleting the task, with corresponding handlers (<code>handleEdit</code> and <code>handleDelete</code>).</p>
<p>Next, create <code>AddTaskForm.tsx</code>, which will be responsible for the task form for adding tasks to the current selected project:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {createTask} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AddTaskForm</span>(<span class="hljs-params">{newTask, setNewTask, projectId, reloadTasks }</span>)
</span>{
    <span class="hljs-keyword">const</span> clearTaskCreate = <span class="hljs-function">() =&gt;</span> {
        setNewTask({title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});
    };
    <span class="hljs-keyword">const</span> submitTaskCreate = <span class="hljs-function">() =&gt;</span> {
        createTask(newTask, projectId).then(<span class="hljs-function">() =&gt;</span> {
            setNewTask({title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});
            reloadTasks();
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;h2 className=<span class="hljs-string">"add-task-header"</span>&gt;Add Task&lt;/h2&gt;

            &lt;h3 className=<span class="hljs-string">"add-task-header"</span>&gt;Title&lt;/h3&gt;
            &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
                   className=<span class="hljs-string">"add-task-input"</span>
                   onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setNewTask({
                       ...newTask,
                    title: e.target.value
                   })}
                   value={newTask.title}
            /&gt;
            &lt;h3 className=<span class="hljs-string">"add-task-input-header"</span>&gt;Description&lt;/h3&gt;
            &lt;textarea className=<span class="hljs-string">"add-task-textarea"</span>
                      onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setNewTask({
                          ...newTask,
                        description: e.target.value
                      })}
                      value={newTask.description || <span class="hljs-string">''</span>}
            /&gt;
            &lt;div className=<span class="hljs-string">"add-task-actions"</span>&gt;
                &lt;button className=<span class="hljs-string">"add-task-btn add-task-btn-cancel"</span>
                        onClick={clearTaskCreate}&gt;Clear
                &lt;/button&gt;
                &lt;button className=<span class="hljs-string">"add-task-btn add-task-btn-submit"</span>
                        onClick={submitTaskCreate}&gt;Add&lt;/button&gt;
            &lt;/div&gt;
        &lt;/&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AddTaskForm;
</code></pre>
<p>The <code>AddTaskForm</code> component provides a form for adding new tasks. It includes input fields for the task title and description, with buttons to clear the form or submit the task creation, and it utilizes the <code>createTask</code> utility function to handle the task creation process, triggering a reload of tasks upon success.</p>
<p>Then, create <code>ModalEdit.tsx</code>, which will be responsible for the modal popup for editing and submitting changes to a task:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Modal} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-responsive-modal"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {editTask} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalEdit</span>(<span class="hljs-params">{
    isModalEditOpen, setIsModalEditOpen, setModalEditTask,
    modalEditTask, reloadTasks
}</span>) </span>{
    <span class="hljs-keyword">const</span> submitTaskEdit = <span class="hljs-function">() =&gt;</span> {
        setIsModalEditOpen(<span class="hljs-literal">false</span>);
        editTask(modalEditTask).then(<span class="hljs-function">() =&gt;</span> {
            reloadTasks();
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;Modal open={isModalEditOpen} center
            onClose={<span class="hljs-function">() =&gt;</span> setIsModalEditOpen(<span class="hljs-literal">false</span>)}&gt;
            &lt;div className=<span class="hljs-string">"modal-content"</span>&gt;
                &lt;h2 className=<span class="hljs-string">"modal-header"</span>&gt;Edit Task&lt;/h2&gt;
                &lt;h3 className=<span class="hljs-string">"modal-input-header"</span>&gt;Title&lt;/h3&gt;
                &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span> value={modalEditTask.title}
                       className=<span class="hljs-string">"modal-input"</span>
                       onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setModalEditTask({
                           ...modalEditTask,
                        title: e.target.value
                       })}
                /&gt;
                &lt;h3 className=<span class="hljs-string">"modal-input-header"</span>&gt;Description&lt;/h3&gt;
                &lt;textarea className=<span class="hljs-string">"modal-textarea"</span>
                          onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setModalEditTask({
                              ...modalEditTask,
                            description: e.target.value
                          })}
                          value={modalEditTask.description || <span class="hljs-string">''</span>}
                /&gt;
                &lt;div className=<span class="hljs-string">"modal-actions"</span>&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-cancel"</span>
                            onClick={<span class="hljs-function">() =&gt;</span> setIsModalEditOpen(<span class="hljs-literal">false</span>)}
                    &gt;Close
                    &lt;/button&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-submit"</span>
                            onClick={submitTaskEdit}
                    &gt;Save&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/Modal&gt;
    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ModalEdit;
</code></pre>
<p>The <code>ModalEdit</code> component displays a modal for editing task details. It includes input fields for modifying the task title and description, and buttons to close the modal or save the changes, using the <code>editTask</code> utility function to handle the task editing process and triggering a reload of tasks upon successful editing.</p>
<p>Next, create <code>ModalDelete.tsx</code>, which will be responsible for submitting a task deletion:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Modal} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-responsive-modal"</span>;
<span class="hljs-keyword">import</span> {deleteTask} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalDelete</span>(<span class="hljs-params">{
    isModalDeleteOpen, setIsModalDeleteOpen,
    modalDeleteTaskId, reloadTasks
}</span>) </span>{
    <span class="hljs-keyword">const</span> submitTaskDelete = <span class="hljs-function">() =&gt;</span> {
        setIsModalDeleteOpen(<span class="hljs-literal">false</span>);
        deleteTask(modalDeleteTaskId).then(<span class="hljs-function">() =&gt;</span> {
            reloadTasks();
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;Modal open={isModalDeleteOpen} onClose={<span class="hljs-function">() =&gt;</span> setIsModalDeleteOpen(<span class="hljs-literal">false</span>)} center&gt;
            &lt;div className=<span class="hljs-string">"modal-content"</span>&gt;
                &lt;h2 className=<span class="hljs-string">"modal-header"</span>&gt;Delete Task&lt;/h2&gt;
                &lt;p className=<span class="hljs-string">"modal-question"</span>&gt;
                    Are you sure you want to <span class="hljs-keyword">delete</span> <span class="hljs-built_in">this</span> task?
                &lt;/p&gt;
                &lt;div className=<span class="hljs-string">"modal-actions"</span>&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-cancel"</span>
                            onClick={<span class="hljs-function">() =&gt;</span> setIsModalDeleteOpen(<span class="hljs-literal">false</span>)}
                    &gt;Cancel&lt;/button&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-submit"</span>
                            onClick={submitTaskDelete}
                    &gt;Yes&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/Modal&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ModalDelete;
</code></pre>
<p>The <code>ModalDelete</code> component displays a modal for confirming the deletion of a task. It provides options to either cancel the deletion or proceed with deleting the task, utilizing the <code>deleteTask</code> utility function and triggering a reload of tasks upon successful deletion.</p>
<p>And lastly, set up the <code>Main.tsx</code> by using the above-defined components.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {getTasks} <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"react-responsive-modal/styles.css"</span>;
<span class="hljs-keyword">import</span> { ToastContainer } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-toastify'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'react-toastify/dist/ReactToastify.css'</span>;
<span class="hljs-keyword">import</span> ModalEdit <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/ModalEdit"</span>;
<span class="hljs-keyword">import</span> ModalDelete <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/ModalDelete"</span>;
<span class="hljs-keyword">import</span> TaskList <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/TaskList"</span>;
<span class="hljs-keyword">import</span> SelectProject <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/SelectProject"</span>;
<span class="hljs-keyword">import</span> AddTaskForm <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/AddTaskForm"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [projectId, setProjectId] = useState(<span class="hljs-string">''</span>);
    <span class="hljs-keyword">const</span> projectsData = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'app'</span>).getAttribute(<span class="hljs-string">'data-projects'</span>);
    <span class="hljs-keyword">const</span> projects = <span class="hljs-built_in">JSON</span>.parse(projectsData);
    <span class="hljs-keyword">const</span> [tasks, setTasks] = useState([]);
    <span class="hljs-keyword">const</span> [isModalEditOpen, setIsModalEditOpen] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [modalEditTask, setModalEditTask] = useState({id: <span class="hljs-string">''</span>, title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});
    <span class="hljs-keyword">const</span> [isModalDeleteOpen, setIsModalDeleteOpen] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [modalDeleteTaskId, setModalDeleteTaskId] = useState(<span class="hljs-string">''</span>);
    <span class="hljs-keyword">const</span> [newTask, setNewTask] = useState({title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});

    <span class="hljs-keyword">const</span> reloadTasks = <span class="hljs-function">() =&gt;</span> {
        getTasks(projectId).then(<span class="hljs-function">(<span class="hljs-params">tasksData</span>) =&gt;</span> setTasks(tasksData));
    };

    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            &lt;ToastContainer autoClose={<span class="hljs-number">2000</span>} /&gt;
            &lt;ModalEdit isModalEditOpen={isModalEditOpen}
                       setIsModalEditOpen={setIsModalEditOpen}
                       modalEditTask={modalEditTask}
                       setModalEditTask={setModalEditTask}
                       reloadTasks={reloadTasks}
            /&gt;
            &lt;ModalDelete isModalDeleteOpen={isModalDeleteOpen}
                         setIsModalDeleteOpen={setIsModalDeleteOpen}
                         modalDeleteTaskId={modalDeleteTaskId}
                         reloadTasks={reloadTasks}
            /&gt;
            &lt;div className=<span class="hljs-string">"left-side"</span>&gt;
                {tasks.length &gt; <span class="hljs-number">0</span> ? (
                    &lt;TaskList tasks={tasks}
                              setIsModalEditOpen={setIsModalEditOpen}
                              setModalEditTask={setModalEditTask}
                              setIsModalDeleteOpen={setIsModalDeleteOpen}
                              setModalDeleteTaskId={setModalDeleteTaskId}
                              projectId={projectId}
                              setTasks={setTasks}
                    /&gt;
                ) : (
                    &lt;div className=<span class="hljs-string">"no-tasks"</span>&gt;
                        {projectId === <span class="hljs-string">''</span> ? (
                            &lt;p&gt;Choose a project to see its tasks.&lt;/p&gt;
                        ) : (
                            &lt;p&gt;This project has no tasks.&lt;/p&gt;
                        )}
                    &lt;/div&gt;
                )}
            &lt;/div&gt;
            &lt;div className=<span class="hljs-string">"right-side"</span>&gt;
                &lt;div className=<span class="hljs-string">"right-side-wrapper"</span>&gt;
                    &lt;SelectProject projectId={projectId}
                                   projects={projects}
                                   setProjectId={setProjectId}
                                   setTasks={setTasks}
                    /&gt;
                    {projectId === <span class="hljs-string">''</span> ? (
                        &lt;div className=<span class="hljs-string">"no-project-selected"</span>&gt;
                            &lt;p&gt;Please select a project.&lt;/p&gt;
                        &lt;/div&gt;
                    ) : (
                        &lt;AddTaskForm newTask={newTask}
                                     setNewTask={setNewTask}
                                     projectId={projectId}
                                     reloadTasks={reloadTasks}
                        /&gt;
                    )}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Main;
</code></pre>
<p>The <code>Main</code> component is a central component that managing project and task-related functionalities. It includes modals for editing and deleting tasks, a task list with dynamic updates, a project selection dropdown, and a form for adding new tasks, leveraging state management and utility functions for smooth user interaction.</p>
<h2 id="heading-final-results">Final Results</h2>
<p>At this point, all the components are ready to interact with each other. So you can build the frontend assets and run the server:</p>
<pre><code class="lang-shell">npm run build &amp;&amp; php artisan serve
</code></pre>
<p>By visiting <code>http://127.0.0.1:8000</code> you'll get this kind of result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/tasklist.gif" alt="Image" width="600" height="400" loading="lazy">
<em>GIF generated from a local working project</em></p>
<p><strong>That's it!</strong></p>
<p>Now you can easily integrate React.js into your Laravel app without using any additional Laravel tools (like Inertia). And as a result, you can continue to maintain your Laravel app to build more scalable APIs with its authentication and other stuff.</p>
<p>So, this can be just an example app for your next full-stack Laravel and React.js project.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>With this article, you built a single-page, full-stack Tasklist app using React.js (with TypeScript) with Vite.js as frontend technologies, Laravel as a backend framework, and <code>react-beautiful-dnd</code> package for having draggable items.<br>Now you know how to manually integrate React.js in your Laravel app and maintain it.</p>
<p>You can find the complete code of the project here on my <a target="_blank" href="https://github.com/boolfalse/laravel-react-tasklist"><strong>GitHub</strong>⭐</a>, where I actively publicize much of my work about various modern technologies.<br>For more information, you can visit my website: <a target="_blank" href="https://boolfalse.com/"><strong>boolfalse.com</strong></a></p>
<p>Feel free to share this article. 😇</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a GitHub Template Repository for Scaffolding with React, Vite, and TailwindCSS ]]>
                </title>
                <description>
                    <![CDATA[ Developers love productivity. When it comes to coding, we want to do things fast and we look out for opportunities to re-use things as much as possible. Say, you are getting started with a ReactJS project and want to use TailwindCSS for the same. The... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-github-template-repository-with-react-vite-and-tailwindcss/</link>
                <guid isPermaLink="false">66bdffd0422f318982ba47be</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Tue, 09 Jan 2024 18:04:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/github-template-vite-react-tailwind.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Developers love productivity. When it comes to coding, we want to do things fast and we look out for opportunities to re-use things as much as possible.</p>
<p>Say, you are getting started with a <code>ReactJS</code> project and want to use <code>TailwindCSS</code> for the same. The first time, it would be fine for you to create a project using the <code>ViteJS</code> tool, and then configure TailwindCSS on top of it.</p>
<p>But the next time (and many more times after that), if you want to start a new React project, would you like to repeat these same steps over and over? A clever developer wouldn't do that. Instead, they'd create a "template" and use it every time they needed something similar in the future.</p>
<p>In this article, we are going to learn how to create a <code>GitHub</code> template repository for scaffolding a new React project with Vite and TailwindCSS. The steps explained in this article will also help you to set up React using Vite, and configure TailwindCSS with it, even if you have reasons not to create the template repository. So, read on.</p>
<p>If you like to learn from video content as well, this article is also available as a video tutorial here: 🙂</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Zk2YJUvfsOA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-vite">What is Vite?</h2>
<p><a target="_blank" href="https://vitejs.dev/">Vite (aka ViteJS)</a> is a next generation frontend tooling system that helps developers get started with local development quickly and easily. It supports super fast hot module replacement (HMR) so that there's hardly any lag between changing the source code and seeing it rendered on the browser.</p>
<p>Vite is way faster in starting the dev server than its predecessors like create-react-app (CRA) which was a go-to option for scaffolding React applications. Vite supports JSX, TypeScript, and CSS out-of-the box. It creates optimized builds and manages dependencies in an efficient manner. </p>
<p>Vite comes with templates available for all modern web technologies like vanilla JavaScript/TypeScript, React, Vue, Preact, Lit, Svelte, Solid, and Qwik.</p>
<p>At this moment, Vite is the most viable tooling system available to get started with React development.</p>
<h2 id="heading-how-to-set-up-a-react-project-with-vite">How to Set Up a React Project with Vite</h2>
<p>To get started, make sure you have <code>Node.js</code> version 18+ installed. You can check this by executing the following command from your command prompt (terminal):</p>
<pre><code class="lang-bash">node -v
</code></pre>
<p>This will print the Node.js version you have installed. If you do not have Node.js installed or you have a lower version than v18, go ahead and download and install it from <a target="_blank" href="https://nodejs.org/en">here</a>.</p>
<p>You can use the <code>--template</code> option of the <code>vite</code> library to create a React project using the template. Just copy-paste the following command on your terminal and press enter to execute it:</p>
<pre><code class="lang-bash">npm create vite@latest your_app_name -- --template react
</code></pre>
<p>Note that you need to replace the <code>your_app_name</code> with the name of your project/application. The <code>vite</code> tool will create a directory with the same name with the generated source code under it.</p>
<p>Next, change directory to your project:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your_app_name
</code></pre>
<p>Now, install the dependencies using this command:</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>Once successful, run the app locally using the following command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Vite will run the app locally on the URL <code>http://localhost:5173</code> by default.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-16.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vite running the app locally at <code>http://localhost:5173</code></em></p>
<p>You can now open a browser tab and try the URL to see your React application up and running.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-17.png" alt="Image" width="600" height="400" loading="lazy">
<em>React app up and running</em></p>
<p>Congratulations! You have now successfully set up a React app with Vite. Feel free to make any changes to the <code>src/App.jsx</code> source code file to see the changes reflected instantly on the browser.</p>
<h2 id="heading-how-to-configure-tailwindcss-with-vite">How to Configure TailwindCSS with Vite</h2>
<p><a target="_blank" href="https://tailwindcss.com/">TailwindCSS</a> is a utility-first CSS framework that can help make you more productive with its rapid development cycle. It provides utility classes you can use to translate any design into markup effortlessly. </p>
<p>Tailwind works quite well with React, and the two have become a modern combo for building fast websites and web applications.</p>
<h3 id="heading-install-tailwindcss">Install TailwindCSS</h3>
<p>We will now install and configure TailwindCSS with the React application we have created so far with Vite. You can now stop the Vite server if it's running locally for you.</p>
<p>First, let's install <code>tailwindcss</code>, <code>postcss</code>, and <code>autoprefixer</code> as the dev dependencies of the project:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
</code></pre>
<p>A few points worth mentioning about the <code>postcss</code> and <code>autoprefixer</code> here:</p>
<ul>
<li>The <code>tailwindcss</code> framework doesn't provide us the CSS styles that the bowser understands directly. It provides us the utility classes that some tool has to translate to regular CSS that the browser understands.</li>
<li>Also, the produced CSS from the utility classes must work across all browsers (Edge, Chrome, Firefox, Safari, and so on).</li>
</ul>
<p>So we need to have PostCSS and Autoprefixer along with TailwindCSS to set up the expected CSS output at the build phase.</p>
<h3 id="heading-configure-tailwindcss">Configure TailwindCSS</h3>
<p>Now create the configuration file for Tailwind and PostCSS using this command:</p>
<pre><code class="lang-bash">npx tailwindcss init -p
</code></pre>
<p>It will create two files for you:</p>
<ul>
<li><code>tailwind.config.js</code>: the configuration file for TailwindCSS. We will have to change this file to provide some basic configuration to start with. The same file must be edited with additional settings when you want to extend TailwindCSS for any advanced use cases.</li>
<li><code>postcss.config.js</code>: the configuration file for PostCSS. In most cases you do not have to change anything in this file.</li>
</ul>
<p>Open the <code>tailwind.config.js</code> file and replace the existing content with the following:</p>
<pre><code class="lang-js"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Note that we have added a couple of entries to the <code>content</code> array value to tell TailwindCSS what to consider for its utility classes to work. In our case, it must be the <code>index.html</code> file and any <code>.js</code> | <code>.ts</code> or <code>.jsx</code> | <code>.tsx</code> files under the <code>src/</code> directory.</p>
<p>Now open the <code>./src/index.css</code> file and add the <code>@tailwind</code> directives for each of Tailwind’s layers:</p>
<pre><code class="lang-js">@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<p>That's it. We have done all the required configuration for TailwindCSS to run with a Vite app.</p>
<h2 id="heading-lets-run-things-together">Let's Run Things Together</h2>
<p>It's time to run things together. Start the Vite server back up locally using the command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Now edit the <code>src/App.jsx</code> file to replace its content with the following code snippet: </p>
<pre><code class="lang-js">

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl text-center text-red-700"</span>
      &gt;</span>Welcome to Vite with TailwindCSS and React<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App
</code></pre>
<p>Here, the JSX of the App component returns a heading tag (h1) with some welcome text. Notice the class names used with the <code>&lt;h1&gt;</code> tag. These are all utility classes from the TailwindCSS framework. You can even read them like plain English. We asked TailwindCSS to render a bigger text (3XL), that should be center aligned, and in a shade of red.</p>
<p>Now access the app like before using the URL <code>http://localhost:5173</code>. You should see the output as expected:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-18.png" alt="Image" width="600" height="400" loading="lazy">
<em>Welcome screen in your React/Vite app</em></p>
<p>Congratulations, again! You have now set up React and TailwindCSS with Vite and everything is working as expected.</p>
<h2 id="heading-how-to-create-the-template-repository-on-github">How to Create the Template Repository on GitHub</h2>
<p>All the hard work is done. Now we want to save this work somewhere so you can use it like a template every time you want to start a React project with TailwindCSS. There is no better place than GitHub to store and manage the source code.</p>
<p>Login to your GitHub account and create a new repository by clicking the <code>New</code> button from the <code>repositories</code> tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-19.png" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new repo on GitHub</em></p>
<p>Now, provide a repository name and description and create the repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-20.png" alt="Image" width="600" height="400" loading="lazy">
<em>Enter your repo details and click "create repository"</em></p>
<p>Next, commit, and push the entire project code to this repository. After pushing the project code, go to the <code>Settings</code> of the repository. Under the general settings, you will find a checkbox with a label <code>Template repository</code>. Check that checkbox to make this repository a template repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-21.png" alt="Image" width="600" height="400" loading="lazy">
<em>Make this repo a template repository by checking the checkbox</em></p>
<p>Great! Now you have created a template repository that'll let you create a React and TailwindCSS project with a single-click in the future. </p>
<p>Now, you will find a new button called <code>Use this template</code> at the top-right corner of your repository. You can click on it to create a new project repository from this template. If your template repository is public, anyone else from the developer community can use it to create their project repository. Amazing, isn't it?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-22.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I've created a template repository using the same steps we discussed in this article. Please feel free to check it out and if you like the work, give the repository a star ⭐.</p>
<p><a target="_blank" href="https://github.com/atapas/vite-tailwind-react">https://github.com/atapas/vite-tailwind-react</a></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>That's all for now. I hope you found this article informative and insightful. I regularly publish meaningful posts on my <a target="_blank" href="https://blog.greenroots.info/">GreenRoots Blog</a>, and I think you'll find them helpful, too.</p>
<p>Let's connect.</p>
<ul>
<li>I am an educator on my YouTube channel, <code>tapaScript</code>. Please <a target="_blank" href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">SUBSCRIBE</a> to the channel if you want to learn JavaScript, ReactJS, Next.js, Node.js, Git, and all about Web Development in the fundamental way.</li>
<li><a target="_blank" href="https://twitter.com/tapasadhikary">Follow me on X (Twitter</a>) or <a target="_blank" href="https://www.linkedin.com/in/tapasadhikary/">LinkedIn</a> if you don't want to miss the daily dose of Web Development and Programming Tips.</li>
<li>Find all my public speaking talks <a target="_blank" href="https://www.tapasadhikary.com/talks">here</a>.</li>
<li>Check out and follow my Open Source work on <a target="_blank" href="https://github.com/atapas">GitHub</a>.</li>
</ul>
<p>See you soon with my next article. Until then, please take care of yourself, and stay happy.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Migrate from create-react-app to Vite using Jest and Browserslist ]]>
                </title>
                <description>
                    <![CDATA[ The React team no longer recommends using create-react-app (CRA) as a bundler to create a new React application. The team and community realized that even though CRA was a jump-starter, it lacked the flexibility needed to configure or manage large an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-migrate-from-create-react-app-to-vite/</link>
                <guid isPermaLink="false">66bb8f98caaeb78feb348947</guid>
                
                    <category>
                        <![CDATA[ create-react-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Saheed Oladele ]]>
                </dc:creator>
                <pubDate>Fri, 06 Oct 2023 18:01:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/pexels-tima-miroshnichenko-5380664.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The React team no longer recommends using <a target="_blank" href="https://github.com/facebook/create-react-app">create-react-app (CRA)</a> as a bundler to create a new React application. The team and community realized that even though CRA was a jump-starter, it lacked the flexibility needed to configure or manage large and complex applications.</p>
<p>Nowadays, the team recommends using <a target="_blank" href="https://react.dev/learn/start-a-new-react-project#production-grade-react-frameworks">production-grade React frameworks</a> like <a target="_blank" href="https://nextjs.org/">NextJS</a>, <a target="_blank" href="https://www.freecodecamp.org/news/p/b2e8aa42-17f7-486f-9fab-b47f9704248b/Remix">Remix</a>, <a target="_blank" href="https://www.gatsbyjs.com/">Gatsby</a>, or <a target="_blank" href="https://expo.dev/">Expo</a> for native apps. While frameworks are the preferred choice, the React team also recommend using <a target="_blank" href="https://vitejs.dev/">Vite</a> or <a target="_blank" href="https://parceljs.org/">Parcel</a> for custom build processes.</p>
<p>This is partly because the <a target="_blank" href="https://www.npmjs.com/package/create-react-app">CRA package has not been updated</a> for about a year. This may cause some problems where packages already updated to more recent versions cannot be used within an existing application. As a result, you may need to update existing applications by replacing the CRA package with the recommended alternatives — Vite or Parcel.</p>
<p>This article walks you through the steps for migrating a production-based application from CRA to Vite. You will learn the "why" of each step, how to retain <code>Jest</code> for tests, and how to update your <code>browserslist</code> since it doesn't work with <code>vite</code> out of the box.</p>
<p>In the conclusion section, you can find a sample pull request that includes all the changes. At the end of each step, you will find a sample commit text which shows the code change required per step.</p>
<h2 id="heading-step-1-install-vite-and-plugins">Step 1: Install <code>Vite</code> and Plugins</h2>
<p>Here are the commands to install the packages we need:</p>
<pre><code class="lang-bash">yarn add vite @vitejs/plugin-react vite-tsconfig-paths

OR

npm install vite @vitejs/plugin-react vite-tsconfig-paths
</code></pre>
<p>Apart from Vite, we are adding two plugins — <code>@vitejs/plugin-react</code> and  <code>vite-tsconfig-paths</code>.</p>
<p>The <code>vitejs/plugin-react</code> <a target="_blank" href="https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md#vitejsplugin-react-">plugin</a> enables fast refresh in development, uses automatic JSX runtime, and custom Babel plugins or presets. It enriches your React development experience.</p>
<p>The <code>vite-tsconfig-paths</code> <a target="_blank" href="https://github.com/aleclarson/vite-tsconfig-paths">plugin</a> resolves imports for TypeScript's path mapping. For example, you can use <code>components/ComponentName</code> instead of <code>./../components/ComponentName</code>.</p>
<h3 id="heading-other-vite-plugins">Other Vite plugins</h3>
<p>Another plugin you could consider is <code>vite-plugin-svgr</code>, which <a target="_blank" href="https://github.com/pd4d10/vite-plugin-svgr">transforms</a> SVG into React components and uses <a target="_blank" href="https://github.com/gregberge/svgr">svgr</a> under the hood. I left it out since we do not have such a use case in the application I’m migrating.</p>
<p>You can also checkout other official Vite plugins <a target="_blank" href="https://vitejs.dev/plugins/">here</a>.</p>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/2b37990690f7898117b1a0cb89e1118451bd24d9">Step 1 sample commit</a>.</p>
<h2 id="heading-step-2-create-a-vite-config-file">Step 2: Create a <code>Vite</code> config file</h2>
<p>On running <code>vite</code> in the command terminal, Vite tries to find a <code>vite.config.ts</code> file inside the project's root directory. You can read more on <a target="_blank" href="https://vitejs.dev/config/">Vite's page</a> for how to further configure this file for i<a target="_blank" href="https://vitejs.dev/config/#config-intellisense">ntellisense</a>, <a target="_blank" href="https://vitejs.dev/config/#conditional-config">environment based configurations</a>, <a target="_blank" href="https://vitejs.dev/config/#async-config">async configuration</a>, and <a target="_blank" href="https://vitejs.dev/config/#using-environment-variables-in-config">env variables usage</a>.</p>
<p>At your application’s root, create a file named <code>vite.config.ts</code> with the following content:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>
<span class="hljs-keyword">import</span> viteTsconfigPaths <span class="hljs-keyword">from</span> <span class="hljs-string">'vite-tsconfig-paths'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
    <span class="hljs-comment">// depending on your application, base can also be "/"</span>
    <span class="hljs-attr">base</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">plugins</span>: [react(), viteTsconfigPaths()],
    <span class="hljs-attr">server</span>: {    
        <span class="hljs-comment">// this ensures that the browser opens upon server start</span>
        <span class="hljs-attr">open</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-comment">// this sets a default port to 3000  </span>
        <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span>, 
    },
})
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/557e8fbda9f8a19bf58ae0eeb1aa81e14111729a">Step 2 sample commit</a>.</p>
<h2 id="heading-step-3-create-a-vite-types-file-reference">Step 3: Create a <code>Vite</code> Types File Reference</h2>
<p>This step is needed to reference a type declarations file which aids type checks and Intellisense. By default, Vite types are for a NodeJS environment. For client side code, <a target="_blank" href="https://vitejs.dev/guide/env-and-mode.html#intellisense-for-typescript">Vite provides the type definitions</a> in <a target="_blank" href="https://github.com/vitejs/vite/blob/main/packages/vite/client.d.ts"><code>vite/client.d.ts</code></a>.</p>
<p>At your application’s root, create a file named <code>vite-env.d.ts</code> with the following content:</p>
<pre><code class="lang-js"><span class="hljs-comment">/// &lt;reference types="vite/client" /&gt;</span>
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/3002d2dbe1a421c0f31f57658ddde3030e898d1a">Step 3 sample commit</a>.</p>
<h2 id="heading-step-4-move-the-indexhtml-file">Step 4: Move the <code>index.html</code> File</h2>
<p>Vite has a <a target="_blank" href="https://vitejs.dev/guide/#index-html-and-project-root">root directory</a> which your files are served from. Since <code>index.html</code> is the entry point for Vite's server, the file needs to be in the root directory.</p>
<p>From the public directory, move the <code>index.html</code> file to the root of your project.</p>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/940f1f743fe6a380b157b86256aef7e29b2457a1">Step 4 sample commit</a>.</p>
<h2 id="heading-step-5-update-the-indexhtml-file">Step 5: Update the <code>index.html</code> File</h2>
<p>Two updates are necessary here:</p>
<h3 id="heading-remove-publicurl">Remove <code>%PUBLIC_URL%</code></h3>
<p>Vite <a target="_blank" href="https://vitejs.dev/guide/#index-html-and-project-root">automatically resolves</a> URLs inside <code>index.html</code>, so there's no need for <code>%PUBLIC_URL%</code> placeholders. You can do a search and replace inside your <code>index.html</code> file for this. Be sure to remove all occurrences.</p>
<p>Before:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/svg+xml"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"%PUBLIC_URL%/favicon.svg"</span> /&gt;</span>
</code></pre>
<p>After:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/svg+xml"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.svg"</span> /&gt;</span>
</code></pre>
<h3 id="heading-add-module-script-to-the-bottom-of-the-body-tag">Add module script to the bottom of the body tag</h3>
<p>Vite treats <code>index.html</code> <a target="_blank" href="https://vitejs.dev/guide/#index-html-and-project-root">as source code and part of the module graph</a>. It resolves <code>&lt;script type="module" src="..."&gt;</code> that references your JavaScript source code.</p>
<p>At the bottom of the body tag in <code>index.html</code> file, add the script as shown below:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    {/* others here */}
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/src/index.tsx"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/406b9765c5c166a0bafff6332f8a908156b794c4">Step 5 sample commit</a>.</p>
<h2 id="heading-step-6-replace-cra-with-vite">Step 6: Replace CRA with <code>Vite</code></h2>
<p>You can now remove CRA, add <code>Vite</code> scripts to the <code>package.json</code> file, and update <code>tsconfig.json</code>.</p>
<h3 id="heading-remove-cra">Remove CRA</h3>
<p>To remove CRA, run the following command. This will remove <code>react-scripts</code> from our installed packages.</p>
<pre><code class="lang-bash">yarn remove react-scripts

OR

npm uninstall react-scripts
</code></pre>
<p>After running the command above, delete the <code>react-app-env.d.ts</code> file.</p>
<h3 id="heading-add-vite-scripts-to-the-packagejson-file">Add Vite scripts to the <code>package.json</code> file</h3>
<p>With Vite installed, you can use the <code>vite</code> binary in your scripts. This could mean replacing <code>react-scripts</code> in a few places. Your focus should be on the <code>start</code> and <code>build</code> keys. The <code>preview</code> key is an addition which helps to preview production build locally.</p>
<p>Note that <code>start</code> is <code>vite</code> and not <code>vite start</code>.</p>
<pre><code class="lang-javascript">{  
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"vite"</span>, <span class="hljs-comment">// start dev server</span>
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"tsc &amp;&amp; vite build"</span>, <span class="hljs-comment">// build for production</span>
    <span class="hljs-string">"preview"</span>: <span class="hljs-string">"vite preview"</span> <span class="hljs-comment">// locally preview production build</span>
  }
},
</code></pre>
<h3 id="heading-update-tsconfigjson">Update <code>tsconfig.json</code></h3>
<p>Here, your focus should be on the <code>isolatedModules</code>, <code>lib</code>, <code>target</code>, and <code>types</code>. For more options, here is a <a target="_blank" href="https://github.com/vitejs/create-vite-app/blob/master/template-react-ts/tsconfig.json">sample tsconfig file from Vite</a>.</p>
<pre><code class="lang-javascript">{  
    <span class="hljs-string">"compilerOptions"</span>: {    
        <span class="hljs-string">"lib"</span>: [<span class="hljs-string">"dom"</span>, <span class="hljs-string">"dom.iterable"</span>, <span class="hljs-string">"esnext"</span>],    
        <span class="hljs-string">"target"</span>: <span class="hljs-string">"ESNext"</span>,    
        <span class="hljs-string">"types"</span>: [<span class="hljs-string">"vite/client"</span>],
        <span class="hljs-string">"isolatedModules"</span>: <span class="hljs-literal">true</span>,
    },
 }
</code></pre>
<h3 id="heading-update-processenvreactappvariable-optional">Update <code>process.env.REACT_APP_VARIABLE</code> (optional)</h3>
<p>This is necessary if your application uses environment variable. Vite uses <code>import.meta.env.REACT_APP_VARIABLE</code> instead of <code>process.env.REACT_APP_VARIABLE</code>. You can find more details on <a target="_blank" href="https://vitejs.dev/guide/env-and-mode.html">Vite's env variables and modes here</a>.</p>
<p>Before:</p>
<pre><code class="lang-javascript">process.env.REACT_APP_VARIABLE
</code></pre>
<p>After:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span>.meta.env.REACT_APP_VARIABLE
</code></pre>
<h3 id="heading-replace-react-with-vite-optional">Replace <code>REACT_</code> with <code>VITE_</code> (optional)</h3>
<p>You only need this if you updated <code>process.env</code> above. Replace your <code>REACT_</code> environment variables to start with <code>VITE_</code>. This is needed because Vite filters out any env variable not starting with <code>VITE_</code>.</p>
<p>Before:</p>
<pre><code class="lang-javascript">REACT_APP_API_BASE
</code></pre>
<p>After:</p>
<pre><code class="lang-javascript">VITE_APP_API_BASE
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/775b3e841f107c2116e46d2a94b3ca9e697561e9">Step 6 sample commit</a>.</p>
<h2 id="heading-step-7-run-your-application">Step 7: Run your Application</h2>
<pre><code class="lang-bash">yarn start

OR

npm start
</code></pre>
<p>Congratulations! You have successfully completed the first step in migrating your application from CRA to Vite. You should see a screen that looks like the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-05-at-17.21.43-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>No sample commit. ;)</p>
<h2 id="heading-possible-blockers-and-their-solutions">Possible blockers and their solutions</h2>
<h3 id="heading-global-is-not-defined-error"><code>global</code> is not defined error</h3>
<p>If you have this error, define global inside your <code>vite.config.ts</code> file as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-comment">// ...</span>
  <span class="hljs-attr">define</span>: {
    <span class="hljs-comment">// here is the main update</span>
    <span class="hljs-attr">global</span>: <span class="hljs-string">'globalThis'</span>,
  },
});
</code></pre>
<h3 id="heading-if-you-use-emotionreact-or-emotioncss">If you use <code>@emotion/react</code> or <code>@emotion/css</code></h3>
<p>You need to inform Vite about this. To do this, install <code>@emotion/babel-plugin</code>.</p>
<pre><code class="lang-shell">yarn add @emotion/babel-plugin

OR

npm install @emotion/babel-plugin
</code></pre>
<p>Then update your Vite's <code>react</code> plugin in the <code>vite.config.ts</code> as shown below:</p>
<pre><code class="lang-javscript">import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgr from 'vite-plugin-svgr';

export default defineConfig({
  // ...
  plugins: [
    // here is the main update
    react({
      jsxImportSource: '@emotion/react',
      babel: {
        plugins: ['@emotion/babel-plugin'],
      },
    }),
  ],
  // ...
});
</code></pre>
<h3 id="heading-oh-no-my-unit-tests-are-not-working">Oh no, my unit tests are not working!</h3>
<p>At this point, try running your unit tests — <code>yarn test</code> or <code>npm run test</code>. It’s possible that they don’t work. The following steps highlight how you can fix your unit tests.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-06-at-16.20.33.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Your unit tests are not working because CRA uses <code>react-scripts test</code> to run tests, so we want to switch to using <code>jest</code>.</p>
<h2 id="heading-step-8-install-jest-and-typescript-related-dependencies">Step 8: Install Jest and TypeScript-related Dependencies</h2>
<p>To start with, you need to install <code>jest</code>, <code>ts-jest</code>, and <code>jest-environment-jsdom</code>. <code>jest</code> will be our new binary for running the tests, <code>[ts-jest](https://www.npmjs.com/package/ts-jest)</code> is a transformer with source map support which allows you to run tests in TypeScript projects, and <code>jest-environment-jsdom</code> imitates the browser's behavior during test runs.</p>
<pre><code class="lang-shell">yarn add -D jest @types/jest ts-jest jest-environment-jsdom

OR

npm install --save-dev jest @types/jest ts-jest jest-environment-jsdom
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/66283764d616c4572ecd53d51ac1711f30fbe8e0">Step 8 sample commit</a>.</p>
<h2 id="heading-step-9-update-jest-config">Step 9: Update Jest Config</h2>
<p>This depends on your current Jest configuration. If it is configured inside <code>package.json</code>, you can update as follows. Here, you focus on <code>preset</code>, <code>testEnvironment</code>, <code>moduleNameMapper</code>, and <code>modulePaths</code>.</p>
<p><code>preset</code> is set to <code>ts-jest/presets/js-with-ts</code> to allow TypeScript with JavaScript. You can also just set it to <code>ts-jest</code> depending on your application.</p>
<p><code>moduleNameMapper</code> configures Jest to gracefully handle assets such as stylesheets and images.</p>
<pre><code class="lang-json">  <span class="hljs-string">"jest"</span>: {
    <span class="hljs-attr">"preset"</span>: <span class="hljs-string">"ts-jest/presets/js-with-ts"</span>,
    <span class="hljs-attr">"testEnvironment"</span>: <span class="hljs-string">"jest-environment-jsdom"</span>,
    <span class="hljs-attr">"moduleNameMapper"</span>: {
      <span class="hljs-attr">"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$"</span>: <span class="hljs-string">"&lt;rootDir&gt;/__mocks__/fileMock.js"</span>,
      <span class="hljs-attr">"\\.(css|less)$"</span>: <span class="hljs-string">"&lt;rootDir&gt;/__mocks__/styleMock.js"</span>
    },
    <span class="hljs-attr">"modulePaths"</span>: [
      <span class="hljs-comment">// you can update this to match your application setup</span>
      <span class="hljs-string">"&lt;rootDir&gt;/src"</span>
    ],
  },
</code></pre>
<p>Since we referenced a file in <code>moduleNameMapper</code> above, we need to create the file and it's corresponding files. Step 10 takes care of this. This setup is explained further in <a target="_blank" href="https://jestjs.io/docs/webpack#handling-static-assets">Jest's documentation here</a>.</p>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/4383f05fce07ac1de3c80d04f581e40218f8c83b">Step 9 sample commit</a>.</p>
<h2 id="heading-step-10-add-the-mocks-directory-to-the-root-of-your-project">Step 10: Add the <code>__mocks__</code> Directory to the Root of Your Project</h2>
<p>At the root of your project, create a folder named <code>__mocks__</code>.</p>
<p>Inside the created <code>__mocks__</code> folder, add a file named <code>styleMock.js</code> and add the following content:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {}
</code></pre>
<p>Inside the created <code>__mocks__</code> folder, add a file named <code>fileMock.js</code> and add the following content.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-string">'test-file-stub'</span>
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/d6da2a8984176041fcbfe08ca2bf0e9339fb25b4">Step 10 sample commit</a>.</p>
<h2 id="heading-step-11-update-the-packagejson-scripts">Step 11: Update the <code>package.json</code> Scripts</h2>
<p>Now that we have <code>jest</code> properly installed, we can replace <code>react-scripts tests</code> with <code>jest</code>. Changes should be as shown below. If you do not have the <code>test:coverage</code> or <code>test:debug</code> keys in your code before, feel free to ignore.</p>
<p>Before:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"react-scripts test"</span>,
    <span class="hljs-attr">"test:coverage"</span>: <span class="hljs-string">"react-scripts test --coverage ."</span>,
    <span class="hljs-attr">"test:debug"</span>: <span class="hljs-string">"react-scripts test --inspect-brk --runInBand --no-cache"</span>
}
</code></pre>
<p>After:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jest"</span>,
    <span class="hljs-comment">// you can add this to keep watch mode on</span>
    <span class="hljs-attr">"test:watch"</span>: <span class="hljs-string">"jest --watch"</span>,
    <span class="hljs-attr">"test:coverage"</span>: <span class="hljs-string">"jest --coverage ."</span>,
    <span class="hljs-attr">"test:debug"</span>: <span class="hljs-string">"jest --inspect-brk --runInBand --no-cache"</span>
}
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/e27a66593954ffcb36d77c3e86076444a0cf82e5">Step 11 sample commit</a>.</p>
<h2 id="heading-step-12-run-your-tests">Step 12: Run your Tests</h2>
<pre><code class="lang-shell">yarn test

OR

npm test
</code></pre>
<p>If you face a problem related to <code>import.meta</code>, you can resolve this by moving all your environment keys to a single file and mocking this file in your test. You can take a <a target="_blank" href="https://github.com/suretrust/stock-ticker/commit/07d15d1f000ec2cef7ec6dd01fccb43af3e67d30">look at this commit</a> to have a better understanding of what I mean.</p>
<p>No sample commit. :)</p>
<p>Ah, it works! But how about the browserslist config?</p>
<h2 id="heading-what-is-browserslist-config">What is browserslist config?</h2>
<p>This is a configuration used to share your target or supported browsers among multiple frontend repositories. </p>
<p>There are various standards depending on the industry. For example, in EdTech, it's possible that all users learning online use similar browsers in terms of brand, version, and screen size. This list of the commonly used browsers can easily become a standard for the EdTech industry.</p>
<p>A sample application of the browserslist config is when you need to be compatible with older browsers. Passing this range to the browserslist config helps your bundler to compile your code using polyfills that are compatible with your target browsers. This way, your page has optimized performance with good user experience.</p>
<p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Polyfill">Mozilla defines a Polyfill</a> as a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it.</p>
<p>Browserslist config is often set in the <code>package.json</code> or <code>.browserslistrc</code> file as shown below.</p>
<h3 id="heading-packagejson"><code>package.json</code></h3>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"browserslist"</span>: [
    <span class="hljs-string">"iOS &gt;= 9"</span>,
    <span class="hljs-string">"Android &gt;= 4.4"</span>,
    <span class="hljs-string">"last 2 versions"</span>,
    <span class="hljs-string">"&gt; 0.2%"</span>,
    <span class="hljs-string">"not dead"</span>
  ]
}
</code></pre>
<h3 id="heading-browserslistrc"><code>.browserslistrc</code></h3>
<pre><code class="lang-javascript">iOS &gt;= <span class="hljs-number">9</span>
Android &gt;= <span class="hljs-number">4.4</span>
last <span class="hljs-number">2</span> versions
&gt; <span class="hljs-number">0.2</span>%
not dead
</code></pre>
<p>You can also <a target="_blank" href="https://modernjs.dev/builder/en/guide/advanced/browserslist">read more about Browserslist here</a>.</p>
<h2 id="heading-why-is-browserslist-a-problem-with-vite">Why is <code>browserslist</code> a Problem with Vite?</h2>
<p>Vite uses ESBuild under the hood which expects a different format to the usual <code>browserslist</code> format.</p>
<p>ESBuild expected format: <code>['es2015', 'safari11', 'ios11']</code></p>
<p>Browserslist format: <code>['defaults', 'Safari &gt;= 11', 'ios_saf &gt;= 11']</code></p>
<p>As a result of this discrepancy, Vite ignores your <code>browserslist</code> configuration which is currently in the <code>package.json</code> or <code>.brwserslistrc</code> file. </p>
<p>To fix this, you can use a package called <a target="_blank" href="https://github.com/marcofugaro/browserslist-to-esbuild"><code>browserslist-to-esbuild</code></a> which does this conversion under the hood and pass the config to <code>build.target</code> inside the <code>vite.config.ts</code> file. Steps 13 and 14 take care of this.</p>
<h2 id="heading-step-13-install-browserslist-to-esbuild">Step 13: Install <code>browserslist-to-esbuild</code></h2>
<pre><code class="lang-shell">yarn add browserslist-to-esbuild

OR 

npm install browserslist-to-esbuild
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/fc7239af0a0da0b4bd844f8d17b3860b794f0ea5">Step 13 sample commit</a>.</p>
<h2 id="heading-step-14-confiure-the-browserslist-in-vite-config">Step 14: Confiure the <code>browserslist</code> in Vite Config</h2>
<p>In the <code>vite.config.ts</code> file, update as shown below.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>
<span class="hljs-keyword">import</span> browserslistToEsbuild <span class="hljs-keyword">from</span> <span class="hljs-string">'browserslist-to-esbuild'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  ..
  build: {
    <span class="hljs-comment">// --&gt; ["chrome79", "edge92", "firefox91", "safari13.1"]</span>
    <span class="hljs-attr">target</span>: browserslistToEsbuild(), 
  },
  ..
})
</code></pre>
<p>And then, you can pass your configs as shown below,</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  ..
  build: {
    <span class="hljs-comment">// you can also pass your usual browserslist config here</span>
    <span class="hljs-attr">target</span>: browserslistToEsbuild([
        <span class="hljs-string">'&gt;0.2%'</span>,
        <span class="hljs-string">'not dead'</span>,
        <span class="hljs-string">'not op_mini all'</span>
    ]),
  },
  ..
})
</code></pre>
<p><a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1/commits/b8758cd4ac9e0e1d79e361b1b0097714bc6a85ae">Step 14 sample commit</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Voilà! You're done and your application is fully migrated.</p>
<p>Missing any step? Here is a <a target="_blank" href="https://github.com/suretrust/stock-ticker/pull/1">sample pull request that highlights all the changes</a> involved.</p>
<p>You have learned the "why" and "how" of replacing <code>create-react-app</code> with <code>Vite</code>. I hope you are as proud of yourself as I was of myself for what I have learned in the process of the migration.</p>
<p>Alrighty, that it! Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create and Publish a Vue Component Library – Update ]]>
                </title>
                <description>
                    <![CDATA[ Back in 2020, I wrote a post about building a Vue Component library. Since then the package I used has been deprecated, and the recommended way to build a library/package is to use Vite. Getting Started I started off the project by running npm create... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-and-publish-a-vue-component-library-update/</link>
                <guid isPermaLink="false">66bb92e15d242388375d388c</guid>
                
                    <category>
                        <![CDATA[ components ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Brian Barrow ]]>
                </dc:creator>
                <pubDate>Tue, 30 May 2023 20:11:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/05/pexels-pixabay-159711.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Back in 2020, I <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-and-publish-a-vue-component-library/">wrote a post</a> about building a Vue Component library. Since then the package I used has been deprecated, and the recommended way to build a library/package is to use Vite.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>I started off the project by running <code>npm create vite@latest</code> and naming my project <code>brian-component-lib</code> to stay consistent with my previous post. I also chose to use TypeScript and Vue when those options came up.</p>
<h2 id="heading-the-component">The Component</h2>
<p>The component I built is a clone of the buttons used on freeCodeCamp.org</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/05/image-160.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Button component we are building</em></p>
<p>Here is the code for that component. Note that it is using TypeScript and the <code>script setup</code> format available in Vue 3.</p>
<pre><code class="lang-js">&lt;script setup lang=<span class="hljs-string">"ts"</span>&gt;

defineProps&lt;{ <span class="hljs-attr">text</span>: string }&gt;()

&lt;/script&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn-cta"</span>&gt;</span>{{ text }}<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">scoped</span>&gt;</span><span class="css">
<span class="hljs-selector-class">.btn-cta</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#d0d0d5</span>;
  <span class="hljs-attribute">border-width</span>: <span class="hljs-number">3px</span>;
  <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#1b1b32</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">border-style</span>: solid;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#1b1b32</span>;
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-weight</span>: normal;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">-ms-touch-action</span>: manipulation;
  <span class="hljs-attribute">touch-action</span>: manipulation;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">white-space</span>: nowrap;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">6px</span> <span class="hljs-number">12px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.42857143</span>;
}

<span class="hljs-selector-class">.btn-cta</span><span class="hljs-selector-pseudo">:active</span><span class="hljs-selector-pseudo">:hover</span>,
<span class="hljs-selector-class">.btn-cta</span><span class="hljs-selector-pseudo">:focus</span>,
<span class="hljs-selector-class">.btn-cta</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#1b1b32</span>;
  <span class="hljs-attribute">border-width</span>: <span class="hljs-number">3px</span>;
  <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#000</span>;
  <span class="hljs-attribute">background-image</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#f5f6f7</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>Then we need to expose this component in the library. We do that by exporting it from an <code>index.ts</code> file.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> FccButton <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/FccButton.vue"</span>;

<span class="hljs-keyword">export</span> { FccButton };
</code></pre>
<h2 id="heading-config">Config</h2>
<p>With the component code ready to go, we need to make sure Vite and the <code>package.json</code> file are configured properly.</p>
<p>Vite has a lot of options when building code. We are interested in the <a target="_blank" href="https://vitejs.dev/guide/build.html#library-mode">"Library Mode"</a>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> { resolve } <span class="hljs-keyword">from</span> <span class="hljs-string">"path"</span>;
<span class="hljs-keyword">import</span> vue <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-vue"</span>;

<span class="hljs-comment">// https://vitejs.dev/config/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">plugins</span>: [vue()],
  <span class="hljs-attr">build</span>: {
    <span class="hljs-attr">lib</span>: {
      <span class="hljs-comment">// src/indext.ts is where we have exported the component(s)</span>
      <span class="hljs-attr">entry</span>: resolve(__dirname, <span class="hljs-string">"src/index.ts"</span>),
      <span class="hljs-attr">name</span>: <span class="hljs-string">"BrianComponentLibrary"</span>,
      <span class="hljs-comment">// the name of the output files when the build is run</span>
      <span class="hljs-attr">fileName</span>: <span class="hljs-string">"brian-component-lib"</span>,
    },
    <span class="hljs-attr">rollupOptions</span>: {
      <span class="hljs-comment">// make sure to externalize deps that shouldn't be bundled</span>
      <span class="hljs-comment">// into your library</span>
      <span class="hljs-attr">external</span>: [<span class="hljs-string">"vue"</span>],
      <span class="hljs-attr">output</span>: {
        <span class="hljs-comment">// Provide global variables to use in the UMD build</span>
        <span class="hljs-comment">// for externalized deps</span>
        <span class="hljs-attr">globals</span>: {
          <span class="hljs-attr">vue</span>: <span class="hljs-string">"Vue"</span>,
        },
      },
    },
  },
});
</code></pre>
<p>Here is the <code>package.json</code> file. We need to make sure we have the properties necessary for pointing to our built files. For more information on what each property does, you can hover over them in VS Code.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"brian-component-lib"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-attr">"files"</span>: [<span class="hljs-string">"dist"</span>],
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"./dist/brian-component-lib.umd.cjs"</span>,
  <span class="hljs-attr">"module"</span>: <span class="hljs-string">"./dist/brian-component-lib.js"</span>,
  <span class="hljs-attr">"exports"</span>: {
    <span class="hljs-attr">"."</span>: {
      <span class="hljs-attr">"import"</span>: <span class="hljs-string">"./dist/brian-component-lib.js"</span>,
      <span class="hljs-attr">"require"</span>: <span class="hljs-string">"./dist/brian-component-lib.umd.cjs"</span>
    },
    <span class="hljs-attr">"./style.css"</span>: <span class="hljs-string">"./dist/style.css"</span>
  },
  <span class="hljs-attr">"types"</span>: <span class="hljs-string">"./dist/index.d.ts"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"vite build &amp;&amp; vue-tsc --emitDeclarationOnly"</span>,
    <span class="hljs-attr">"types"</span>: <span class="hljs-string">"vue-tsc "</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview"</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"vue"</span>: <span class="hljs-string">"^3.2.47"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^20.2.5"</span>,
    <span class="hljs-attr">"@vitejs/plugin-vue"</span>: <span class="hljs-string">"^4.2.3"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^5.0.2"</span>,
    <span class="hljs-attr">"vite"</span>: <span class="hljs-string">"^4.3.9"</span>,
    <span class="hljs-attr">"vue-tsc"</span>: <span class="hljs-string">"^1.4.2"</span>
  }
}
</code></pre>
<p>In order for the <code>vue-tsc --emitDeclarationOnly</code> to work when building, we need to add the following properties to the <code>compilerOptions</code> section of the tsconfig.json file:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"outDir"</span>: <span class="hljs-string">"dist"</span>,
<span class="hljs-string">"declaration"</span>: <span class="hljs-literal">true</span>,
</code></pre>
<p>We will also need to remove the <code>noEmit: true</code> property. This will make it so our types are available in the package, so a project using TypeScript with Vue doesn't yell at us for not having declared types.</p>
<p>I also added this line to make sure my <code>App.vue</code> and <code>main.ts</code> file aren't included in the component library built files.</p>
<p><code>"exclude": ["src/App.vue", "src/main.ts"],</code></p>
<h2 id="heading-test-the-library">Test the Library</h2>
<p>We can now run <code>npm run build</code> and then test out our library. To do this, open up a Vue project (you can open a current Vue 3 project you have, or create a blank one).</p>
<p>Inside the package.json file for the project you just opened add the following to the dependencies:</p>
<p><code>"brian-component-lib": "file:../brian-component-library"</code></p>
<p>Make sure the file path you put in points to the correct folder where the component library lives.</p>
<p>Run <code>npm install</code> and you should now have the component library in your <code>node_modules</code>.</p>
<p>Import the component into one of the pages to test that it is working.</p>
<p>Note: You will need to also import the CSS because it gets built to its own file during the build process.</p>
<pre><code class="lang-js">&lt;script setup lang=<span class="hljs-string">"ts"</span>&gt;
<span class="hljs-keyword">import</span> { FccButton } <span class="hljs-keyword">from</span> <span class="hljs-string">'brian-component-lib'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"brian-component-lib/style.css"</span>
&lt;/script&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">FccButton</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"Run the Tests"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></span>
</code></pre>
<p>You should now see the button when you run the project.</p>
<h2 id="heading-how-to-publish-to-npm">How to Publish to NPM</h2>
<p>If you haven't signed into NPM inside your terminal you can do that by running the <code>npm adduser</code> command.</p>
<p>Then just run the <code>npm publish</code> command and the package should be pushed up and made available on NPM.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Vite makes it pretty easy to get a component library published. Hopefully this helped. <a target="_blank" href="https://twitter.com/the_brianb">Let me know on Twitter</a> if it did or if you'd like to see something else from me in the future.</p>
<p>You can see the repository for this code here: <a target="_blank" href="https://github.com/briancbarrow/vue-component-library-2023">https://github.com/briancbarrow/vue-component-library-2023</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Live Football Scoreboard in React with Vite and Vitest ]]>
                </title>
                <description>
                    <![CDATA[ Welcome to yet another tutorial where you will learn how to build a ⚽ scoreboard app in React. This time we will use Vite as our next generation frontend tooling and Vitest as a Vite-native unit testing framework. You will also learn how to leverage ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-project-with-vite-and-vitest/</link>
                <guid isPermaLink="false">66d4605cb3016bf139028d71</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Thu, 13 Apr 2023 20:41:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-13-at-5.15.35-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Welcome to yet another <a target="_blank" href="https://www.mihailgaberov.com/">tutorial</a> where you will learn how to build a ⚽ scoreboard app in React. This time we will use Vite as our <a target="_blank" href="https://vitejs.dev/">next generation frontend tooling</a> and <a target="_blank" href="https://vitest.dev/">Vitest</a> as a Vite-native unit testing framework.</p>
<p>You will also learn how to leverage <a target="_blank" href="https://react.dev/learn/reusing-logic-with-custom-hooks">React hooks</a>, built as an abstraction of the native JavaScript timeouts and time intervals. For styling the application we will use <a target="_blank" href="https://github.com/css-modules/css-modules">CSS Modules</a> with <a target="_blank" href="https://sass-lang.com/">SASS</a>.</p>
<p>Here's what we'll be building:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-04-at-09.11.31.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Football live scoreboard - current games screen</em></p>
<p>💡If you want to skip the reading, 💁 <a target="_blank" href="https://github.com/mihailgaberov/scoreboard">here</a> is the GitHub repository, and here you can see the live <a target="_blank" href="https://scoreboard-mihailgaberov.vercel.app/">demo</a> 📺.</p>
<h2 id="heading-what-is-a-scoreboard">What is a Scoreboard?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-04-at-11.58.35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Football scoreboard</em></p>
<p>A live scoreboard is a digital sport scoreboard that automatically displays up-to-the-minute sports scores and data from a certain game – for example a football game. This way it’s much easier for the users to follow the game, make predictions or bets, and so on.</p>
<p>Our application is going to reflect such a board, but in the browser.</p>
<h2 id="heading-the-project">The Project</h2>
<p>Our application has just a few dependencies and several components. It also uses JavaScript timeouts and intervals to simulate real-time score updates.</p>
<h3 id="heading-application-features"><strong>⚙️ Application features</strong></h3>
<p>Before going into the technical part of the tutorial, let's talk about the application features we will implement.</p>
<p>It’s always better (if possible, of course) to have clear project requirements laid out before writing a single line of code. But folks with some experience in the software engineering and development world know that the reality is often completely different.</p>
<p>The beauty of such small projects that you build for educational purposes is exactly this – you have the freedom to define your own requirements and to meet them in a feasible manner.</p>
<p>So here it the summary of the requirements/features:</p>
<p><strong>Live Football World Cup Scoreboard</strong> that shows matches and scores.</p>
<p>The board supports the following operations:</p>
<ol>
<li><p>Start a game. When a game starts, it should capture the home team and away team (with an initial score of 0 – 0).</p>
</li>
<li><p>Finish game. It will remove a match from the scoreboard.</p>
</li>
<li><p>Update score. Receiving the pair's score. When the home team or away team scores, it updates the game score.</p>
</li>
<li><p>Get a summary of games by total score. Those games with the same total score will be returned ordered by the most recently added to our system.</p>
</li>
</ol>
<p>✍️ As an example, if this is the current data in the system:</p>
<p><code>a. Mexico - Canada: 0 - 5</code><br><code>b. Spain - Brazil: 10 – 2</code><br><code>c. Germany - France: 2 – 2</code><br><code>d. Uruguay - Italy: 6 – 6</code><br><code>e. Argentina - Australia: 3 - 1</code></p>
<p>The summary would give us the following information:</p>
<p><code>1. Uruguay 6 - Italy 6</code><br><code>2. Spain 10 - Brazil 2</code><br><code>3. Mexico 0 - Canada 5</code><br><code>4. Argentina 3 - Australia 1</code><br><code>5. Germany 2 - France 2</code></p>
<h3 id="heading-project-structure">🏗️ Project Structure</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-04-at-15.08.38.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Project Structure</em></p>
<p>Let me go through each of the files and give a short explanation of what they are and why we need them:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/package.json">package.json</a> – the configuration file of every Node.js app, created with npm or yarn, or any other package manager that uses the same approach.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/README.md">README.md</a> – not much to say here – it's a simple text file that uses Markdown and contains the description of the project, plus any other information you want to put there.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/vite.config.js">vite.config.js</a> – the main configuration file that Vite uses, which you get when you do the installation from the previous step. The content of this file by default looks like this:</p>
</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>

<span class="hljs-comment">// &lt;https://vitejs.dev/config/&gt;</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">plugins</span>: [react()],
})
</code></pre>
<p>But in my case, I had to add the <code>test</code> configurations so that we could run the tests. You will read more about this later in the article.</p>
<ul>
<li><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/setupTests.js">setupTests.js</a> – this one is used for configuring the unit tests. We put here any of the things we would like to have available in the tests we write.</li>
</ul>
<p>For example, in order to be able to use unique keys when rendering multiple elements (<a target="_blank" href="https://react.dev/learn/rendering-lists#where-to-get-your-key">because we React needs it</a>), I am using the <code>randomUUID()</code>method of the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto">Crypto</a> interface to generate a v4 <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/UUID">UUID</a> using a cryptographically secure random number generator. And to make it available in my tests, I need to add it here, like this:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { expect, afterEach } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;
<span class="hljs-keyword">import</span> { cleanup } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> matchers <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/jest-dom/matchers'</span>;
<span class="hljs-keyword">import</span> {randomUUID} <span class="hljs-keyword">from</span> <span class="hljs-string">'node:crypto'</span>;

<span class="hljs-comment">// extends Vitest's expect method with methods from react-testing-library</span>
expect.extend(matchers);

<span class="hljs-comment">// runs a cleanup after each test case (e.g. clearing jsdom)</span>
afterEach(<span class="hljs-function">() =&gt;</span> {
    cleanup();
});

<span class="hljs-built_in">window</span>.crypto.randomUUID = randomUUID;
</code></pre>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/yarn.lock">yarn.lock</a> – this is generated automatically when run yarn installation and lock the version of the packages being used.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/.gitignore">.gitignore</a> – comes out of the box from Vite installation. Here you define which files and folders you want Git to ignore, that is not get committed to your repo.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/index.html">index.html</a> – this the app’s entry point. It's a simple HTML document that has a few meta tags, and includes the logo and the main script file.</p>
</li>
<li><p>/src – contains a few different things we need to discuss:</p>
</li>
</ul>
<ol>
<li><p>First, it has the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/main.jsx">main.jsx</a> file, which is where React and ReactDOM come in play. We also load here the default styles file I mentioned earlier.</p>
</li>
<li><p>It also has <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/index.css">index.css</a> which I have explained already.</p>
</li>
<li><p>Then we have <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/App.jsx">App.jsx</a> which is where our actual application code begins. This file can be considered as the main component in our application, as it contains all ‘inner’ parts of our app.</p>
</li>
<li><p>Then we have <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/app.module.scss">app.module.scss</a> which contains styles for App component, using the CSS module convention to name the files with ‘module’ prefix and ‘scss’ extension.</p>
</li>
<li><p>Finally, we have <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/App.test.jsx">App.test.jsx</a> which contains a simple test for App component, using <a target="_blank" href="https://vitest.dev">Vitest</a> for testing framework.</p>
</li>
</ol>
<h3 id="heading-components">🛠️ Components</h3>
<p>Let me walk you quickly through each of the components in the application. They are located in the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components">components</a> folder.</p>
<h4 id="heading-components-folder-contents">/components folder contents:</h4>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Footer">Footer</a> – self-explanatory, contains the footer part of the app.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/GameStatus">GameStatus</a> – used to show if a game has started, that is if it's being played.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Header">Header</a> – self-explanatory, contains the header part of the app.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/MessageBoard">MessageBoard</a> – a small component used to display text messages that state when the games are starting or if we are looking at the “Summary” screen or the “Current Games”.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Result">Result</a> – another small component showing game scores.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Scoreboard">Scoreboard</a> – kind a <em>parent</em> component, serving as container that holds all the small ones in place.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/ScoreboardsGrid">ScoreboardGrid</a> – this is the most important component in the app, as it contains all the logic related to the timers. It holds all child components and it’s responsible for passing the necessary data to them via their props.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/TeamView">TeamView</a> – another small component serving as a representation of a team, which shows team’s flag and name.</p>
</li>
</ul>
<h3 id="heading-timeouts">⏱️ Timeouts</h3>
<p>The timeouts – or more precisely the time intervals – in the application are implemented with the help of several React hooks. All of them are located in the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/hooks">hooks</a> folder. I borrowed them from a very knowledgable and kinda famous guy named Josh W Comeau. I'll post the links in the end of the article.</p>
<p>So basically we use three hooks, one per type of time interval or timeout we need.</p>
<ol>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/hooks/useInterval.js">useInterval</a> – this is based on the built-in JavaScript <code>setInterval</code> function and it's used for initial countdown, before the games start</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/hooks/useRandomInterval.js">useRandomInterval</a> – this is an enhanced version of the previous one, and it’s used for randomly updating the score of the games, as well as randomly starting and stopping them</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/hooks/useTimeout.js">useTimeout</a> – this is based on the built-in JavasScript <code>setTimeout</code> function and it’s used for deciding when to stop the playing time of the games and start finalizing them</p>
</li>
</ol>
<h2 id="heading-how-to-build-the-project">🧾 How to Build the Project</h2>
<p>By now you should have a decent understanding of what our application is and how its various parts are put together.</p>
<p>Let me now guide you, step-by-step, from the very beginning, and show you how I built it. I will add images where necessary, so that it’s easier for all of you to follow along.</p>
<h3 id="heading-dependencies">📦 Dependencies</h3>
<p>The dependencies we have are very few. Except Vite and Vitest, I have installed additionally only SASS, and the React Testing Library. Here is how my <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/package.json">package.json</a> file looks:</p>
<pre><code class="lang-jsx">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"scoreboard"</span>,
  <span class="hljs-string">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-string">"author"</span>: <span class="hljs-string">"Mihail Gaberov"</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"vitest"</span>,
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"vite build"</span>,
    <span class="hljs-string">"preview"</span>: <span class="hljs-string">"vite preview"</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"@testing-library/jest-dom"</span>: <span class="hljs-string">"^5.16.5"</span>,
    <span class="hljs-string">"@testing-library/react"</span>: <span class="hljs-string">"^14.0.0"</span>,
    <span class="hljs-string">"@types/react"</span>: <span class="hljs-string">"^18.0.28"</span>,
    <span class="hljs-string">"@types/react-dom"</span>: <span class="hljs-string">"^18.0.11"</span>,
    <span class="hljs-string">"@vitejs/plugin-react"</span>: <span class="hljs-string">"^3.1.0"</span>,
    <span class="hljs-string">"jsdom"</span>: <span class="hljs-string">"^21.1.1"</span>,
    <span class="hljs-string">"sass"</span>: <span class="hljs-string">"^1.59.3"</span>,
    <span class="hljs-string">"vite"</span>: <span class="hljs-string">"^4.2.0"</span>,
    <span class="hljs-string">"vitest"</span>: <span class="hljs-string">"^0.29.7"</span>
  }
}
</code></pre>
<h3 id="heading-installation">🧑🏻‍💻 Installation</h3>
<p>In this step I assume you are starting from scratch. We are going to use Vite for scaffolding the project. In order to do that you need to have Node.js installed on your system – at least version 14..18. I suggest that you update it to the latest stable version. And as a package manager you may go with either <a target="_blank" href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">npm</a> or <a target="_blank" href="https://classic.yarnpkg.com/lang/en/docs/install/">yarn</a>. In my case I use yarn.</p>
<p>In your terminal app run the following:</p>
<pre><code class="lang-jsx">yarn create vite
</code></pre>
<p>And then follow the prompt.</p>
<p>Some of you may ask “Why Vite?” Here is a <a target="_blank" href="https://cloudfour.com/thinks/in-praise-of-vite/">little praise</a> of Vite that should answer that question.</p>
<p>After doing the installation we have the bare skeleton of a React app that we can start building on. Here is how it looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/default-vite-project.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Default Vite Project</em></p>
<p>💡 When starting such projects, I usually wipe off what’s there by default. Meaning that I delete the files I don’t plan to use, clean up App.jsx, and update the index.html file.</p>
<p>Another thing you may have noticed already is that the only pure CSS file I kept is <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/index.css">index.css</a>. This is one of the files that comes by default from Vite’s installation. I kept it as it is because it contains some basic styling that I didn’t want to move anywhere else.</p>
<p>After the initial cleaning and adding the files for styling and testing App.jsx, the project looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/first-steps.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>First steps of the project in App.jsx</em></p>
<p>In the screenshot above, you can see what the App.jsx file looks like after my changes. I placed comments as placeholders for where the components I need to create will be.</p>
<p>We are now ready to start building the components in question. Usually there are several different approaches you can take when deciding what to begin with. In this case, we'll start from top to bottom, create the header component, then jump to the scoreboard component, and in the end we'll build the footer component.</p>
<p>You could also decide to first build the essential part of the app, that is the scoreboard and in the end to add the “hat” and the “shoes”.</p>
<p>But in any case, what I recommend is to create empty components for each of the placeholders we have placed, based on the idea we have in mind about what our application will be.</p>
<p>Usually I use something called “Live Templates” in my <a target="_blank" href="https://www.jetbrains.com/webstorm/">IDE</a> (in case you are using different IDE, I am sure there is an alternative for it) that can generate different types of a boilerplate code.</p>
<p>In our case I use it for generating empty functional React components. This comes in very handy at this stage of the development process, because we can quickly create our project's components, leaving them empty. Then later we can start filling them up with content.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/live-templates-webstorm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Live Templates in Webstorm</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/generate-boilerplate.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Generating boilerplate code for the components we will create</em></p>
<p>And this is what the result of the above looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/generated-boilerplate.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Generated boilerplate code for a functional component in React</em></p>
<h3 id="heading-how-to-build-the-header">🧩 How to Build the Header</h3>
<p>In order to make the application to look more like a real-life one, I decided to add a small logo in the left part of the header, and a title next to it. Let’s see how it will look like in the browser and then how to implement it with code:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/header-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Application Header</em></p>
<p>First, I did a quick Google search and chose an appropriate image (the cup). I made sure to pick a SVG file for several reasons.</p>
<p>First and most important is the performance and the adjustability that come from it. And second, in the Vite default settings there is already an SVG logo added. So the only thing you need to do is to replace the existing one with yours. And then add some styling if necessary.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/replace-svg-logo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>SVG logo</em></p>
<p>Let’s now look at the code of our brand new header component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> <span class="hljs-string">'./header.module.scss'</span>
<span class="hljs-keyword">const</span> Header = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">'./logo.svg'</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">'FIFA World Cup Scoreboard'</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>FIFA World Cup Scoreboard<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header
</code></pre>
<p>If you keep the logo file in the <code>public</code> folder, you don’t need to worry about the path to the image. It’s taken care by Vite and you refer to it as it’s shown in the code above. The import statement in the beginning applies all styles to the header that make it looks like the picture.</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fdbe11</span>;
  <span class="hljs-attribute">justify-content</span>: flex-start;
  <span class="hljs-attribute">align-items</span>: center;

  <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">3rem</span>;
    <span class="hljs-attribute">height</span>: auto;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">1rem</span>;
  }
}
</code></pre>
<p>After adding some tests, the content of the component folder looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/header-component-directory.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Header component directory</em></p>
<p>I mentioned in the beginning of the article that we will use Vitest and React Testing Library to write the units/components tests for this application. Here is how the tests for the header look:</p>
<pre><code class="lang-scss">import { render, screen } from '<span class="hljs-keyword">@testing-library</span>/react<span class="hljs-string">'
import { beforeEach, describe, expect, it } from '</span>vitest<span class="hljs-string">'
import Header from "./index"

describe('</span>Header<span class="hljs-string">', () =&gt; {
    beforeEach(() =&gt; {
        render(&lt;Header /&gt;)
    })
    it('</span>renders correctly the app title<span class="hljs-string">', async () =&gt; {
        expect(screen.getByText(/FIFA World Cup Scoreboard/i)).toBeVisible()
    })

    it('</span>renders correctly the app logo<span class="hljs-string">', async () =&gt; {
        const logo = screen.getByAltText('</span>FIFA World Cup Scoreboard<span class="hljs-string">');
        expect(logo).toHaveAttribute('</span>src<span class="hljs-string">', '</span>./logo.svg<span class="hljs-string">')
    })
})</span>
</code></pre>
<p>As you probably can guess just by reading the tests, what we're doing here is checking for the app title and then the logo we saw on the left.</p>
<p>Congratulations 🎉 You just finished the implementation of the first building block of your application. Let’s continue now with the main area. This is where the essential functionality of the scoreboard will be.</p>
<h3 id="heading-how-to-build-the-scoreboard">🧩 How to Build the Scoreboard</h3>
<p>The scoreboard supports two screens: one showing the scores of the games that are currently being played, and another one showing a summary of the end results.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/current-games-screen.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Current Games screen</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/summary-screen.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Summary screen</em></p>
<p>When I see this kind of layout design, I usually start thinking about a grid. Because, what’s a grid if not just rows and columns?</p>
<p>The modern CSS language has support for grid systems with just few lines of code, as you will see a bit later in this section. For example, to achieve this result I used the following styles:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.grid</span> {
  <span class="hljs-attribute">list-style-type</span>: none;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">display</span>: grid;
  gap: <span class="hljs-number">1rem</span>;
  grid-template-<span class="hljs-attribute">columns</span>: repeat(auto-fit, minmax(<span class="hljs-number">300px</span>, <span class="hljs-number">1</span>fr));
}
</code></pre>
<p>Whenever I am about to implement a UI like this in a component based library such as React, I tend to mentally split it to separated blocks. These will turn into components.</p>
<p>Let me show you visually what I mean by this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/thinking-components.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Thinking components - visual representation</em></p>
<p>I hope you can figure out what I am showing you on the picture above.</p>
<p>This is how I mentally split the design layout we have in place into representational components.</p>
<p>After defining the constituent parts of our application, it’s time to move forward and implement them in code.</p>
<p>First we need the <code>ScoreboardsGrid</code> component that will hold all the smaller ones and will contain the logic for managing different events based on time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/scoreboards-grid-directory.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>ScoreboardsGrid component directory</em></p>
<p>As you may have noticed, in addition to the usual files, here we have one more – <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/components/ScoreboardsGrid/ScoresReducer.js">ScoresReducer.js</a>. This is where our reducer logic lives. It’s responsible for manipulating the application state, depending on what actions are being triggered. In other words, this is where we actually update the score when a team scores, and also where we start and finish games.</p>
<p>In the return statement of the component we make use of the rest of the components we defined earlier.</p>
<pre><code class="lang-jsx">...
...
...
return (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            {timeElapsed === 0 ?
                <span class="hljs-tag">&lt;&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">MessageBoard</span> <span class="hljs-attr">message</span>=<span class="hljs-string">{getScoreBoardStateMessage()}/</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.grid}</span>&gt;</span>
                        {gamesToRender?.map(pairScore =&gt; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">Scoreboard</span>
                                <span class="hljs-attr">key</span>=<span class="hljs-string">{crypto.randomUUID()}</span>
                                <span class="hljs-attr">pairScore</span>=<span class="hljs-string">{pairScore}</span>
                                <span class="hljs-attr">status</span>=<span class="hljs-string">{getGameStatus(pairScore.startedGame)}/</span>&gt;</span>))}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/&gt;</span></span> :
                <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MessageBoard</span> <span class="hljs-attr">message</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">Games</span> <span class="hljs-attr">are</span> <span class="hljs-attr">about</span> <span class="hljs-attr">to</span> <span class="hljs-attr">start</span> <span class="hljs-attr">in</span> ${<span class="hljs-attr">timeElapsed</span>} <span class="hljs-attr">seconds.</span>`}/&gt;</span></span>
            }
        &lt;/&gt;
    );
</code></pre>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/components/ScoreboardsGrid/index.jsx">rest of the code</a> consists of a few helper methods, <code>dispatch</code> methods, and the logic for starting and stopping the timers.</p>
<p>From here, things become even easier. We just are going to use our smaller components for displaying different things in our scoreboard app.</p>
<p>For example, the <code>MessageBoard</code> component is just a container that shows in a stylistic way a bit of a string, passed via its props. Here is the implementation:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./message-board.module.scss"</span>;

<span class="hljs-keyword">const</span> Index = <span class="hljs-function">(<span class="hljs-params">{ message }</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.message}</span>&gt;</span>
            {message}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Index;
</code></pre>
<p>Same goes for the <code>[GameStatus](https://github.com/mihailgaberov/scoreboard/tree/main/src/components/GameStatus)</code> and <code>[Result](https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Result)</code> components. The difference between the later ones is that <code>Result</code> gets two arguments – the name of each team in a game – and displays them with a dash (’-’) in the middle. <code>GameStatus</code> is just showing whatever we pass through via its props, which happens to be a string signifying that a game is playing at the moment.</p>
<p>The only component that is a bit different is <code>TeamView</code>, because it contains both an image and text, representing the teams. The code itself is far from complicated. See for yourself:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./team-view.module.scss"</span>;

<span class="hljs-keyword">const</span> TeamView = <span class="hljs-function">(<span class="hljs-params">{teamData}</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.team}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">flagcdn.com</span>/${<span class="hljs-attr">teamData.countryCode</span>}<span class="hljs-attr">.svg</span>`} <span class="hljs-attr">width</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">teamData.name</span>}`}/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{teamData.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TeamView;
</code></pre>
<p>Here I used a regular HTML <code>img</code> tag, setting the width using inline styling. The rest is pretty straightforward.</p>
<p>With that, our coding job is more or less done. As you have probably seen, all the component have tests. These, in most cases, are just simple checks to see whether the component is being rendered correctly.</p>
<p>Maybe the most interesting tests to talk about are the ones we added for the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/components/ScoreboardsGrid/index.jsx">ScoreboardGrid</a> component.</p>
<p>This is so because we are using <a target="_blank" href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-waitfor-to-wait-for-elements-that-can-be-queried-with-find">React Testing Library</a> asynchronicity support to test the state of the component in different moments of the time. In this way we are able to test the initial timer ticking, before the games start. And after it expires, we can use it to check that our Current Games screen is displayed correctly. I'm pasting the code here as well, for easier reading.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>
<span class="hljs-keyword">import</span> { describe, expect, it } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>
<span class="hljs-keyword">import</span> ScoreboardsGrid <span class="hljs-keyword">from</span> <span class="hljs-string">"./index"</span>

describe(<span class="hljs-string">'ScoreboardsGrid'</span>, <span class="hljs-function">() =&gt;</span> {
    it(<span class="hljs-string">'renders correctly all available scoreboards'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ScoreboardsGrid</span> /&gt;</span></span>)

        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Games are about to start in 3 seconds./i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Games are about to start in 2 seconds./i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Games are about to start in 1 seconds./i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Argentina/i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Australia/i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Spain/i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Brazil/i</span>)).toBeVisible()
    })
})
</code></pre>
<p>After you've completed the implementation of the entire application and have a cup of ☕ or a glass of 🚰, it’s time to think about possible improvements.</p>
<p>For example, if we have more time to work on this project, what would we add or change, to make it an even better scoreboard app?</p>
<h3 id="heading-how-to-build-the-footer">🧩 How to Build the Footer</h3>
<p>To give the application a more complete look, I’ve decided to add a footer component as well. This is how it looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/footer-component.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Footer Component</em></p>
<p>The implementation of it is pretty simple, too. We have two links to social platforms and a bit of copyright text. Here is how I coded it:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./footer.module.scss"</span>;
<span class="hljs-keyword">import</span> packageJson <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../package.json'</span>;

<span class="hljs-keyword">const</span> Footer = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> currentYear = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear();

  <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.footer}</span> <span class="hljs-attr">data-cy</span>=<span class="hljs-string">"footer"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.footerLinks}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                <span class="hljs-attr">href</span>=<span class="hljs-string">"&lt;https://twitter.com/mihailgaberov&gt;"</span>
                <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
                <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
                <span class="hljs-attr">data-cy</span>=<span class="hljs-string">"twitterLink"</span>
            &gt;</span>
              twitter
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>{" "}
            <span class="hljs-symbol">&amp;bull;</span>{" "}
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                <span class="hljs-attr">href</span>=<span class="hljs-string">"&lt;https://github.com/mihailgaberov&gt;"</span>
                <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
                <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
                <span class="hljs-attr">data-cy</span>=<span class="hljs-string">"githubLink"</span>
            &gt;</span>
              github
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.footerCopyrights}</span>&gt;</span>
            © {packageJson.author} {currentYear}. All rights reserved.
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.version}</span>&gt;</span>v.{packageJson.version}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Footer;
</code></pre>
<p>Again, in the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Footer">component folder</a> you will find the other files that are necessary for applying the styles and the tests for that component.</p>
<p>The code looks a bit cluttered because I <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul">used an unordered list</a> and added the links and the text as a separated <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li">list elements</a>. Then I used the tests to verify that the elements I want are rendered correctly.</p>
<p>Here is the essential part of the code that is doing this:</p>
<pre><code class="lang-jsx">it(<span class="hljs-string">'renders correctly social links'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    expect(screen.getByText(<span class="hljs-regexp">/twitter/i</span>)).toBeVisible()
    expect(screen.getByText(<span class="hljs-regexp">/github/i</span>)).toBeVisible()
  });

  it(<span class="hljs-string">'has social links working correctly'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    expect(screen.getByText(<span class="hljs-string">'twitter'</span>).closest(<span class="hljs-string">'a'</span>)).toHaveAttribute(<span class="hljs-string">'href'</span>, <span class="hljs-string">'&lt;https://twitter.com/mihailgaberov&gt;'</span>);
    expect(screen.getByText(<span class="hljs-string">'github'</span>).closest(<span class="hljs-string">'a'</span>)).toHaveAttribute(<span class="hljs-string">'href'</span>, <span class="hljs-string">'&lt;https://github.com/mihailgaberov&gt;'</span>);
  });

  it(<span class="hljs-string">"should contain copyright info"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(screen.getByText(<span class="hljs-regexp">/© Mihail Gaberov 2023. All rights reserved./i</span>)).toBeVisible()
  });

  it(<span class="hljs-string">"should contain version number"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(screen.getByText(<span class="hljs-regexp">/v.1.0.0/i</span>)).toBeVisible()
  });
</code></pre>
<h3 id="heading-possible-improvements">🚀 Possible Improvements</h3>
<ul>
<li><p>Add a clock under the game status, say counting down the seconds, to make it look more like a real-time app</p>
</li>
<li><p>Add some animation when updating the scores to make it easier for the user to spot the change</p>
</li>
<li><p>Add some interactivity in general:</p>
<ul>
<li><p>Clicking on each game leads to a details pane with the match details</p>
</li>
<li><p>Option for selecting a favourite team</p>
</li>
<li><p>Add another page/tab where users can read a history summary of the past games</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That was a fun challenge. Especially if you are a football fan, right? ⚽</p>
<p>We learned about several interesting topics.</p>
<p>First we learned what a scoreboard is, and the ‘why’ and the ‘how’ behind it.</p>
<p>Then we learned about Vite and Vitest, which I think are currently the best tools to use when making a React app – especially if you don’t want to start from scratch and deal with Webpack manually.</p>
<p>Then we saw how to leverage JavaScript timeouts with React hooks, and thus create some kind of time-based interactivity.</p>
<p>And last, but not least, we had fun, didn't we? 🕺🏻</p>
<p>Thanks for reading 🙏🏻</p>
<h2 id="heading-references"><strong>References:</strong></h2>
<ul>
<li><p>Resources from Josh W. Comeau: for <a target="_blank" href="https://www.joshwcomeau.com/snippets/react-hooks/use-interval/">use-interval</a> and <a target="_blank" href="https://www.joshwcomeau.com/snippets/react-hooks/use-timeout/">use-timeout</a> and <a target="_blank" href="https://www.joshwcomeau.com/snippets/react-hooks/use-random-interval/">use-random-interval</a></p>
</li>
<li><p>An article from Kent C. Dodds about <a target="_blank" href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-waitfor-to-wait-for-elements-that-can-be-queried-with-find">common mistakes devs make with the React Testing Library</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Use Vite to Speed Up Web Development ]]>
                </title>
                <description>
                    <![CDATA[ Vite is a powerful build tool and development server designed to speed up modern web development. We just published a full Vite course on the freeCodeCamp.org YouTube channel that will teach you how to use Vite effectively and efficiently to streamli... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/complete-vite-course-for-beginners/</link>
                <guid isPermaLink="false">66b20157a8b92c9329236421</guid>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 23 Mar 2023 13:20:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/03/vite.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Vite is a powerful build tool and development server designed to speed up modern web development.</p>
<p>We just published a full Vite course on the freeCodeCamp.org YouTube channel that will teach you how to use Vite effectively and efficiently to streamline your web development workflow.</p>
<p>Arsalan Khattak developed this course. Arsalan is a frontend developer and DevRel engineer on a mission to help developers learn frontend technologies. He has created many popular courses and has taught at events such as Google's DevFest and Microsoft's Reactor.</p>
<p>Vite is a modern build tool and development server designed to make web development faster and more efficient. Unlike traditional build tools, Vite uses an optimized development server that leverages native ES modules, resulting in lightning-fast development and build times. With Vite, you can create and maintain modern web applications with ease and speed.</p>
<p>What makes Vite fast? Vite's speed is mainly due to its innovative development server, which allows for lightning-fast hot module replacement (HMR) and static asset handling. With HMR, changes you make to your code will be reflected in your browser almost instantly, without requiring a full page refresh. This feature alone can save you hours of development time.</p>
<p>One of the first things you'll learn in this course is how to create a static server with Vite. This will enable you to quickly and easily serve up your web application for local development and testing.</p>
<p>Vite makes it easy to use templates to speed up your development process. You'll learn how to use templates in your Vite project to quickly scaffold out your application and get up and running faster.</p>
<p>Tailwind is a popular utility-first CSS framework that helps you quickly create beautiful, responsive web applications. In this course, you'll learn how to integrate Tailwind with Vite, allowing you to take advantage of its powerful features to streamline your development process.</p>
<p>In this course, you'll also learn how to use environment variables in your Vite project. This will enable you to create dynamic web applications that can adapt to different environments, such as development, staging, and production.</p>
<p>Once you've created your Vite project, you'll need to deploy it to a hosting platform. In this course, you'll learn how to deploy your Vite project to popular hosting platforms such as GitHub, Netlify, and Vercel.</p>
<p>Finally, you'll learn how to configure Vite to suit your specific development needs. This will enable you to customize Vite to work best with your particular development environment, workflow, and tools.</p>
<p>By the end of this course, you'll have a solid understanding of how to use Vite to make modern web development faster and more efficient. You'll be able to create and maintain modern web applications with ease and speed, saving you time and effort in the development process.</p>
<p>Watch the full course below or <a target="_blank" href="https://www.youtube.com/watch?v=VAeRhmpcWEQ">on the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VAeRhmpcWEQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Setup React and Tailwind CSS with Vite in a Project ]]>
                </title>
                <description>
                    <![CDATA[ Tailwind CSS is a popular CSS framework, and React is one of the most popular JavaScript libraries. And Tailwind CSS and React are a great combo to use if you're building a frontend project. In this article, you will learn how to setup your coding en... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-install-tailwindcss-in-react/</link>
                <guid isPermaLink="false">66c8c94cc4cede4e0083f73b</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Segun Ajibola ]]>
                </dc:creator>
                <pubDate>Mon, 09 Jan 2023 18:49:56 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/Parameters-vs-Arguments--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Tailwind CSS is a popular CSS framework, and React is one of the most popular JavaScript libraries.</p>
<p>And Tailwind CSS and React are a great combo to use if you're building a frontend project.</p>
<p>In this article, you will learn how to setup your coding environment with Vite, install React and Tailwind CSS with their latest versions, and start building your projects right away.</p>
<p>We will be using these tools:</p>
<ul>
<li><a target="_blank" href="https://code.visualstudio.com/download">VSCode</a> for our code editor</li>
<li><a target="_blank" href="https://nodejs.org/en/download/">Node.js</a> for our package manager</li>
<li><a target="_blank" href="https://vitejs.dev/">Vite</a> for our development environment</li>
</ul>
<p>If you don't have these tools installed, you can do so by clicking the links for each one above.</p>
<p>After setting up Node.js for your VSCode, you can now use Node.js to install Vite for your project using the terminal.</p>
<h2 id="heading-step-1-create-your-project-folder">Step 1 – Create Your Project Folder</h2>
<p>Open your terminal, and navigate to the folder where you want to build your project – for example Desktop. Input the command below in the terminal and click <code>enter</code>:‌</p>
<pre><code class="lang-node.js">npm create vite@latest your-project-name -- --template react
</code></pre>
<p>The command above will create your project folder.‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-03-000708.png" alt="My project name is &quot;food-app&quot;, the food-app folder will be created in the Programming folder on my Desktop" width="600" height="400" loading="lazy">
<em>My project name is "food-app", the food-app folder will be created in the Programming folder on my Desktop</em></p>
<p>‌Note that we have used <code>-- --template react</code> to specify that we are building a React app with Vite.</p>
<h2 id="heading-step-2-navigate-to-your-project-folder">Step 2 – Navigate to Your Project Folder</h2>
<p>Input the command below in your terminal and click <code>enter</code>:</p>
<pre><code class="lang-shell">cd food-app
</code></pre>
<p>‌This command will navigate to your project folder. You should have this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-03-001414.png" alt="Inputing &quot;cd food-app&quot; in terminal to navigate to the &quot;food-app&quot; folder" width="600" height="400" loading="lazy">
<em>Inputing "cd food-app" in terminal to navigate to the "food-app" folder</em></p>
<h2 id="heading-step-3-install-tailwind-css-and-other-dependencies"><strong>Step 3 – Install Tailwind CSS and Other Dependencies</strong></h2>
<p>Input the command below in your terminal and click <code>enter</code>:</p>
<pre><code class="lang-node">npm install -D tailwindcss postcss autoprefixer
</code></pre>
<p>This command will install the following:</p>
<ul>
<li>The Tailwind CSS framework</li>
<li>Post CSS, which provides plugins to perform different functionalities like prefixes in Vanilla CSS</li>
<li>Autoprefixer, which is a PostCSS plugin to parse CSS and add vendor prefixes to CSS rules.</li>
</ul>
<p>Your folder should look like this in your VSCode:‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-03-004354.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Confirm that you have the below text in your <code>package.json</code>‌:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-03-004416.png" alt="Image" width="600" height="400" loading="lazy">
<em></em></p><div id="ember183" class="miw-100 tc bn form-text bg-transparent pr8 pl8 ember-view" data-kg-has-link-toolbar="true" data-koenig-dnd-disabled="true"><div class="koenig-basic-html-input__editor-wrappper"><div class="koenig-basic-html-input__editor __mobiledoc-editor" data-gramm="false" data-kg="editor" data-kg-allow-clickthrough="" data-placeholder="Type caption for image (optional)"><p><em>Notice the autoprefixer, postcss and tailwindcss dependencies from line 19 - 21. The version number might have changed when you read this.</em></p></div></div></div><p></p>
<h2 id="heading-step-4-generate-the-configuration-files">Step 4 – Generate the Configuration Files</h2>
<p>Input the command below in your terminal and click <code>enter</code>:</p>
<pre><code class="lang-node">npx tailwindcss init -p
</code></pre>
<p>This command generates <code>tailwind.config.cjs</code> and<code>postcss.config.cjs</code> configuration files, also known as config files. They help you interact with your project and customize everything.</p>
<h2 id="heading-step-5-configure-source-paths">Step 5 – Configure Source Paths</h2>
<p>Add the paths to all of your template files in your <code>tailwind.config.cjs</code> file. Template files include HTML templates, JavaScript components, and other source files that contain Tailwind class names. This is to make sure that vanilla CSS is generated for the corresponding elements.</p>
<p>Your <code>tailwind.config.cjs</code> looks like this for now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-03-235907.png" alt="Current config file named as tailwind.config.cjs, it contains module.export object to customize tailwind with property like content, theme and plugins" width="600" height="400" loading="lazy">
<em>Current config file named as tailwind.config.cjs, it contains the module.export object to customize tailwind with property like content, theme and plugins</em></p>
<p>Add this in your content section.</p>
<pre><code class="lang-json"><span class="hljs-string">"./index.html"</span>,


<span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
</code></pre>
<p>So your file should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-04-000648.png" alt="config file after updating the content property" width="600" height="400" loading="lazy">
<em>Config file after updating the content property</em></p>
<h2 id="heading-step-6-add-tailwind-directives-to-your-css"><strong>Step 6 – Add Tailwind Directives to Your CSS</strong></h2>
<p>Tailwind directives are custom Tailwind-specific statements that instruct CSS how to behave. You'll need to add directives for three of Tailwind’s layers. </p>
<p><code>@tailwind base</code> injects Tailwind's base styles and base styles registered by plugins, <code>@tailwind components</code> injects Tailwind's component classes and component classes registered by plugins, while <code>@tailwind utilities</code> injects Tailwind's utility classes and utility classes registered by plugins.</p>
<p>Add the statements below to your <code>./src/index.css</code> file:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>Your <code>index.css</code> file contains some default styling. You can clear all that and paste the three lines of directives above.</p>
<h2 id="heading-step-7-start-your-vite-server">Step 7 – Start Your Vite Server</h2>
<p>Run your build process with the <code>npm run dev</code> command in the terminal. You should get this message below in your terminal‌:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-04-005534.png" alt="The message you get after running your Vite server that provides localhost link, network and help." width="600" height="400" loading="lazy">
<em>The message you get after running your Vite server that provides localhost link, network, and help.</em></p>
<p>Hold the <code>ctrl</code> key and click on the link at Local – here it's http://127.0.0.1:5174. It will open a new tab in your browser if you do that.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-04-005850.png" alt="A screenshot of the webpage on first run" width="600" height="400" loading="lazy">
<em>A screenshot of the webpage on first run</em></p>
<p>Our styles are broken because we cleared the default CSS in the <code>index.css</code> file to input our directives.</p>
<h2 id="heading-step-8-start-writing-tailwind-css">Step 8 – Start Writing Tailwind CSS</h2>
<p>You can start using Tailwind’s utility classes to style your content. Navigate to your <code>App.jsx</code> file, where you should see this below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-04-184158.png" alt="Screenshot of the App.jsx file" width="600" height="400" loading="lazy">
<em>Screenshot of the App.jsx file</em></p>
<p>Clear the return element starting from line 9, and replace it with the text below to test your Tailwind to know if it is working. Input this:</p>
<pre><code class="lang-jsx">&lt;h1 className=<span class="hljs-string">"text-3xl font-bold underline text-center"</span>&gt;Hello world!&lt;/h1&gt;
</code></pre>
<p>Now you have this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-04-184804.png" alt="Adding the h1 element to the App.jsx file with tailwindcss styles applied" width="600" height="400" loading="lazy">
<em>Adding the h1 element to the App.jsx file</em></p>
<p>According to the above image, <code>text-3xl font-bold text-red-500 underline text-center</code> has been added as a className to the <code>div</code> element. This is the standard for writing Tailwind CSS styling.</p>
<p>You can learn more about Tailwind classnames <a target="_blank" href="https://tailwindcss.com/docs/">here</a>. Your browser should update automatically.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/Screenshot-2023-01-04-184720.png" alt="Screenshot of the webpage after add the h1 element, showing Hello World with tailwind CSS styles applied." width="600" height="400" loading="lazy">
<em>Screenshot of the webpage after add the h1 element</em></p>
<p>You can now start building your React projects and style them with Tailwind CSS.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>You have now created a React and Tailwind CSS app using Vite, a frontend build tool. You have learned what Vite is and how to create a Vite app with a React template, as well as how to install Tailwind and other dependencies.</p>
<p>Thanks for reading this article. If you enjoyed it, consider sharing it to help other developers.</p>
<p>You can reach me on <a target="_blank" href="https://twitter.com/iamsegunajibola">Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/segunajibola/">LinkedIn</a> and <a target="_blank" href="https://github.com/segunajibola">GitHub</a>.</p>
<p>Happy Learning.‌</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Modern Documentation Site with VitePress ]]>
                </title>
                <description>
                    <![CDATA[ By Victor Eke Documentation is a crucial aspect of software development. But developers often neglect it because it can be a hassle to maintain. This is why it's important to use tools that help simplify this process.  In this tutorial, you'll learn ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-modern-documentation-site-with-vitepress/</link>
                <guid isPermaLink="false">66d4617738f2dc3808b79115</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 16 Nov 2022 17:01:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/How-to-build-a-modern-documentation-site-with-vitepress-by-Victor-Eke.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Victor Eke</p>
<p>Documentation is a crucial aspect of software development. But developers often neglect it because it can be a hassle to maintain. This is why it's important to use tools that help simplify this process. </p>
<p>In this tutorial, you'll learn how to build a complete docs site quickly by utilizing a modern tool called VitePress.</p>
<h2 id="heading-what-is-vitepress">What is VitePress?</h2>
<p><a target="_blank" href="https://vitepress.vuejs.org/">VitePress</a> is a simple and performant static site generator built on top of <a target="_blank" href="https://vitejs.dev">Vite</a> that lets you create docs in a matter of minutes. It is powered by V<a target="_blank" href="https://vuejs.org/">uejs</a> and Vite with built-in customizable components. </p>
<p>VitePress powers some popular documentation sites like Vuejs, V<a target="_blank" href="https://vitest.dev/">itest</a>, <a target="_blank" href="https://faker.js/dev">faker.js</a>, and Vite itself.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you need to have a basic understanding of the following:</p>
<ul>
<li><a target="_blank" href="https://daringfireball.net/projects/markdown/">Markdown</a> syntax</li>
<li>Basic understanding of NPM and Vite</li>
</ul>
<p>Here's a screenshot of what you'll build by the end of this tutorial:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/final-works-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Want to play around with it? Check out the <a target="_blank" href="http://adocs.vercel.app/">live demo</a>. Also, the source code for this can be found on <a target="_blank" href="https://github.com/Evavic44/adocs">GitHub</a>.</p>
<h2 id="heading-step-1-create-a-new-project">Step 1: Create a New Project</h2>
<p>If you already have a folder created, you can skip this step and go on to the next one. If not, use the following command to create a project folder and move into the folder.</p>
<pre><code class="lang-bash">mkdir project-name
<span class="hljs-built_in">cd</span> project-name
</code></pre>
<p>Next you need to initialize the project with your preferred package manager. I'll be using NPM for the rest of this guide.</p>
<pre><code class="lang-js">npm init
<span class="hljs-comment">// or use this command if you want to skip all the questions</span>
npm init -y
</code></pre>
<p>If you used the first command, you'll be prompted with certain questions, so just complete them as appropriate. </p>
<p>After a successful operation, you should have a <code>package.json</code> file in your root directory. This is where the VitePress dev dependency will be installed.</p>
<h2 id="heading-step-2-install-vitepress">Step 2: Install VitePress</h2>
<p>The next step is to add VitePress and Vue as dev dependencies to your project, like this:</p>
<pre><code class="lang-bash">npm install --dev vitepress vue
</code></pre>
<p>You've successfully installed VitePress and Vue and added them as dev dependencies. Now you can start creating creating your respective doc files. </p>
<p>But before you do that, I believe it's essential to explain how VitePress works.</p>
<h2 id="heading-how-does-vitepress-vork">How Does VitePress Vork?</h2>
<p>VitePress makes use of Markdown <code>.md</code> files for its markup which is automatically converted into static HTML. In other for this to work, a special folder called <code>docs</code> gets created in the root directory.</p>
<p>This folder behaves similarly to the <code>pages</code> folder in NextJS, where any <code>.js</code> file created in the directory is automatically treated as a web page. In this case a file called <code>index.md</code> will be the treated as <code>index.html</code> and serve as the root of your docs template.</p>
<p>Now that you understand how that works, you can create your respective doc files.</p>
<h2 id="heading-step-3-create-the-respective-doc-files">Step 3: Create the Respective Doc Files</h2>
<p>You can create the docs folder and the <code>index.md</code> file manually, or you can do it with the terminal like a hacker.</p>
<pre><code class="lang-bash">mkdir docs &amp;&amp; <span class="hljs-built_in">echo</span> <span class="hljs-string">'# Hello VitePress'</span> &gt; docs/index.md
</code></pre>
<p>This command simply creates a folder called <code>docs</code> and adds an <code>index.md</code> file containing a <code>h1</code> element that says "Hello World". </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/create-respective-files.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With this, you can boot up your dev environment to see what has been created so far.</p>
<h2 id="heading-step-4-boot-up-your-dev-environment">Step 4: Boot Up Your Dev Environment</h2>
<p>In order to run your docs locally, you need to add the following scripts inside the <code>package.json</code> file. Simply copy the code below and replace the <code>"script"</code> object with it:</p>
<pre><code class="lang-js"><span class="hljs-comment">// package.json</span>
<span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"docs:dev"</span>: <span class="hljs-string">"vitepress dev docs"</span>,
    <span class="hljs-string">"docs:build"</span>: <span class="hljs-string">"vitepress build docs"</span>,
    <span class="hljs-string">"docs:serve"</span>: <span class="hljs-string">"vitepress serve docs"</span>
  },
</code></pre>
<p>Finally, the documentation site can be served on a local server by running the command below:</p>
<pre><code class="lang-bash">npm run docs:dev
</code></pre>
<p>This will start a hot-reloading development server at <code>http://localhost:5173</code>, and you can visit it to see your docs site.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/boot-dev-server.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>All you had to do was add the markup and VitePress handled the appearance from its template engine. In the next session, you'll learn how you can customize the docs to fit your needs.</p>
<h2 id="heading-how-to-customize-your-docs-with-vitepress">How to Customize Your Docs with VitePress</h2>
<p>First create a <code>.vitepress</code> folder inside the docs directory you created earlier on. This is where all VitePress-specific files will be placed. </p>
<p>Inside this new directory, you need a <code>config.js</code> file. Again, you can use the terminal command like so:</p>
<pre><code class="lang-bash">mkdir .vitepress &amp;&amp; touch .vitepress/config.js
</code></pre>
<p>To test this config file, you can start by changing the meta title and description of your docs site. Copy this markup and paste into the <code>config.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/config.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">title</span>: <span class="hljs-string">'Adocs'</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">'An awesome docs template built by me'</span>
}
</code></pre>
<p>If you check the dev tools, you should see the changes in the meta title and description.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/title-and-description.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-update-the-title-and-logo">How to Update the Title and Logo</h3>
<p>In order to change the logo title and add an image, copy the markup below and paste it into a new object called <code>themeConfig</code> inside the same <code>config.js</code> file. This will overwrite the current title and add a logo to your docs site.</p>
<pre><code class="lang-js"><span class="hljs-comment">// config.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">themeConfig</span>: {
    <span class="hljs-attr">logo</span>: <span class="hljs-string">"/logo.svg"</span>,
    <span class="hljs-attr">siteTitle</span>: <span class="hljs-string">"Adocs"</span>,
  },
};
</code></pre>
<p>For the image source, you can pass in an image URL or specify the path to a local image. To do it locally, make sure you place the image within the <code>public</code> directory.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/logo-and-title.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Note that files in the public directory are served at the root path. So instead of ../public/logo.svg, just use /logo.svg.</p>
<h3 id="heading-how-to-customize-the-navbar">How to Customize the Navbar</h3>
<p>Customizing the <code>Navbar</code> is a pretty straightforward process as well. Inside your <code>themeConfig</code> file, paste the markup below. Here we have an object that contains two properties: the anchor text <code>text</code>, and the path <code>link</code> defines the URL path.</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/config.js</span>
{  
  <span class="hljs-comment">// ...</span>
   <span class="hljs-attr">nav</span>: [
    { <span class="hljs-attr">text</span>: <span class="hljs-string">"About"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/about"</span> },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">"Contact"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/contact"</span> },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">"Guide"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/guide"</span> },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">"Configs"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/configs"</span> },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">"Changelog"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"https://github.com/..."</span> },
  ],
  <span class="hljs-comment">// ...     </span>
}
</code></pre>
<p>Essentially navigating to <a target="_blank" href="http://localhost:5173/about">localhost:5173/about</a> should take you to an about page (though we haven't created that yet).</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/navigaiton-menu.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Navigation links can also be dropdown menus, too. To add one, simply replace any of the <code>links</code> property with the items object which contains an array of links.</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/config.js</span>
{
  <span class="hljs-attr">text</span>: <span class="hljs-string">"Changelog"</span>,
  <span class="hljs-attr">items</span>: [
   { <span class="hljs-attr">text</span>: <span class="hljs-string">"v0.0.1"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/item-1"</span> },
   { <span class="hljs-attr">text</span>: <span class="hljs-string">"v0.0.2"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/item-2"</span> },
   { <span class="hljs-attr">text</span>: <span class="hljs-string">"v0.0.3"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/item-3"</span> },
  ],
},
</code></pre>
<p>Now the changelog will become a dropdown menu with the respective links you pass inside.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/dropdown-menu.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-add-social-icons">How to Add Social Icons</h3>
<p>Navigation menus usually have social icons that visitors can use to visit your social platforms. To add them, define a new object called <code>socialLinks</code> inside <code>themeConfig</code> and simply pass in the social icon and the link you want it to navigate to.</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/config.js</span>
<span class="hljs-attr">socialLinks</span>: [
  { <span class="hljs-attr">icon</span>: <span class="hljs-string">"github"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"https://github.com/Evavic44/adocs"</span> },
  { <span class="hljs-attr">icon</span>: <span class="hljs-string">"twitter"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"https://twitter.com/victorekea"</span> },
  { <span class="hljs-attr">icon</span>: <span class="hljs-string">"discord"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"..."</span> },
]
</code></pre>
<p>By default, only 8 icons (Discord, Facebook, GitHub, Instagram, LinkedIn, Slack, Twitter, and YouTube) are provided. If you want to add a custom icon, use the SVG property to define an svg image. You can get free icons from <a target="_blank" href="https://icones.js.org/">icones.js.org</a>.</p>
<p>For example, here's a snippet of the <code>apple</code> icon. </p>
<pre><code class="lang-js">{
  <span class="hljs-attr">icon</span>: {
    <span class="hljs-attr">svg</span>: <span class="hljs-string">'&lt;svg role="img" width="26.01" height="32" viewBox="0 0 256 315"&gt;&lt;path d="M213.803 167.03c.442 47.58 41.74 63.413 42.197 63.615c-.35 1.116-6.599 22.563-21.757 44.716c-13.104 19.153-26.705 38.235-48.13 38.63c-21.05.388-27.82-12.483-51.888-12.483c-24.061 0-31.582 12.088-51.51 12.871c-20.68.783-36.428-20.71-49.64-39.793c-27-39.033-47.633-110.3-19.928-158.406c13.763-23.89 38.36-39.017 65.056-39.405c20.307-.387 39.475 13.662 51.889 13.662c12.406 0 35.699-16.895 60.186-14.414c10.25.427 39.026 4.14 57.503 31.186c-1.49.923-34.335 20.044-33.978 59.822M174.24 50.199c10.98-13.29 18.369-31.79 16.353-50.199c-15.826.636-34.962 10.546-46.314 23.828c-10.173 11.763-19.082 30.589-16.678 48.633c17.64 1.365 35.66-8.964 46.64-22.262"/&gt;&lt;/svg&gt;'</span>,
    },
  <span class="hljs-attr">link</span>: <span class="hljs-string">"https://www.apple.com/"</span>,
},
</code></pre>
<p>For custom SVG icons, make sure you add the <code>role="img"</code> property to the <code>svg</code> tag, as this allows the string convert it properly.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/social-icons.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-add-a-sidebar">How to Add a Sidebar</h3>
<p>VitePress also comes with built-in components like sidebar menus. To add a sidebar, create an object called <code>sidebar</code> and inside it, add nested objects that take in three values: the nested title, collapsible functionality (default is set to true), and the nested links.</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/config.js</span>
<span class="hljs-attr">sidebar</span>: [
    {
      <span class="hljs-attr">text</span>: <span class="hljs-string">"Section A"</span>,
      <span class="hljs-attr">collapsible</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">items</span>: [
        { <span class="hljs-attr">text</span>: <span class="hljs-string">"Introduction"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/introduction"</span> },
        { <span class="hljs-attr">text</span>: <span class="hljs-string">"Getting Started"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/getting-started"</span> },
      ],
    },
    {
      <span class="hljs-attr">text</span>: <span class="hljs-string">"Section B"</span>,
      <span class="hljs-attr">collapsible</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">items</span>: [
        { <span class="hljs-attr">text</span>: <span class="hljs-string">"Introduction"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/introduction"</span> },
        { <span class="hljs-attr">text</span>: <span class="hljs-string">"Getting Started"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/getting-started"</span> },
      ],
    },
    {
      <span class="hljs-attr">text</span>: <span class="hljs-string">"Section C"</span>,
      <span class="hljs-attr">collapsible</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">items</span>: [
        { <span class="hljs-attr">text</span>: <span class="hljs-string">"Introduction"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/introduction"</span> },
        { <span class="hljs-attr">text</span>: <span class="hljs-string">"Getting Started"</span>, <span class="hljs-attr">link</span>: <span class="hljs-string">"/getting-started"</span> },
      ],
    },
  ],
</code></pre>
<p>By adding <code>collapsible: "true"</code> to the sidebar object, it shows a toggle button to hide/show each section. You can create as many sections as you want.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/sidebar-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can see section B is not collapsible, and we have that nice next page button on the bottom of the page.</p>
<h3 id="heading-how-to-set-up-page-routing">How to Set Up Page Routing</h3>
<p>As explained earlier, VitePress automatically converts every <code>.md</code> file inside the root of the docs directory to static HTML that can be accessed in the address bar. For instance the <code>index.md</code> is converted to <code>index.html</code>, and <code>about.md</code>, <code>about.html</code> and so on. </p>
<p>Since you've created your nav links and pointed them to their respective URLs, you can access these pages easily by creating them.</p>
<pre><code>docs/
├── .vitepress/
│   └── config.js
├── public/
│   └── logo.svg
├── about.md
├── contact.md
├── guide.md
├── configs.md
└── get-started.md
</code></pre><p>Create these files inside your docs folder and add a simple markup inside them just to see how this works. This page is basic markdown so all your markdown syntax like links, code blocks, headings, and so on works here.</p>
<p>Just for testing purposes, copy this markdown content and paste it inside any of the <code>.md</code> files you just created:</p>
<pre><code class="lang-md"><span class="hljs-section"># About</span>

Welcome to the about page.

This markdown supports html elements like the <span class="hljs-code">`p`</span> tag coupled with inline styles

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: #ff7340; border: 1px solid rgba(255, 135, 23, 0.25); border-radius:5px; padding: 1rem;"</span>&gt;</span></span>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>

Even satire code snippets with syntax highlighting are also supported. 😅

<span class="hljs-code">```js
const lang = prompt("What is your favorite programming language?");

(lang === "JavaScript") | (lang === "javascript") | (lang === "js")
  ? alert("JavaScript to the world! 🚀🟡")
  : alert(`We don't permit such languages here 💩`);</span>
</code></pre>
<p>Of course, images are not left out.</p>
<p><img src="/logo.svg" alt="adocs logo" width="600" height="400" loading="lazy"></p>
<pre><code>
Here<span class="hljs-string">'s the output:

![Image](https://www.freecodecamp.org/news/content/images/2022/11/page-routing-2.gif)

Great! You'</span>ve set-up the docs, added a navigation menu <span class="hljs-keyword">with</span> a dropdown feature, added a sidebar, and customized the links to navigate to different pages. Next up, <span class="hljs-keyword">let</span><span class="hljs-string">'s work on the home page.

## How to Customize the Homepage

Just like every other component, VitePress provides us with markup for building the homepage. I'</span>ve broken it down into three parts: Hero, features, and footer section.

### The Hero Section

First, we<span class="hljs-string">'ll start with the hero section. Replace the Hello World text in the `index.md` page with the following markup:

```yml
# docs/index.md
---
layout: home

hero:
  name: Adocs
  text: Static docs template built with VitePress.
  image:
    src: /logo-big.svg
    alt: Adocs logo
  tagline: A free to use template for creating docs for your projects
  actions:
    - theme: brand
      text: Get Started
      link: /get-started
    - theme: alt
      text: View on GitHub
      link: https://github.com/evavic44/adocs-template
---</span>
</code></pre><h3 id="heading-the-features-section">The Features Section</h3>
<p>Additionally, you can add a features section after the hero section. Simply paste the code below under the hero objects:</p>
<pre><code class="lang-yml"><span class="hljs-comment"># /docs/index.md</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">link:</span> <span class="hljs-string">https://github.com/evavic44/adocs-template</span>

<span class="hljs-attr">features:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">icon:</span> <span class="hljs-string">⚡️</span>
    <span class="hljs-attr">title:</span> <span class="hljs-string">Adocs,</span> <span class="hljs-string">The</span> <span class="hljs-string">DX</span> <span class="hljs-string">that</span> <span class="hljs-string">can't</span> <span class="hljs-string">be</span> <span class="hljs-string">beat</span>
    <span class="hljs-attr">details:</span> <span class="hljs-string">Lorem</span> <span class="hljs-string">ipsum...</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">icon:</span> <span class="hljs-string">🎉</span>
    <span class="hljs-attr">title:</span> <span class="hljs-string">Power</span> <span class="hljs-string">of</span> <span class="hljs-string">Vue</span> <span class="hljs-string">meets</span> <span class="hljs-string">Markdown</span>
    <span class="hljs-attr">details:</span> <span class="hljs-string">Lorem</span> <span class="hljs-string">ipsum...</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">icon:</span> <span class="hljs-string">🔥</span>
    <span class="hljs-attr">title:</span> <span class="hljs-string">Simple</span> <span class="hljs-string">and</span> <span class="hljs-string">minimal,</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">details:</span> <span class="hljs-string">Lorem</span> <span class="hljs-string">ipsum...</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">icon:</span> <span class="hljs-string">🎀</span>
    <span class="hljs-attr">title:</span> <span class="hljs-string">Stylish</span> <span class="hljs-string">and</span> <span class="hljs-string">cool</span>
    <span class="hljs-attr">details:</span> <span class="hljs-string">Lorem</span> <span class="hljs-string">ipsum...</span>
<span class="hljs-meta">---</span>
</code></pre>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/hero-redesign.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-the-footer-section">The Footer Section</h3>
<p>You can add a footer message on the bottom of the page, but this will only show up in the home page.</p>
<p>According to the <a target="_blank" href="https://vitepress.vuejs.org/guide/theme-footer#footer"><em>VitePress</em> docs</a>:</p>
<blockquote>
<p>Note that the footer will not be displayed when the SideBar is visible.</p>
</blockquote>
<p>To add the footer component, go to the <code>config.js</code> file and paste the markup inside the <code>themeConfig</code> object:</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/config.js</span>
 <span class="hljs-attr">footer</span>: {
   <span class="hljs-attr">message</span>: <span class="hljs-string">"Released under the MIT License."</span>,
   <span class="hljs-attr">copyright</span>: <span class="hljs-string">"Copyright © 2022-present Adocs"</span>,
 },
</code></pre>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/footer-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Aside from the markup, you can also customize the components using custom CSS to change things like fonts family, colors, layout, ETC.</p>
<h2 id="heading-how-to-add-custom-css">How to Add Custom CSS</h2>
<p>The default theme <code>CSS</code> is customized by overriding root level CSS variables. If you want, you can check out the <a target="_blank" href="https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css">full list of CSS variables that are customizable</a>. </p>
<p>To do get started, create a <code>.vitepress/theme</code> directory, and inside this theme folder, add an <code>index.js</code> and <code>custom.css</code> file. If you've been following along, you can use the terminal command below to do this quickly:</p>
<pre><code class="lang-bash">mkdir docs/.vitepress/theme &amp;&amp; touch docs/.vitepress/theme/index.js &amp;&amp; touch docs/.vitepress/theme/custom.css
</code></pre>
<p>If you run into any issues with the terminal command, just create the files manually and move on to the next step.</p>
<p>Here's an overview of the folder structure:</p>
<pre><code class="lang-bash">docs/
├── .vitepress/
│   ├── config.js
│   └── theme/
│       ├── index.js
│       └── custom.css
├── public/
│   └── logo.svg
├── about.md
├── contact.md
├── guide.md
├── configs.md
└── get-started.md
</code></pre>
<p>After creating these files, inside the <code>.vitepress/theme/index.js</code> file, paste the import commands:</p>
<pre><code class="lang-js"><span class="hljs-comment">// .vitepress/theme/index.js</span>
<span class="hljs-keyword">import</span> DefaultTheme <span class="hljs-keyword">from</span> <span class="hljs-string">"vitepress/theme"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./custom.css"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> DefaultTheme;
</code></pre>
<h3 id="heading-color-theme">Color Theme</h3>
<p>The colors are controlled by the CSS variables. You can simply replace them with any colors you want.</p>
<p>Note that this color has a provision for both light and dark mode. So make sure you change them accordingly.</p>
<p>Here's an example of my custom colors:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* .vitepress/theme/custom.css */</span>

<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--vp-c-brand</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">255</span>, <span class="hljs-number">115</span>, <span class="hljs-number">64</span>);
  <span class="hljs-attribute">--vp-c-brand-light</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">255</span>, <span class="hljs-number">87</span>, <span class="hljs-number">25</span>);
  <span class="hljs-attribute">--vp-c-brand-lighter</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">255</span>, <span class="hljs-number">115</span>, <span class="hljs-number">64</span>);
  <span class="hljs-attribute">--vp-c-brand-dark</span>: <span class="hljs-number">#FF622D</span>;
  <span class="hljs-attribute">--vp-c-brand-darker</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">226</span>, <span class="hljs-number">60</span>, <span class="hljs-number">0</span>);

  <span class="hljs-attribute">--vp-c-sponsor</span>: <span class="hljs-number">#fd1d7c</span>;
}
</code></pre>
<p>If you don't see the effects immediately, try stopping the server and starting it again.</p>
<p>Aside from the color themes, you can also override other things like font family, typography, layout, breakpoints, and so on.</p>
<h3 id="heading-how-to-use-custom-fonts">How to Use Custom Fonts</h3>
<p>You can import <a target="_blank" href="https://fonts.google.com/">Google fonts</a> inside the CSS file to override the default font family.</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(<span class="hljs-attribute">https:</span>//fonts.googleapis.com/css?family=Space+<span class="hljs-attribute">Mono:</span>regular,italic,<span class="hljs-number">700</span>,<span class="hljs-number">700</span>italic);
<span class="hljs-keyword">@import</span> url(<span class="hljs-attribute">https:</span>//fonts.googleapis.com/css?family=Space+<span class="hljs-attribute">Grotesk:</span>regular,italic,<span class="hljs-number">700</span>,<span class="hljs-number">700</span>italic);

<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--vp-c-brand</span>: <span class="hljs-number">#ff7340</span>;
  <span class="hljs-attribute">--vp-c-brand-light</span>: <span class="hljs-number">#ff5719</span>;
  <span class="hljs-attribute">--vp-c-brand-lighter</span>: <span class="hljs-number">#ff7340</span>;
  <span class="hljs-attribute">--vp-c-brand-lighter</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">135</span>, <span class="hljs-number">23</span>, <span class="hljs-number">0.25</span>);
  <span class="hljs-attribute">--vp-c-brand-dark</span>: <span class="hljs-number">#ff622d</span>;
  <span class="hljs-attribute">--vp-c-brand-darker</span>: <span class="hljs-number">#e23c00</span>;

  <span class="hljs-attribute">--vp-c-sponsor</span>: <span class="hljs-number">#fd1d7c</span>;

  <span class="hljs-comment">/* Typography */</span>
  <span class="hljs-attribute">--vp-font-family-base</span>: <span class="hljs-string">"Space Grotesk"</span>, <span class="hljs-string">"Inter var experimental"</span>, <span class="hljs-string">"Inter var"</span>,
    -apple-system, BlinkMacSystemFont, <span class="hljs-string">"Segoe UI"</span>, Roboto, Oxygen, Ubuntu,
    Cantarell, <span class="hljs-string">"Fira Sans"</span>, <span class="hljs-string">"Droid Sans"</span>, <span class="hljs-string">"Helvetica Neue"</span>, sans-serif;

  <span class="hljs-comment">/* Code Snippet font */</span>
  <span class="hljs-attribute">--vp-font-family-mono</span>: <span class="hljs-string">"Space Mono"</span>, Menlo, Monaco, Consolas, <span class="hljs-string">"Courier New"</span>,
    monospace;
}
</code></pre>
<p>With the <code>--vp-font-family-base</code> variable you can change the main font and <code>--vp-font-family-mono</code>, the font for code snippets.</p>
<p>Here's the output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/final-works.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You've successfully customized the theme and changed the font family using CSS. Though there's more you can do in regards to styling, but at this point, I hope it's clear how you can customize your docs with CSS. </p>
<p>Let's discuss hosting in the next section.</p>
<h2 id="heading-how-to-host-your-doc-site">How to Host Your Doc Site</h2>
<p>You can publish or host your docs site when you're done to different platforms like <a target="_blank" href="https://netlify.com">Netlify</a>, <a target="_blank" href="https://vercel.com">Vercel</a>, <a target="_blank" href="https://aws.com">AWS Amplify</a>, and so on.</p>
<p>First, run the build command:</p>
<pre><code class="lang-bash">npm run docs:build
</code></pre>
<p>This should create a new <code>dist</code> folder that contains all the static files of your docs. </p>
<p>In deciding on what hosting service to use, you can pick any of the options I mentioned earlier but we'll be using Vercel in this guide. Also, feel free to look at other alternatives of your choice. </p>
<p>If you don't have a Vercel account, follow this guide to <a target="_blank" href="https://vercel.com/docs/concepts/get-started/deploy">create one and configure your Git provider</a> before you move on to the next step.</p>
<p>Assuming you've successfully set-up your account and uploaded your docs site to Vercel, navigate to the <strong>project &gt;</strong> <strong>settings</strong> <strong>&gt; build and deploy settings</strong>, and paste the following commands to their respective fields:</p>
<ul>
<li>Build command: <code>npm run docs:build</code> </li>
<li>Output directory:  <code>docs/.vitepress/dist</code></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/deploy-settings-vercel.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After editing the settings, save them and deploy your site!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you've set-up a full-fledged documentation site and customized it using CSS and VitePress built-in components. </p>
<p>Just keep in mind that this tutorial only covers a fragment of what is possible with VitePress. To learn more, check out the <a target="_blank" href="https://vitepress.vuejs.org">VitePress docs</a>.</p>
<h3 id="heading-additional-reading">Additional Reading</h3>
<p>Here are a few things not covered in this article that I think is also worth looking into:</p>
<ul>
<li><a target="_blank" href="https://vitepress.vuejs.org/guide/markdown#custom-containers">Custom Containers</a></li>
<li><a target="_blank" href="https://vitepress.vuejs.org/guide/using-vue#using-vue-in-markdown">Using Vue in markdown</a></li>
<li><a target="_blank" href="https://vitepress.vuejs.org/guide/theme-team-page">Team Section</a></li>
<li><a target="_blank" href="https://vitepress.vuejs.org/guide/theme-carbon-ads">Carbon Ads</a></li>
</ul>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><a target="_blank" href="https://adocs.vercel.app">Live Demo</a></li>
<li><a target="_blank" href="https://github.com/Evavic44/adocs">GitHub Repo</a></li>
</ul>
<p>If you are an open source fan like myself or you enjoy hearing about such cool projects, do follow me on my socials so you don't miss my next post. Cheers. 🍷</p>
<p><a target="_blank" href="https://github.com/evavic44">GitHub</a> | <a target="_blank" href="https://twitter.com/victorekea">Twitter</a> | <a target="_blank" href="https://eke.hashnode.dev">Blog</a> | <a target="_blank" href="https://victoreke.com">Portfolio</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Do Test-Driven Development with Svelte and Vitest – A Project-Based Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ By Sriram Thiagarajan Test Driven Development (TDD) is one of the best ways to make sure your code is high quality and works like it's supposed to work. It can also help you create reliable builds during continuous deployments.  In this post, we will... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-do-test-driven-development-with-svelte-and-vitest/</link>
                <guid isPermaLink="false">66d461492472e5ed2fa07bbd</guid>
                
                    <category>
                        <![CDATA[ Svelte ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TDD (Test-driven development) ]]>
                    </category>
                
                    <category>
                        <![CDATA[ test driven development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 19 Sep 2022 17:20:38 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/SvelteVitestTDDPoster.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sriram Thiagarajan</p>
<p>Test Driven Development (TDD) is one of the best ways to make sure your code is high quality and works like it's supposed to work. It can also help you create reliable builds during continuous deployments. </p>
<p>In this post, we will learn how to create a Svelte application using TDD methods.</p>
<h2 id="heading-what-were-building">What We're Building</h2>
<p>We are going to build a vertical tabs component where we can switch between three tabs. We are going to build this component by writing test cases first and developing the component functionality after that to make the tests more effective.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/FinalComponent-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Final implementation</em></p>
<h2 id="heading-prerequisite">Prerequisite</h2>
<p>I will explain all the steps needed to create an app and you can follow along with the code. It's great if you have basic programming knowledge and fundamental knowledge of HTML and CSS for this tutorial.</p>
<p>Also, you'll need to have Node.js installed. You can <a target="_blank" href="https://nodejs.org/en/">see how to do that here</a> if you don't have it already.</p>
<h2 id="heading-what-is-test-driven-development">What is Test-Driven Development?</h2>
<p>The basic idea of test-driven development, or TDD, is to write the test before you implement the actual functionality. This helps you clearly figure out what you're developing and how it works. </p>
<p>You first watch the test fail, and then you write the code to make it pass. This ensures that there are no false positive tests in your code.</p>
<p>TDD is a methodology you can apply to any programming language. It is more prevalent when developing backend applications that contain business logic that you can easily test. </p>
<p>The good news is, you can apply similar techniques to test your front-end applications as well.</p>
<h2 id="heading-three-stages-of-tdd">Three stages of TDD</h2>
<p>The three stages of TDD are:</p>
<ol>
<li>Red stage – Write the test and watch it fail</li>
<li>Green stage – Write the minimum code required to make the test pass</li>
<li>Refactor – Cleanup and refactor the code to make it more robust</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/TDDStages.png" alt="Image" width="600" height="400" loading="lazy">
<em>Three stages of TDD</em></p>
<h2 id="heading-what-is-vitest">What is Vitest?</h2>
<p>Vitest is an up-and-coming testing framework which has similar functionality to Jest. </p>
<p>Since we are using Vite as our build tool for Svelte in this tutorial, Vitest has very good integration with Vite and offers a similar testing environment without needing extra configuration.</p>
<h2 id="heading-how-to-create-a-svelte-application">How to Create a Svelte Application</h2>
<p>We are going to create a Svelte application using Vite as the build tool. You can do that with this command:</p>
<pre><code>npm create vite@latest
</code></pre><p>This will create a new project, and you can follow the steps below to create and setup the project:</p>
<ol>
<li>Enter the name of the project. This will also create a new folder with the project name. In this example, I will add the project name as <code>svelte-tdd-vitest</code>.</li>
<li>You can select the framework in the next step. Let's choose <code>svelte</code> as the framework.</li>
<li>Then you can enter the variant of the framework. We can choose the <code>TypeScript</code> variant for this article. If you are not comfortable with TypeScript, you can also choose <code>JavaScript</code> in this option.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ProjectInit.png" alt="Image" width="600" height="400" loading="lazy">
<em>Initialise Svelte project with Vite</em></p>
<p>You can then follow the helpful steps provided in the terminal to install the dependencies and start the application. Run the following command:</p>
<pre><code>cd svelte-tdd-vitest
npm install
npm run dev
</code></pre><p><code>npm install</code> will install the dependencies of the project. </p>
<p><code>npm run dev</code> will start the development server. You should see the app running in the port specified in the terminal.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ViteServerStartCmd.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vite Server started using npm run dev</em></p>
<p>Congrats, you can now see the starter app running in your browser. You can open the project in your favourite Code Editor and start working.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/SvelteViteStarter.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-set-up-vitest">How to Set Up Vitest</h2>
<p>You can add Vitest to the project right now as a dev dependency. This means that Vitest will not be packaged into the production build of the application since you will be running the test in your local environment.</p>
<pre><code>npm install -D vitest
</code></pre><p>Vitest can read the config of Vite inside the <code>vite.config.js</code> file and prepare the test environment similar to the build environment. This makes the test more reliable. So you can reuse the Vite config file and add more options for the configuration of Vitest.</p>
<p>You can also override the config for the Vitest by creating a new file called <code>vitest.config.js</code> in the project root. So now, create a new file called <code>vitest.config.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest/config'</span>
<span class="hljs-keyword">import</span> { svelte } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/vite-plugin-svelte'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">plugins</span>: [
    svelte({ <span class="hljs-attr">hot</span>: !process.env.VITEST }),
  ],
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">environment</span>: <span class="hljs-string">'jsdom'</span>,
  }
})
</code></pre>
<p>There are a couple of configs we are adding to the file</p>
<ol>
<li>We are disabling Svelte's hot module reload when tests are running. </li>
<li>We are defining the environment for running the test as <code>jsdom</code>. It helps in mocking the DOM API and running the tests in a reliable method.</li>
</ol>
<p>In order to use the <code>jsdom</code> you need to add it as an Dev Dependency as well. So let's install that package using the terminal.</p>
<pre><code>npm install -D jsdom
</code></pre><p>After installation, let's add a couple of scripts to the <code>package.json</code> file to start the Vitest test from the <code>npm</code> command line:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    ...
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"vitest"</span>,
        <span class="hljs-attr">"coverage"</span>: <span class="hljs-string">"vitest run --coverage"</span>
}
</code></pre>
<h2 id="heading-how-to-create-the-first-test">How to Create the First Test</h2>
<p>You now have all the setup needed to write your first test. Create a new file called <code>sample.spec.ts</code> inside the <code>lib</code> directory.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {describe, test, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;

describe(<span class="hljs-string">"Example File"</span>, <span class="hljs-function">() =&gt;</span> {
    test(<span class="hljs-string">"Sample test"</span>, <span class="hljs-function">() =&gt;</span> {
        expect(<span class="hljs-number">1</span> + <span class="hljs-number">3</span>).equal(<span class="hljs-number">4</span>);
    });
});
</code></pre>
<p>Let's break down the different functions used to create this test file:</p>
<ol>
<li><code>describe</code> – you use this to group similar test together and benchmark tests when generating your reports. It takes a name and a function that contains the group of tests.</li>
<li><code>test</code> – Represents a single test. It can contain multiple expectations within it. It is created by passing a name of the test and the function to run the test.</li>
<li><code>expect</code> – Represents the expression which you're testing.</li>
</ol>
<p>There are multiple different ways to write your test based on what you are testing. You can have a look at the complete API for Vitest in the <a target="_blank" href="https://vitest.dev/api/">official API Reference</a>.</p>
<p>Let's run the test using the following npm command:</p>
<pre><code>npm run test
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2022/09/NpmRunFirstTest.png" alt="Image" width="600" height="400" loading="lazy">
<em>Running the test</em></p>
<h2 id="heading-how-to-add-the-global-test-config">How to Add the Global Test Config</h2>
<p>You are going to be using the <code>describe</code>, <code>test</code>, and <code>expect</code> functions a lot in the test files and it might be verbose to import them in all the test files. So Vitest has a nice config where you can set these global imports and so you don't have to add them to each file. </p>
<p>So let's update the <code>vitest.config.js</code> file with this configuration:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  ...
  test: {
    ...
    globals: <span class="hljs-literal">true</span>
  }
})
</code></pre>
<p>After adding this <code>globals</code> equals true line in your config, you can now remove the imports in your spec file. </p>
<p>If you are using TypeScript, your TypeScript compiler will complain in your spec file. You can solve that by adding the following line to your <code>tsconfig.json</code> file:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"@tsconfig/svelte/tsconfig.json"</span>,
  <span class="hljs-attr">"compilerOptions"</span>: {
    ...
    <span class="hljs-attr">"types"</span>: [<span class="hljs-string">"vitest/globals"</span>]
  },
}
</code></pre>
<p>Now your test can look like this. This is not a huge upgrade for this small file, but when you have lots of spec files, this config change is useful.</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">"Example File"</span>, <span class="hljs-function">() =&gt;</span> {
    it(<span class="hljs-string">"Sample test"</span>, <span class="hljs-function">() =&gt;</span> {
        expect(<span class="hljs-number">1</span> + <span class="hljs-number">3</span>).equal(<span class="hljs-number">4</span>);
    });
});
</code></pre>
<h2 id="heading-how-to-create-a-svelte-component">How to Create a Svelte Component</h2>
<p>We will create a new svelte component called <code>VerticalTabs.svelte</code>. The requirements are to create a vertical tab component that can contain a few items and for the user to be able to select a particular tab to view its content in the right side.</p>
<p>The component will be divided into two parts. The left side displays all the tabs. The right side displays the content based on the tab.</p>
<p>Let's create the basic HTML and CSS styles needed for the component. We will add the functionality to switch tabs after writing the tests.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>First Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Second Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Third Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-content"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>First Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. Autem aut deserunt veniam tempora deleniti quos reprehenderit natus. Animi, obcaecati dolorum, culpa, maiores maxime ullam soluta unde rerum nihil temporibus quibusdam!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
    <span class="hljs-selector-class">.vertical-tab-container</span> {
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-direction</span>: row;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid gray;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">1rem</span>;
    }

    <span class="hljs-selector-class">.vertical-tab</span> {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">3rem</span>;
        <span class="hljs-attribute">list-style</span>: none;
        <span class="hljs-attribute">border-right</span>: <span class="hljs-number">1px</span> solid gray
    }

    <span class="hljs-selector-class">.vertical-tab</span> <span class="hljs-selector-tag">li</span> {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    }

    <span class="hljs-selector-class">.vertical-tab-content</span> {
        <span class="hljs-attribute">flex</span>:<span class="hljs-number">1</span>
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>In this code, you are adding simple elements like an unordered list to display the list of tabs. You are styling it in the <code>&lt;style&gt;</code> tag which will be locally scoped CSS for this component.</p>
<p>Then, you are adding <code>&lt;h2&gt;</code> tag to display the tab heading and a paragraph tag <code>&lt;p&gt;</code> to show more dummy text for the tab. You're using <code>flex</code> to show the two items side by side and the property of <code>flex: 1</code> on the content will make that container take all the remaining available space to expand.</p>
<p>The component should look like the below image:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/VerticalTabInitial-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vertical Tab initial</em></p>
<h2 id="heading-how-to-mount-the-svelte-component-in-test">How to Mount the Svelte Component in Test</h2>
<p>Now you need to create the first test for the component by mounting the svelte component and then checking to see if you can find the "First Tab" text in the component. </p>
<p>So create a new spec file called <code>VerticalTabs.spec.ts</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> VerticalTabs <span class="hljs-keyword">from</span> <span class="hljs-string">"./VerticalTabs.svelte"</span>;

describe(<span class="hljs-string">"VerticalTabs Component"</span>, <span class="hljs-function">() =&gt;</span> {

    test(<span class="hljs-string">"should render the component"</span>, <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Create a new container for the test</span>
        <span class="hljs-keyword">const</span> host = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>);

        <span class="hljs-comment">// Append the new container in the HTML body</span>
        <span class="hljs-built_in">document</span>.body.appendChild(host);

        <span class="hljs-comment">// Create an instance of the vertical tab</span>
        <span class="hljs-keyword">const</span> instance = <span class="hljs-keyword">new</span> VerticalTabs({ <span class="hljs-attr">target</span>: host });

        <span class="hljs-comment">// Check if the instance has value</span>
        expect(instance).toBeTruthy()

        <span class="hljs-comment">// Test if we can find the "First Tab Heading"</span>
        expect(host.innerHTML).toContain(<span class="hljs-string">"First Tab Heading"</span>)

    });

})
</code></pre>
<p>In order to mount the Svelte component, you need to first create a <code>div</code> container and attach that <code>div</code> to the <code>body</code> of the HTML document. Then you need to attach your component to the <code>div</code>. You can then test the <code>innerHTML</code> of the main container to see if you have the required content.</p>
<p>Now this test should pass since you have the <code>First Tab Heading</code> content displayed in the component.</p>
<p>Going through this long process in all the steps might prove to be difficult. So let's add another package to make the job easier. The package is <code>testing-library/svelte</code> and it provides more features to make the assertions easy and less verbose.</p>
<h2 id="heading-how-to-use-the-svelte-testing-library">How to Use the Svelte Testing Library</h2>
<p>First, you'll need to install the library:</p>
<pre><code>npm install -D @testing-library/svelte
</code></pre><p>Let's update the previous test to make it less verbose and let <code>testing-library</code> handle all the heavy lifting for us. You can use the <code>render</code> function to add the component to the testing page.</p>
<pre><code><span class="hljs-keyword">import</span> VerticalTabs <span class="hljs-keyword">from</span> <span class="hljs-string">"./VerticalTabs.svelte"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/svelte'</span>;

describe(<span class="hljs-string">"VerticalTabs Component"</span>, <span class="hljs-function">() =&gt;</span> {

    test(<span class="hljs-string">"should render the component"</span>, <span class="hljs-function">() =&gt;</span> {

        render(VerticalTabs);

        <span class="hljs-keyword">const</span> firstTabNode = screen.getByText(<span class="hljs-regexp">/First Tab Heading/i</span>)

        expect(firstTabNode).toBeTruthy()
    });

})
</code></pre><p>After the <code>render</code> function, add the component to the testing page. You can use the <code>screen</code> object imported from the library to query the nodes that are rendered.</p>
<p>There are multiple methods inside this object to make testing easy, and you will use one of the methods to get the text in the component.</p>
<p><code>getByText</code> will return the instance of a given text. You are expecting the node to contain some value.</p>
<p>There are three main ways to retrieve the element in the testing library, and each serves a different purpose:</p>
<ol>
<li>getByText – This will throw an error when the text is not found and the test will fail</li>
<li>queryByText – This will return null when the text is not found</li>
<li>findByText – This will also throw an error when the text is not found and you can use it when doing async tests where the element will take some time to appear/disappear</li>
</ol>
<p>You can find a useful summary of these helper functions in the <a target="_blank" href="https://testing-library.com/docs/queries/about">official docs page</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/QueriesSummary.png" alt="Image" width="600" height="400" loading="lazy">
<em>Summary screenshot from Official docs of testing library</em></p>
<p>You can find more details on this API in this <a target="_blank" href="https://testing-library.com/docs/svelte-testing-library/api">official page</a>.</p>
<h2 id="heading-how-to-build-the-tab-switching-feature">How to Build the Tab Switching Feature</h2>
<p>We will start adding the feature to switch tabs in the component and test the component by writing the test first.</p>
<h3 id="heading-red-stage">Red stage</h3>
<p>Let's write a test the switch to a different tab on click of the "Second Tab" list item. </p>
<p>Since we don't have this functionality implemented, we will fail this test first and that's okay. Once we fail the test, we should write the logic to make it pass in the next step. </p>
<p>So let's write a failing test:</p>
<pre><code>test(<span class="hljs-string">"should switch tabs"</span>, <span class="hljs-keyword">async</span> () =&gt; {
        render(VerticalTabs);

        <span class="hljs-keyword">const</span> secondTabElement = screen.getByText(<span class="hljs-regexp">/Second Tab/i</span>);

        fireEvent.click(secondTabElement)

        <span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Second Tab Heading/i</span>);
})
</code></pre><p>We are using the <code>fireEvent</code> from the testing library to simulate the click of the element. We can make the test <code>async</code> and <code>await</code> for the element since the text will change after the element is clicked.</p>
<p>You should have a failing test now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/TestFailing.png" alt="Image" width="600" height="400" loading="lazy">
<em>Test failing unable to find the content</em></p>
<h3 id="heading-green-stage">Green stage</h3>
<p>Let's add the logic to change the tab in the Svelte component. We can do that easily by creating a <code>selectedIndex</code> variable and changing its value based on the selected tab.</p>
<pre><code>&lt;script lang=<span class="hljs-string">"ts"</span>&gt;
    <span class="hljs-keyword">let</span> selectedIndex = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">const</span> changeSecondTab = <span class="hljs-function">() =&gt;</span> {
        selectedIndex = <span class="hljs-number">1</span>;
    }
&lt;/script&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>First Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> changeSecondTab()}&gt;Second Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Third Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-content"</span>&gt;</span>
        {#if selectedIndex == 0}
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>First Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        {:else if selectedIndex == 1}
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Second Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        {/if}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre><p><strong>Note: This is not the best implementation.</strong> It is only meant for showing that you can do minimal work to make the test pass. We will clean it up in the next stage</p>
<p>We have a method <code>changeSecondTab</code> that will change the <code>selectedIndex</code> value to 1 which will make the <code>#if</code> condition to change the tab. Even though it is not the best solution to handle all cases, we have a starting point.</p>
<p>Let's look at the test now. It should be working:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/TestPassed.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-refactor">Refactor</h3>
<p>Let's fix the implementation to make it more generic and have it work for all three tabs. We can also add an indicator to show which tab is currently selected.</p>
<pre><code class="lang-javascript">&lt;script lang=<span class="hljs-string">"ts"</span>&gt;
    <span class="hljs-keyword">let</span> selectedIndex = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">const</span> changeTab = <span class="hljs-function">(<span class="hljs-params">index: number</span>) =&gt;</span> {
        selectedIndex = index;
    }
&lt;/script&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class:selected</span>=<span class="hljs-string">{selectedIndex</span> == <span class="hljs-string">0}</span> 
            <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> changeTab(0)}&gt;First Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class:selected</span>=<span class="hljs-string">{selectedIndex</span> == <span class="hljs-string">1}</span> 
            <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> changeTab(1)}&gt;Second Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class:selected</span>=<span class="hljs-string">{selectedIndex</span> == <span class="hljs-string">2}</span> 
            <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> changeTab(2)}&gt;Third Tab<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-content"</span>&gt;</span>
        {#if selectedIndex == 0}
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>First Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        {:else if selectedIndex == 1}
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Second Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        {:else}
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Third Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        {/if}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
    ...
    <span class="hljs-selector-class">.selected</span> {
        <span class="hljs-attribute">color</span>: blue;
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>We have created a method <code>changeTab</code> that will be called on the click of each element and then change the <code>selectedIndex</code>. This will cause the <code>#if</code> logic to change the tab based on its value.</p>
<p>We also have <code>class:selected</code> followed by an expression and when the expression becomes true, the <code>selected</code> class is added to the element. So we have added one more CSS class and we made the text colour blue to show the selected tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/FinalComponent.png" alt="Image" width="600" height="400" loading="lazy">
<em>Finished vertical tabs component</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-375.png" alt="Image" width="600" height="400" loading="lazy">
<em>Test after finishing refactor</em></p>
<p>We have now confirmed that the test is also passing after the refactor. You can continue this process to add more tests and features to your component.</p>
<h2 id="heading-how-to-add-animation">How to Add Animation</h2>
<p>Svelte makes it easy to add animation when content is changed. You can make use of the <code>transition</code> directive to add pre-built animation to your application.</p>
<p>So let's add a fly animation when the content changes. You can import the fly animation from <code>svelte/transition</code> and then add it to the element using <code>transition:fly</code>. This will add the default fly animation when the content flies out and new content flies in. Such a neat effect with just a single line of code!</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { fly } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte/transition'</span>;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"vertical-tab-content"</span> &gt;</span>
        {#if selectedIndex == 0}
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">transition:fly</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>First Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {:else if selectedIndex == 1}
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">transition:fly</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Second Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {:else}
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">transition:fly</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Third Tab Heading<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {/if}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Animation brings life to your application and helps it stand out. I am a big fan of the simple animation transition system in Svelte.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you have learned how to create a new component using test-driven development methodology. Please share your feedback on this post and let me know your thoughts.</p>
<p>Thanks for reading! You can contact me on Twitter <a target="_blank" href="https://twitter.com/sriram_thiagar">@sriram_thiagar</a>. I regularly post articles on my blog <a target="_blank" href="https://www.eternaldev.com/">eternaldev.com</a> if you want to read more articles from me.</p>
<p>Hello everyone. I am Sriram, and I work as a Full Stack developer. I like to share my learning with others. I have been <a target="_blank" href="https://www.eternaldev.com/">blogging</a> for more than a year now on my website.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Import SVGs in a React and Vite app ]]>
                </title>
                <description>
                    <![CDATA[ Are you having difficulties importing SVGs into your React app? This is a problem that many developers face, especially when setting up a new React app with a module bundler. In this article, I will share with you the different ways of importing SVGs... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-import-svgs-in-react-and-vite/</link>
                <guid isPermaLink="false">66d45f35052ad259f07e4ae6</guid>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SVG ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Israel Oyetunji ]]>
                </dc:creator>
                <pubDate>Fri, 01 Jul 2022 22:15:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/Blog-article-cover-images--3-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you having difficulties importing SVGs into your React app? This is a problem that many developers face, especially when setting up a new React app with a module bundler.</p>
<p>In this article, I will share with you the different ways of importing SVGs in React, as well as how the process works under the hood.</p>
<p>Let's get started.</p>
<h2 id="heading-what-is-an-svg">What Is an SVG?</h2>
<p>SVG, short for Scalable Vector Graphic, is an image format used for rendering two-dimensional (2D) graphics on the internet.</p>
<p>The SVG format stores images as <strong>vectors</strong> which are graphics made up of points, lines, and curves based on geometry and mathematical formulas.</p>
<p>Because they are based on numbers and values rather than a grid of pixels like <a target="_blank" href="https://en.wikipedia.org/wiki/Raster_graphics">raster images</a>(.png and.jpg), they do not lose quality when zoomed or resized.</p>
<p>They're also great for creating responsive websites that need to look good and function well across a variety of screen sizes.</p>
<p>Overall, SVGs are great as they are scalable, lightweight, customizable, and can be animated using CSS when used <a class="post-section-overview" href="#2usingsvgsbyaddingdirectlyasjsx">inline</a>.</p>
<h2 id="heading-how-to-import-svgs-in-react-apps">How to Import SVGs in React Apps</h2>
<p>Let's go through some of the most used methods when importing SVGs into React Apps.</p>
<h3 id="heading-1-how-to-import-svgs-using-the-image-tag">1. How to Import SVGs Using the Image Tag</h3>
<p>Importing SVGs using the image tag is one of the easiest ways to use an SVG. If you initialize your app using CRA (Create React App), you can import the SVG file in the image source attribute, as it supports it off the bat.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> YourSvg <span class="hljs-keyword">from</span> <span class="hljs-string">"/path/to/image.svg"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{YourSvg}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Your SVG"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>But if you are not using CRA, you have to set up a file loader system in the bundler you're using (Webpack, Parcel, Rollup, and so on).</p>
<p>Webpack, for instance, has a loader for handling SVGs called <a target="_blank" href="https://v4.webpack.js.org/loaders/file-loader/">file-loader</a>.</p>
<p>To install the file-loader, add the following command:</p>
<pre><code class="lang-bash">npm install file-loader --save-dev
</code></pre>
<p>Next, add the loader to the <code>webpack.config.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">module</span>: {
    <span class="hljs-attr">rules</span>: [
      {
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.(png|jpe?g|gif)$/i</span>,
        use: [
          {
            <span class="hljs-attr">loader</span>: <span class="hljs-string">"file-loader"</span>,
          },
        ],
      },
    ],
  },
};
</code></pre>
<p>Now, you can import your SVG files and use them:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> YourSvg <span class="hljs-keyword">from</span> <span class="hljs-string">"/path/to/image.svg"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{YourSvg}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Your SVG"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>NOTE: While this approach is straightforward, it does have one disadvantage: unlike the other methods for importing, you cannot style the SVG imported in a <code>img</code> element. As a result, it will be suitable for an SVG that does not need customization, like logos.</p>
<h3 id="heading-2-how-to-import-svgs-by-adding-them-directly-as-jsx">2. How to Import SVGs by Adding them Directly as JSX</h3>
<p>JSX supports the <code>svg</code> tag, so we can copy-paste the SVG directly into our React components. This method is straightforward as it helps you take full advantage of SVGs without using a bundler.</p>
<p>The approach is possible because SVGs are in XML format, just like HTML. So, we can convert it to JSX syntax. You can also use a <a target="_blank" href="https://transform.tools/html-to-jsx">compiler</a> instead of manually converting.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
        <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"ionicon"</span>
        <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 512 512"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
          <span class="hljs-attr">d</span>=<span class="hljs-string">"M160 136c0-30.62 4.51-61.61 16-88C99.57 81.27 48 159.32 48 248c0 119.29 96.71 216 216 216 88.68 0 166.73-51.57 200-128-26.39 11.49-57.38 16-88 16-119.29 0-216-96.71-216-216z"</span>
          <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
          <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
          <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
          <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
          <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">{32}</span>
        /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>The advantage of including SVGs inline is that we have access to their different properties, which allows us to style and customize them as we see fit.</p>
<p>One thing to keep in mind is that if your SVG file size is large, your code may become complex, reducing readability and productivity. If this is the case, use a png or jpeg file..</p>
<h3 id="heading-3-how-to-import-svgs-as-react-components">3. How to Import SVGs as React Components</h3>
<p>If you use CRA, there's a chance you have imported and used SVGs directly as a React component at one point in time.</p>
<p>This method, which is possible with the help of a file loader, works by loading the image alongside the HTML rather than as a separate file.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { ReactComponent <span class="hljs-keyword">as</span> Logo } <span class="hljs-keyword">from</span> <span class="hljs-string">"./logo.svg"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Logo</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-4-how-to-convert-svgs-to-react-components">4. How to Convert SVGs to React Components</h3>
<p>This approach is similar to the one previously mentioned. Here, we can convert an SVG to a React component by returning it from inside a class or functional component.</p>
<p>To do this, open up the SVG file in a text editor, and copy-paste the code into a new component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ArrowUndo = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
      <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"ionicon"</span>
      <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 512 512"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M245.09 327.74v-37.32c57.07 0 84.51 13.47 108.58 38.68 5.4 5.65 15 1.32 14.29-6.43-5.45-61.45-34.14-117.09-122.87-117.09v-37.32a8.32 8.32 0 00-14.05-6L146.58 242a8.2 8.2 0 000 11.94L231 333.71a8.32 8.32 0 0014.09-5.97z"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
        <span class="hljs-attr">d</span>=<span class="hljs-string">"M256 64C150 64 64 150 64 256s86 192 192 192 192-86 192-192S362 64 256 64z"</span>
        <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
        <span class="hljs-attr">strokeMiterlimit</span>=<span class="hljs-string">{10}</span>
        <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">{32}</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span></span>
  );
};
</code></pre>
<p>Now, you can import and render the SVG component in another component like this:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { ArrowUndo } <span class="hljs-keyword">from</span> <span class="hljs-string">"./path/to/ArrowUndo.jsx"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Button = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ArrowUndo</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
};
</code></pre>
<p>Again, this approach is only possible if your React app has a loader like SVGR's <a target="_blank" href="https://www.npmjs.com/package/@svgr/webpack">Webpack loader</a> included.</p>
<h3 id="heading-5-how-to-import-svgs-using-svgr">5. How to Import SVGs Using SVGR</h3>
<p><a target="_blank" href="https://react-svgr.com/">SVGR</a> is a tool that takes raw SVG files and transforms them into React components. It also has a large ecosystem with support for Create React App, Gatsby, Parcel, Rollup, and other technologies.</p>
<p>So, how do we set it up?</p>
<p>First, install the package by running the code below:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># with npm</span>
npm install --save-dev @svgr/webpack

<span class="hljs-comment"># with yarn</span>
yarn add --dev @svgr/webpack
</code></pre>
<p>Next, update your <code>webpack.config.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">module</span>: {
    <span class="hljs-attr">rules</span>: [
      {
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.svg$/i</span>,
        issuer: <span class="hljs-regexp">/\.[jt]sx?$/</span>,
        use: [<span class="hljs-string">"@svgr/webpack"</span>],
      },
    ],
  },
};
</code></pre>
<p>Now, you can import an SVG file as a React component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Logo <span class="hljs-keyword">from</span> <span class="hljs-string">"./logo.svg"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Logo</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-6-how-to-import-svgs-using-the-vite-plugin-for-svgr">6. How to Import SVGs Using the Vite Plugin for SVGR</h3>
<p><a target="_blank" href="https://www.npmjs.com/package/vite-plugin-svgr"><code>vite-plugin-svgr</code></a> is a plugin for Vite that uses svgr under the hood to transform SVGs into React components.</p>
<p>You can install it by running the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># with npm</span>
npm i vite-plugin-svgr

<span class="hljs-comment"># with yarn</span>
yarn add vite-plugin-svgr
</code></pre>
<p>Next, add the plugin inside your app's <code>vite.config.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react"</span>;
<span class="hljs-keyword">import</span> svgr <span class="hljs-keyword">from</span> <span class="hljs-string">"vite-plugin-svgr"</span>;

<span class="hljs-comment">// https://vitejs.dev/config/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">plugins</span>: [svgr(), react()],
});
</code></pre>
<p>Now, you can import the SVG files as <a class="post-section-overview" href="#3importingsvgsasreactcomponents">React components</a>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { ReactComponent <span class="hljs-keyword">as</span> Logo } <span class="hljs-keyword">from</span> <span class="hljs-string">"./logo.svg"</span>;
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>And it's a wrap! In this article, we've covered how to import SVGs in a React App using custom configuration from specific packages, how importing React components works, and how to use them in a Vite setup.</p>
<p>When working with Vite, I use the vite svgr plugin, which works flawlessly. You can also experiment with the other ways discussed in this article.</p>
<p>I hope you found this article insightful. If you do have any questions, feel free to send a message on <a target="_blank" href="https://twitter.com/israelmitolu">Twitter</a> or <a target="_blank" href="https://www.linkedin.com/in/israeloyetunji/">LinkedIn</a>.</p>
<p>Thanks for reading, and happy coding!</p>
<p>Before you go, check out these resources:</p>
<ul>
<li><p><a target="_blank" href="https://israelmitolu.hashnode.dev/why-you-should-ditch-create-react-app-for-vite">Why You Should Ditch Create-React-App for Vite</a></p>
</li>
<li><p><a target="_blank" href="https://rossbulat.medium.com/working-with-svgs-in-react-d09d1602a219">Working with SVGs in React</a></p>
</li>
<li><p><a target="_blank" href="https://twitter.com/i/communities/1532313139810906114">Twitter Community for Devs</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
