<?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[ Olabisi Olaoye - 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[ Olabisi Olaoye - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:29:49 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/Olabisi09/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Enforce Type Safety in FormData with TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ When working with the FormData interface in JavaScript, where data is appended as key/value pairs, there's no built-in way to enforce type safety on the keys you append. This can lead to typos, missing keys, and unexpected runtime errors. But in Type... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-enforce-type-safety-in-formdata-with-typescript/</link>
                <guid isPermaLink="false">67cef1f13c2aba931eee9785</guid>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ formdata ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Olabisi Olaoye ]]>
                </dc:creator>
                <pubDate>Mon, 10 Mar 2025 14:06:41 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741615550682/6e709ad7-f8bb-4d26-acad-02f168d83acc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When working with the FormData interface in JavaScript, where data is appended as key/value pairs, there's no built-in way to enforce type safety on the keys you append. This can lead to typos, missing keys, and unexpected runtime errors. But in TypeScript, we can solve this by enforcing strict key validation.</p>
<p>I needed this solution myself when sending my form values to an API. I later realized that I had made several typographical errors in more than one key/value pair I was trying to append to my payload. Because FormData accepts any string as a key, I was able to pass in the wrong strings and proceed with the API request.</p>
<p>After this happened, I looked for a way to ensure that TypeScript doesn’t allow those errors.</p>
<p>This article will show you how to make <code>FormData</code> keys <strong>type-safe</strong> using TypeScript.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To get the most out of this article, you should have a basic understanding of the following:</p>
<ol>
<li><p>JavaScript programming</p>
</li>
<li><p>TypeScript fundamentals, especially how interfaces, types, and the <code>keyof</code> operator work</p>
</li>
<li><p>the FormData interface</p>
</li>
</ol>
<p>If you’re new to TypeScript or FormData, I recommend checking out <a target="_blank" href="https://www.typescriptlang.org/docs/">TypeScript’s official documentation</a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/FormData">MDN’s guide on FormData</a> before proceeding.</p>
<h2 id="heading-step-1-define-your-allowed-keys">Step 1: Define Your Allowed Keys</h2>
<h3 id="heading-the-old-way">The Old Way</h3>
<p>The default way of appending data with FormData is to do it manually, with plain strings:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> payload = <span class="hljs-keyword">new</span> FormData();

payload.append(<span class="hljs-string">"id"</span>, <span class="hljs-string">"1122"</span>);
payload.append(<span class="hljs-string">"name"</span>, <span class="hljs-string">"Clark Kent"</span>);

payload.append(<span class="hljs-string">"agge"</span>, <span class="hljs-string">"36"</span>); <span class="hljs-comment">// Typo in key is allowed</span>
</code></pre>
<p>In the code snippet above, you can see that there was a typo when defining a key for <code>age</code>. But TypeScript won’t flag it as an error, and this could lead to errors when this data is sent with an API request.</p>
<h3 id="heading-the-better-way">The Better Way</h3>
<p>Instead of manually typing out the keys, define them in an object schema with a TypeScript interface.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> MyAllowedData {
    id: <span class="hljs-built_in">number</span>;
    name: <span class="hljs-built_in">string</span>;
    age: <span class="hljs-built_in">number</span>;
}
</code></pre>
<p>Alternatively, you can define them with types:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> MyAllowedData = {
    id: <span class="hljs-built_in">number</span>;
    name: <span class="hljs-built_in">string</span>;
    age: <span class="hljs-built_in">number</span>;
}
</code></pre>
<p>You can use either types or interfaces, it’s just a matter of preference. You can find out more about how they differ in this <a target="_blank" href="https://www.typescriptlang.org/play/?#example/types-vs-interfaces">official TypeScript documentation playground</a>.</p>
<p>Next, define a union type from each key in your interface.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> MyFormDataKeys = keyof MyAllowedData
<span class="hljs-comment">// this is the same as `type MyFormDataKeys = 'id' | 'name' | 'age'`</span>
</code></pre>
<p>The <code>keyof</code> operator helps to create a union type of an object type’s keys, so it comes in really handy if you don’t want to manually define a union type for a larger object with many keys.</p>
<h2 id="heading-step-2-create-an-append-helper-function">Step 2: Create an Append Helper Function</h2>
<p>Now that you’ve defined your strictly-typed keys, the next step is to create a helper function that ensures only valid keys are appended to FormData.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">appendToFormData</span> (<span class="hljs-params">formData: FormData, key: MyFormDataKeys, value: <span class="hljs-built_in">string</span></span>) </span>{
  formData.append(key, value);
};
</code></pre>
<p>The <code>appendToFormData</code> function takes in three arguments. Here’s how it all works:</p>
<ul>
<li><p>The first argument, <code>formData</code>, is an instance of the FormData object. This is where key/value pairs will be appended before sending them in an API request.</p>
</li>
<li><p>The second argument, <code>key</code>, is the key name of the field you want to append. Its type is of <code>MyFormDataKeys</code>, the union type we created to ensure only those keys we defined are appended to FormData.</p>
</li>
<li><p>The third argument is a string <code>value</code> which represents the value to be appended with the key.</p>
</li>
</ul>
<p>Note that <strong>FormData only accepts the</strong> <code>string</code> <strong>and</strong> <code>Blob</code> <strong>types as values</strong> in each key/value pair. In this guide, we’re only working with string values – but keep in mind that you can use blob values for appending files to API requests.</p>
<p>Now, let’s test out the function:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> payload = <span class="hljs-keyword">new</span> FormData();

appendToFormData(payload, <span class="hljs-string">"id"</span>, <span class="hljs-string">"19282"</span>); <span class="hljs-comment">// ✅ Allowed</span>
appendToFormData(payload, <span class="hljs-string">"name"</span>, <span class="hljs-string">"Lenny Brown"</span>); <span class="hljs-comment">// ✅ Allowed</span>
appendToFormData(payload, <span class="hljs-string">"age"</span>, <span class="hljs-string">"20"</span>); <span class="hljs-comment">// ✅ Allowed</span>

appendToFormData(payload, <span class="hljs-string">"someOtherKey"</span>, <span class="hljs-string">"89"</span>); <span class="hljs-comment">// ❌ TypeScript Error: Argument of type 'someOtherKey' is not assignable.</span>
</code></pre>
<h2 id="heading-step-3-use-the-helper-function-after-form-submission">Step 3: Use the Helper Function after Form Submission</h2>
<p>Now let’s append our fields to FormData before sending them to an API.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSubmitForm = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> payload = <span class="hljs-keyword">new</span> FormData();
   appendToFormData(payload, <span class="hljs-string">"id"</span>, <span class="hljs-string">"19282"</span>);
   appendToFormData(payload, <span class="hljs-string">"name"</span>, <span class="hljs-string">"Lenny Brown"</span>);
   appendToFormData(payload, <span class="hljs-string">"age"</span>, <span class="hljs-string">"20"</span>);

  <span class="hljs-comment">// Send payload via API</span>
  fetch(<span class="hljs-string">"/api/submit"</span>, { method: <span class="hljs-string">"POST"</span>, body: payload });
};
</code></pre>
<h3 id="heading-appending-fields-from-an-object">Appending Fields from an Object</h3>
<p>Alternatively, if you already have your entire payload in an object, you can avoid appending each field one by one by implementing the function like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleSubmitForm = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// all your fields in an object</span>
  <span class="hljs-keyword">const</span> formValues: MyAllowedData = {
    id: <span class="hljs-number">1123</span>,
    name: <span class="hljs-string">'John Doe'</span>,
    age: <span class="hljs-number">56</span>
  }
  <span class="hljs-keyword">const</span> payload = <span class="hljs-keyword">new</span> FormData();

  <span class="hljs-built_in">Object</span>.entries(formValues).forEach(<span class="hljs-function">(<span class="hljs-params">[key, value]</span>) =&gt;</span> {
    appendToFormData(payload, key <span class="hljs-keyword">as</span> MyFormDataKeys, <span class="hljs-string">`<span class="hljs-subst">${value}</span>`</span>); <span class="hljs-comment">// use template literals to pass in value</span>
  });

  <span class="hljs-comment">// Send payload via API</span>
  fetch(<span class="hljs-string">"/api/submit"</span>, { method: <span class="hljs-string">"POST"</span>, body: payload });
};
</code></pre>
<p>In the snippet above, we’re using <code>Object.entries</code> to iterate over each key/value pair in an object so it can be appended to the FormData object. Note that the value in each pair, whether it’s a string or a number, is passed as a string using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">template literals</a> to prevent a TypeScript type mismatch from the <code>value</code> argument in our helper function.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By leveraging TypeScript’s <code>keyof</code> operator, we can make <code>FormData.append()</code> fully type-safe. This simple technique helps prevent key mismatches and makes your API requests more reliable.</p>
<p>Let me know your thoughts about the article, and feel free to make any suggestions you think could improve my solution.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Extract an Error Object from a Blob API Response in JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ I encountered an issue when I made a GET request in my React project which was supposed to return a file I could download. For the file to download properly, I had to make the response type a blob.  But if an error occurred when the server returns a ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-extract-an-error-object-from-a-blob/</link>
                <guid isPermaLink="false">66bb45031eb3452dfca5384e</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Olabisi Olaoye ]]>
                </dc:creator>
                <pubDate>Fri, 29 Mar 2024 00:34:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/React-form-validation--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I encountered an issue when I made a GET request in my React project which was supposed to return a file I could download. For the file to download properly, I had to make the response type a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">blob</a>. </p>
<p>But if an error occurred when the server returns a JSON object, I'd be unable to get that object because I had already defined the response type as a blob. And if I remove the blob definition, the file would just return as regular JSON and might not download properly. </p>
<p>So how do I get the blob to download it and retrieve the error object in case something didn't go well from the server? Thankfully, there's a way to achieve this. </p>
<p>This guide will show you how to retain a JSON object for error handling purposes, while being able to download a file from a server. We'll be using <a target="_blank" href="https://axios-http.com/">Axios</a>, a JavaScript library used for making HTTP requests, to make our API call.</p>
<h2 id="heading-step-1-define-the-response-type-in-the-api-call">Step 1: Define the Response Type in the API Call</h2>
<p>First, define a function that makes the HTTP request to the server. In this case, we're expecting a file, so the conventional HTTP verb would be GET. </p>
<p>The response type for Axios requests is JSON by default, but we want to change that to a blob like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">const</span> getFileFromServer = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://api.some-server.com'</span>, {<span class="hljs-attr">responseType</span>: <span class="hljs-string">'blob'</span>})?.data;
    <span class="hljs-keyword">return</span> res;
}
</code></pre>
<h2 id="heading-step-2-convert-blob-to-text">Step 2: Convert Blob To Text</h2>
<p>In the previous step, we were able to get our file as a blob easily. But when it comes to showing the error, we need it to show as JSON. </p>
<p>First, we need to wrap the request in a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try/catch</a> statement to specify what should happen if an error is thrown while the request is being made.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">const</span> getFileFromServer = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://api.some-server.com'</span>, {<span class="hljs-attr">responseType</span>: <span class="hljs-string">'blob'</span>}).data;
    <span class="hljs-keyword">return</span> res;
    }
    <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">let</span> errorResponse = <span class="hljs-keyword">await</span> error.response.data.text();
        <span class="hljs-keyword">const</span> errorObj = <span class="hljs-built_in">JSON</span>.parse(response);
        <span class="hljs-built_in">console</span>.log(errorObj) <span class="hljs-comment">// log error to console</span>
    }
}
</code></pre>
<p>The type conversion was done inside the <code>catch</code> block. First, we converted the response data to a JSON string using the <code>text()</code> method from JavaScript's <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch API</a>.</p>
<p>Finally, we used the <code>JSON.parse()</code> method to convert that string to actual JSON. That way, we can access the object in its intended format while being able to retrieve the file from the server if there is no error.</p>
<p>Logging the error object to the console will result in something like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"statusCode"</span>: <span class="hljs-number">400</span>,
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Some error occured"</span>
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This is one of the problems I faced in real life, so I thought I'd share it in case someone else encounters it. </p>
<p>Let me know your thoughts about the article, and feel free to make any suggestions you think could improve my solution.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Stepper Component in React ]]>
                </title>
                <description>
                    <![CDATA[ I was working on a React project recently, and I realized that there were so many UI components that I had to build to get things done quickly. So I began to explore a lot of UI libraries like Material UI or Chakra UI. Then I started wondering why I ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-stepper-component-in-react/</link>
                <guid isPermaLink="false">66bb44fff34335150558aa13</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Olabisi Olaoye ]]>
                </dc:creator>
                <pubDate>Wed, 10 Jan 2024 15:28:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/12/Your-paragraph-text-3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I was working on a React project recently, and I realized that there were so many UI components that I had to build to get things done quickly. So I began to explore a lot of UI libraries like <a target="_blank" href="https://mui.com">Material UI</a> or <a target="_blank" href="https://chakra-ui.com/">Chakra UI</a>. Then I started wondering why I hadn't attempted to build some of these components myself. </p>
<p>So, I decided to embark on an adventure to build as many components as I could. Sure, it would take more time, but I would get to learn how these components are being built.</p>
<p>In this guide, you'll learn how to build a stepper, a UI component that guides a user through a process by dividing it into a number of steps. We'll achieve this with React and Tailwind CSS, an open-source, utility-first CSS framework which allows you to style your HTML directly without having to open a <code>.css</code> file. </p>
<p>Here are some prerequisites you need before you can effectively follow this guide:</p>
<ul>
<li>Familiarity with HTML, CSS, and JavaScript</li>
<li>Knowledge of React fundamentals</li>
</ul>
<h2 id="heading-how-to-set-up-the-project">How to Set Up the Project</h2>
<p>First, you need to create a React project. In the terminal of your text editor and at your chosen directory, type in this command:</p>
<pre><code class="lang-bash">npx create-react-app my-stepper
</code></pre>
<p>Next, install Tailwind CSS in your project with this command:</p>
<pre><code class="lang-bash">npm install -D tailwindcss
</code></pre>
<p>When this is done, a tailwind.config.js file is automatically created for you in the root directory of the project. It initially contains no config in particular, so you'll have to add the paths to your template files like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./src/**/*.{js,jsx,ts,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<h2 id="heading-how-to-build-the-stepper-layout">How to Build the Stepper Layout</h2>
<p>Next, create a Stepper component in the <code>src</code> directory of your project. It should be able to move to the next step by showing some visible color change or helper text, or both. For example, an inactive step can be gray in color and an active step can be blue. </p>
<p>Basically, we should be able to move back and forth between steps. The layout will only involve a circle and a connector line for now, but later some JavaScript will help to replicate the circles and connector lines depending on how many steps the stepper will consist of.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Stepper.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Stepper</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"rounded-full bg-blue-500 w-6 h-6"</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">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-1 w-8 bg-blue-500"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>In the code block above, I styled the basic stepper look: a rounded circle with a blue background, and a short line right beside it. They will both be replicated, depending on the number of steps you want to show.</p>
<p>For the purpose of this guide, you can add two buttons in the <code>App</code> component to switch back and forth between steps. Then the Stepper component can be rendered just above them like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//App.js</span>
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Stepper <span class="hljs-keyword">from</span> <span class="hljs-string">'./Stepper'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>)  </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Stepper</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Previous step<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Next step<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
    )
}
</code></pre>
<h2 id="heading-how-to-add-functionality-to-the-stepper">How to Add Functionality to the Stepper</h2>
<p>To make a basic stepper work, there are two things to take note of: how many steps there are in total, and what the current step is. </p>
<p>If you're dealing with five steps, for instance, you need to know which step is the active one. This means that you'll need these two bits of information inside the <code>Stepper</code> component in the form of props. </p>
<p>From the total number of steps passed into the component, you can generate a group of circles and connector lines that number of times. You can use JavaScript's inbuilt <code>Array.from()</code> function to create an array of steps like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Stepper</span> (<span class="hljs-params">{currentStep, numberOfSteps}</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
        {Array.from({length: numberOfSteps}).map((_, index) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">w-6</span> <span class="hljs-attr">h-6</span> <span class="hljs-attr">rounded-full</span>`}&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">w-12</span> <span class="hljs-attr">h-1</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">React.Fragment</span>&gt;</span>
          ))}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>In the code block above, I used <code>React.Fragment</code> so I wouldn't have to wrap the circle div and the connector div in a redundant div (JSX expressions must have only one parent element). </p>
<p>Also, when mapping the array, I used the <code>_</code> symbol as the first parameter in the <code>map</code> function because we don't need it. It's more of a 'throwaway' parameter that we don't use because we just need to access the index parameter of the array.</p>
<p>Let's do a bit of styling. Each active step has to have some distinct color to indicate to the user that it's the current step in the component. </p>
<p>To implement this, create an <code>activeColor</code> function that takes in as an argument the current index of the array generated from <code>Array.from()</code>, and compares it with the <code>currentStep</code> variable. If the current index matches the current step, a distinct color is used, otherwise an inactive color is used.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> activeColor = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> currentStep === index ? <span class="hljs-string">"bg-blue-500"</span> : <span class="hljs-string">"bg-gray-300"</span>
</code></pre>
<p>The condition incorporated in the <code>activeColor</code> function above states, in simple terms, "at this point in our array of steps, are we at the current step? If so, the step color is blue. Otherwise, it's gray." </p>
<p>Now, add that line of code to the <code>Stepper</code> component and call the function in the class name of each circle and connector line.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Stepper</span> (<span class="hljs-params">{currentStep, numberOfSteps}</span>) </span>{
  <span class="hljs-keyword">const</span> activeColor = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> currentStep &gt;= index ? <span class="hljs-string">'bg-blue-500'</span> : <span class="hljs-string">'bg-gray-300'</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
      {Array.from({length: numberOfSteps}).map((_, index) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">w-6</span> <span class="hljs-attr">h-6</span> <span class="hljs-attr">rounded-full</span> ${<span class="hljs-attr">activeColor</span>(<span class="hljs-attr">index</span>)}`}&gt;</span>  
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">w-12</span> <span class="hljs-attr">h-1</span> ${<span class="hljs-attr">activeColor</span>(<span class="hljs-attr">index</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">React.Fragment</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>There's a bit of a problem, though. Because the circle and connector line is just being replicated with respect to the number of steps, this is the current result:  </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/image-167.png" alt="Image" width="600" height="400" loading="lazy">
<em>Stepper showing the extra connector line at the end</em></p>
<p>There's an extra connector line at the end – but we can easily fix this by conditionally rendering it. The condition will simply check whether the current index in the array is on the final step. If so, then there's no need to render a connector line. This way, that lone connector line disappears on the last step. </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> isFinalStep = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> index === numberOfSteps - <span class="hljs-number">1</span>
</code></pre>
<p>So when I call this function in the <code>Stepper</code> component's JSX, the connector line will only be displayed if that step isn't the current index of the array isn't equal to the number of steps (minus one, since we are working with a zero-indexed array). The <code>Stepper</code> component, all finished, will then look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Stepper</span> (<span class="hljs-params">{currentStep, numberOfSteps}</span>) </span>{
  <span class="hljs-keyword">const</span> activeColor = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> currentStep &gt;= index ? <span class="hljs-string">'bg-blue-500'</span> : <span class="hljs-string">'bg-gray-300'</span>
  <span class="hljs-keyword">const</span> isFinalStep = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> index === numberOfSteps - <span class="hljs-number">1</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
      {Array.from({length: numberOfSteps}).map((_, index) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">w-6</span> <span class="hljs-attr">h-6</span> <span class="hljs-attr">rounded-full</span> ${<span class="hljs-attr">activeColor</span>(<span class="hljs-attr">index</span>)}`}&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          {isFinalStep(index) ? null : <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">w-12</span> <span class="hljs-attr">h-1</span> ${<span class="hljs-attr">activeColor</span>(<span class="hljs-attr">index</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">React.Fragment</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<h2 id="heading-how-to-pass-in-the-props">How to Pass in the Props</h2>
<p>Since the <code>Stepper</code> component accepts <code>currentStep</code> and <code>numberOfSteps</code> as props, these two need to be defined in the <code>App</code> component. </p>
<p>Remember, the current step changes, so it needs to be tracked. You can use React's <code>useState</code> hook for this. The initial state is set to zero, which is the first step. For the purpose of this guide, I'll be using five steps.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//App.js</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [currentStep, setCurrentStep] = React.useState(<span class="hljs-number">0</span>)
    <span class="hljs-keyword">const</span> NUMBER_OF_STEPS = <span class="hljs-number">5</span>

    <span class="hljs-keyword">return</span> (
        <span class="hljs-comment">//...some code</span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Stepper</span> <span class="hljs-attr">currentStep</span>=<span class="hljs-string">{currentStep}</span> <span class="hljs-attr">numberOfSteps</span>=<span class="hljs-string">{NUMBER_OF_STEPS}/</span>&gt;</span></span>
        <span class="hljs-comment">//...some code</span>
    )
}
</code></pre>
<h2 id="heading-how-to-move-back-and-forth-between-steps">How to Move Back and Forth Between Steps</h2>
<p>The last thing left to do is to add some functionality to those two buttons in the <code>App</code> component. </p>
<p>Create two functions, <code>goToPreviousStep</code> and <code>goToNextStep</code>, which will simply decrement or increment the current step state. </p>
<p>To prevent the previous button from decrementing past zero, since the first step has an index of zero, you can add a condition to check whether the current step is greater than or equal to zero. That will be the lower boundary of the stepper. </p>
<p>For the next button, the current step should not go past the number of steps minus one, since we are dealing with a <a target="_blank" href="https://en.wikipedia.org/wiki/Zero-based_numbering">zero-indexed</a> array.</p>
<p>Here's the final code for the <code>App</code> component:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Stepper <span class="hljs-keyword">from</span> <span class="hljs-string">'./Stepper'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [currentStep, setCurrentStep] = React.useState(<span class="hljs-number">0</span>)
  <span class="hljs-keyword">const</span> NUMBER_OF_STEPS = <span class="hljs-number">5</span>

  <span class="hljs-keyword">const</span> goToNextStep = <span class="hljs-function">() =&gt;</span> setCurrentStep(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev === NUMBER_OF_STEPS - <span class="hljs-number">1</span> ? prev : prev + <span class="hljs-number">1</span>)
  <span class="hljs-keyword">const</span> goToPreviousStep = <span class="hljs-function">() =&gt;</span> setCurrentStep(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev &lt;= <span class="hljs-number">0</span> ? prev : prev - <span class="hljs-number">1</span>)

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl"</span>&gt;</span>Here is the stepper in action!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Stepper</span> <span class="hljs-attr">currentStep</span>=<span class="hljs-string">{currentStep}</span> <span class="hljs-attr">numberOfSteps</span>=<span class="hljs-string">{NUMBER_OF_STEPS}/</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex gap-10"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{goToPreviousStep}</span> 
          <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-600 text-white p-2 rounded-md"</span>
        &gt;</span>
          Previous step
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{goToNextStep}</span> 
          <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-600 text-white p-2 rounded-md"</span>
        &gt;</span>
          Next step
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>And that's it! You've successfully built a stepper component in a React project. You can play around with this <a target="_blank" href="https://stackblitz.com/edit/stackblitz-starters-ga6rgw?file=src%2FApp.js">working demo on Stackblitz</a>. Please let me know your thoughts and suggestions about this article.</p>
<p>Bonus: You can take this code up a notch by adding labels and descriptions to each step.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
