<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Building Products & Sharing the Journey]]></title><description><![CDATA[Writing about tech by building products and exploring ideas.]]></description><link>https://blog.pulkitgangwar.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 16 Oct 2026 11:58:55 GMT</lastBuildDate><atom:link href="https://blog.pulkitgangwar.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Scroll animation for text in React]]></title><description><![CDATA[We will learn how to create a simple scroll animation. We will not use any animation library. The animation we will be creating looks like this.

The idea is that we will define two opacities. The default opacity will be 0.2 while the opacity when th...]]></description><link>https://blog.pulkitgangwar.com/scroll-animation-for-text-in-react</link><guid isPermaLink="true">https://blog.pulkitgangwar.com/scroll-animation-for-text-in-react</guid><category><![CDATA[React]]></category><category><![CDATA[react-animation]]></category><category><![CDATA[parallax scroll animation with react js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Pulkit Gangwar]]></dc:creator><pubDate>Sun, 02 Apr 2023 09:10:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1688893126986/873474c6-3d1a-44cf-b3f2-439c1902e7ba.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We will learn how to create a simple scroll animation. We will not use any animation library. The animation we will be creating looks like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1688892951257/5c7ecbf7-cd51-440b-9b14-df533a0f24bb.gif" alt class="image--center mx-auto" /></p>
<p>The idea is that we will define two opacities. The default opacity will be 0.2 while the opacity when the text is visible will be 1. We will also divide the sentence into an array of strings so that we can work on each word individually. After that, we will define a threshold for each word. After the word has crossed that threshold the word will get an opacity of 1.</p>
<p>Let's first initialize an empty react-typescript project with tailwindcss.</p>
<pre><code class="lang-bash">yarn create vite
</code></pre>
<blockquote>
<p>You can refer to this <a target="_blank" href="https://tailwindcss.com/docs/guides/vite">link</a> to install tailwindcss with react.</p>
</blockquote>
<p>Let's create a scroller component with some styles.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

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

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;section className=<span class="hljs-string">"bg-black h-[500vh] text-center"</span>&gt;

      &lt;/section&gt;
    &lt;/&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Scroller;
</code></pre>
<p>Now we have to add a scroll event on the window.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

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

useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">scroller</span>(<span class="hljs-params">e: <span class="hljs-built_in">any</span></span>) </span>{
    }

    <span class="hljs-keyword">if</span> (sectionRef.current) {
      <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"scroll"</span>, scroller);
    }

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

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;section className=<span class="hljs-string">"bg-black h-[500vh] text-center"</span>&gt;

      &lt;/section&gt;
    &lt;/&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Scroller;
</code></pre>
<p>We are simply just adding an event listener on the window and cleaning up the listener on unmount.</p>
<p>Now to calculate how much section is scrolled we will use refs and getboundingclientrect for the container section.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Scroller</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> sectionRef = useRef&lt;HTMLElement | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
    <span class="hljs-keyword">const</span> [progress,setProgress] = useState(<span class="hljs-number">0</span>);

    useEffect(<span class="hljs-function">() =&gt;</span> {

        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">scroller</span>(<span class="hljs-params">e: <span class="hljs-built_in">any</span></span>) </span>{
            <span class="hljs-keyword">if</span> (sectionRef.current) {
            <span class="hljs-keyword">const</span> elScrollTop = sectionRef.current.getBoundingClientRect().top;
            <span class="hljs-keyword">const</span> elScrollHeight =
              sectionRef.current.getBoundingClientRect().height;

            <span class="hljs-keyword">const</span> currentProgress =
              <span class="hljs-built_in">Math</span>.abs(elScrollTop) / (elScrollHeight - <span class="hljs-built_in">window</span>.innerHeight);
            <span class="hljs-keyword">if</span> (currentProgress &gt; <span class="hljs-number">1.1</span>) {
              <span class="hljs-keyword">return</span>;
            }
            setProgress(currentProgress);

      }
        }

        <span class="hljs-keyword">if</span> (sectionRef.current) {
          <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"scroll"</span>, scroller);
        }

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

      <span class="hljs-keyword">return</span> (
        &lt;&gt;
          &lt;section ref={sectionRef} className=<span class="hljs-string">"bg-black h-[500vh] text-center"</span>&gt;

          &lt;/section&gt;
        &lt;/&gt;
      );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Scroller;
</code></pre>
<p>To calculate the scroll percentage we are taking the container's total height and the offset Y or top. We will divide these two values to get a value between 0 and 1. We are setting this value to our state.</p>
<p>Now let's complete our animation by adding the logic for text.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Scroller</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> sectionRef = useRef&lt;HTMLElement | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
    <span class="hljs-keyword">const</span> [progress,setProgress] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">const</span> text = <span class="hljs-string">"This text is animated on scroll"</span>;
    <span class="hljs-keyword">const</span> totalTextLength = text.split(<span class="hljs-string">" "</span>).length;

    useEffect(<span class="hljs-function">() =&gt;</span> {

        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">scroller</span>(<span class="hljs-params">e: <span class="hljs-built_in">any</span></span>) </span>{
            <span class="hljs-keyword">if</span> (sectionRef.current) {
            <span class="hljs-keyword">const</span> elScrollTop = sectionRef.current.getBoundingClientRect().top;
            <span class="hljs-keyword">const</span> elScrollHeight =
              sectionRef.current.getBoundingClientRect().height;

            <span class="hljs-keyword">const</span> currentProgress =
              <span class="hljs-built_in">Math</span>.abs(elScrollTop) / (elScrollHeight - <span class="hljs-built_in">window</span>.innerHeight);
            <span class="hljs-keyword">if</span> (currentProgress &gt; <span class="hljs-number">1.1</span>) {
               <span class="hljs-comment">// to stop from updating state when the content is not visible</span>
              <span class="hljs-keyword">return</span>;
            }
            setProgress(currentProgress);

          }
        }

        <span class="hljs-keyword">if</span> (sectionRef.current) {
          <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"scroll"</span>, scroller);
        }

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

      <span class="hljs-keyword">return</span> (
        &lt;&gt;
          &lt;section ref={sectionRef} className=<span class="hljs-string">"bg-black h-[500vh] text-center"</span>&gt;
            &lt;div className=<span class="hljs-string">"fixed top-[50%] translate-y-[-50%] bg-black "</span>&gt;
              {text.split(<span class="hljs-string">" "</span>).map(<span class="hljs-function">(<span class="hljs-params">word, index</span>) =&gt;</span> {
                <span class="hljs-keyword">const</span> textNumber = index + <span class="hljs-number">1</span>;
                <span class="hljs-keyword">const</span> currentTextThreshold = (<span class="hljs-number">1</span> / totalTextLength) * textNumber;
                <span class="hljs-keyword">const</span> opacity = progress &gt;= currentTextThreshold ? <span class="hljs-number">1</span> : <span class="hljs-number">0.2</span>;

                <span class="hljs-keyword">return</span> (
                  &lt;h1
                    key={<span class="hljs-string">`<span class="hljs-subst">${word}</span> <span class="hljs-subst">${index}</span>`</span>}
                    className={<span class="hljs-string">`transition ease-in-out delay-15 font-black inline-block px-[4px] md:px-[8px] lg:px-[16px] text-[48px] md:text-[64px] lg:text-[102px] text-white`</span>}
                    style={{ opacity }}
                  &gt;
                    {word}
                  &lt;/h1&gt;
                );
              })}
            &lt;/div&gt;
          &lt;/section&gt;
        &lt;/&gt;
      );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Scroller;
</code></pre>
<p>We have added a text string and split it on " " to make an array of strings. We are defining a threshold depending on the length of the array of strings. If the scroll progress (value between 0 and 1) has crossed that word threshold. We will give that word opacity of 1.</p>
<blockquote>
<p>You can refer to this <a target="_blank" href="https://github.com/pulkitgangwar/scroll-animation-for-text-in-react">link</a> for the code.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Shopify Web Pixel Api]]></title><description><![CDATA[Shopify Web Pixel API makes it easier to collect customers' behavioural data. We will be creating a simple tracker that sends add-to-cart events and event data to our app backend. While making this tracker we will understand how this extension works....]]></description><link>https://blog.pulkitgangwar.com/shopify-web-pixel-api</link><guid isPermaLink="true">https://blog.pulkitgangwar.com/shopify-web-pixel-api</guid><category><![CDATA[shopify]]></category><category><![CDATA[Shopify Development]]></category><category><![CDATA[shopify app]]></category><category><![CDATA[shopify web pixel]]></category><category><![CDATA[web pixel api]]></category><dc:creator><![CDATA[Pulkit Gangwar]]></dc:creator><pubDate>Thu, 02 Mar 2023 15:43:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1686753473816/2f9b9711-f1c9-49ae-9bfe-a2d499639542.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Shopify Web Pixel API makes it easier to collect customers' behavioural data. We will be creating a simple tracker that sends add-to-cart events and event data to our app backend. While making this tracker we will understand how this extension works.</p>
<blockquote>
<p><strong>Prerequisites</strong></p>
<p>I am assuming that you have some knowledge of building Shopify apps.</p>
<p>All the code is available on my GitHub repository (<a target="_blank" href="https://github.com/pulkitgangwar/shopify-web-pixel-api-example">Link</a>).</p>
</blockquote>
<p>I have already created a basic test app using the command</p>
<pre><code class="lang-bash">yarn create @shopify/app
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675864019238/73ffc481-01e4-4d86-a5c0-b02dabff0e9c.png" alt class="image--center mx-auto" /></p>
<p>To use Pixel API our app needs two Shopify scopes. Scopes are write_pixel and read_customer_events.</p>
<p><code>./shopify.app.toml</code></p>
<pre><code class="lang-ini"><span class="hljs-comment"># This file stores configurations for your Shopify app.</span>

<span class="hljs-attr">scopes</span> = <span class="hljs-string">"write_products,write_pixels,read_customer_events"</span>
</code></pre>
<p>We need to install the app on our development store using the command.</p>
<pre><code class="lang-bash">yarn dev
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686670777717/8b01f491-e4b4-47a1-b8a1-3049cda93803.png" alt class="image--center mx-auto" /></p>
<p>With this app installation end's on the store now we need to start tracking events with pixel API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686670833546/f369a49a-f9df-4040-b6fc-6ce29d09c493.png" alt class="image--center mx-auto" /></p>
<p>To connect pixel API with our current app we will use a Shopify command to create an extension.</p>
<pre><code class="lang-bash">yarn run shopify app generate extension
</code></pre>
<p>There are many extension options available we need to choose Web Pixel.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686670325525/e8f85c26-fc33-4d62-bc10-086ca5141a9a.png" alt class="image--center mx-auto" /></p>
<p>After this, we can see that a new folder has appeared in our app folder structure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686670400501/82d2bb23-41ed-4348-ab0e-6f9f8b860344.png" alt class="image--center mx-auto" /></p>
<p>Now we need to create an endpoint to enable pixel API. We will use the webPixelCreate mutation.</p>
<pre><code class="lang-javascript">app.post(<span class="hljs-string">"/api/webpixel"</span>, <span class="hljs-keyword">async</span> (_req, res) =&gt; {
  <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> shopify.api.clients.Graphql({
    <span class="hljs-attr">session</span>: res.locals.shopify.session,
  });

  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> client.query({
    <span class="hljs-attr">data</span>: {
      <span class="hljs-attr">query</span>: <span class="hljs-string">`
      mutation webPixelCreate($webPixelInput: WebPixelInput!) {
        webPixelCreate(webPixel: $webPixelInput) {
          webPixel {
            settings
            id
          }
          userErrors {
            code
            field
            message
          }
        }
      }
      `</span>,
      <span class="hljs-attr">variables</span>: {
        <span class="hljs-attr">webPixelInput</span>: {
          <span class="hljs-attr">settings</span>: {
            <span class="hljs-attr">accountID</span>: crypto.randomUUID(),
          },
        },
      },
    },
  });

  <span class="hljs-keyword">if</span> (response.body.data.webPixelCreate.userErrors.length) {
    <span class="hljs-keyword">const</span> errors = queryResponse.body.data.webPixelCreate.userErrors;
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errors.map(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> error.message).join(<span class="hljs-string">" , "</span>));
  }

  <span class="hljs-keyword">return</span> response.body.data.webPixelCreate.webPixel;
});
</code></pre>
<p>We are first creating a graphql client. After that we are sending a webPixelCreate mutation.</p>
<blockquote>
<p>The accountID in variables is the setting that we pass from webpixel to Shopify. We can create other variables like this by editing the shopify.ui.extension.toml file.</p>
</blockquote>
<p>Let's create a button on the frontend to consume this endpoint</p>
<p><code>./web/frontend/pages/index.jsx</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  Card,
  Page,
  Layout,
  TextContainer,
  Image,
  Stack,
  Link,
  Text,
  Button,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@shopify/polaris"</span>;
<span class="hljs-keyword">import</span> {
  TitleBar,
  useAuthenticatedFetch,
  useToast,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@shopify/app-bridge-react"</span>;
<span class="hljs-keyword">import</span> { useTranslation, Trans } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;

<span class="hljs-keyword">import</span> { trophyImage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../assets"</span>;

<span class="hljs-keyword">import</span> { ProductsCard } <span class="hljs-keyword">from</span> <span class="hljs-string">"../components"</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">HomePage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { t } = useTranslation();
  <span class="hljs-keyword">const</span> fetch = useAuthenticatedFetch();
  <span class="hljs-keyword">const</span> toast = useToast();

  <span class="hljs-keyword">const</span> enableTracker = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/webpixel"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
    });
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

    toast.show(<span class="hljs-string">"Tracker installed"</span>);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Page</span> <span class="hljs-attr">narrowWidth</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">primary</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{enableTracker}</span>&gt;</span>
        Enable Tracker
      <span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TitleBar</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{t(</span>"<span class="hljs-attr">HomePage.title</span>")} <span class="hljs-attr">primaryAction</span>=<span class="hljs-string">{null}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Layout.Section</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Card</span> <span class="hljs-attr">sectioned</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Stack</span>
              <span class="hljs-attr">wrap</span>=<span class="hljs-string">{false}</span>
              <span class="hljs-attr">spacing</span>=<span class="hljs-string">"extraTight"</span>
              <span class="hljs-attr">distribution</span>=<span class="hljs-string">"trailing"</span>
              <span class="hljs-attr">alignment</span>=<span class="hljs-string">"center"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Stack.Item</span> <span class="hljs-attr">fill</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">TextContainer</span> <span class="hljs-attr">spacing</span>=<span class="hljs-string">"loose"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">as</span>=<span class="hljs-string">"h2"</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"headingMd"</span>&gt;</span>
                    {t("HomePage.heading")}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">Text</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">Trans</span>
                      <span class="hljs-attr">i18nKey</span>=<span class="hljs-string">"HomePage.yourAppIsReadyToExplore"</span>
                      <span class="hljs-attr">components</span>=<span class="hljs-string">{{</span>
                        <span class="hljs-attr">PolarisLink:</span> (
                          &lt;<span class="hljs-attr">Link</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"https://polaris.shopify.com/"</span> <span class="hljs-attr">external</span> /&gt;</span>
                        ),
                        AdminApiLink: (
                          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                            <span class="hljs-attr">url</span>=<span class="hljs-string">"https://shopify.dev/api/admin-graphql"</span>
                            <span class="hljs-attr">external</span>
                          /&gt;</span>
                        ),
                        AppBridgeLink: (
                          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                            <span class="hljs-attr">url</span>=<span class="hljs-string">"https://shopify.dev/apps/tools/app-bridge"</span>
                            <span class="hljs-attr">external</span>
                          /&gt;</span>
                        ),
                      }}
                    /&gt;
                  <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>{t("HomePage.startPopulatingYourApp")}<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">Trans</span>
                      <span class="hljs-attr">i18nKey</span>=<span class="hljs-string">"HomePage.learnMore"</span>
                      <span class="hljs-attr">components</span>=<span class="hljs-string">{{</span>
                        <span class="hljs-attr">ShopifyTutorialLink:</span> (
                          &lt;<span class="hljs-attr">Link</span>
                            <span class="hljs-attr">url</span>=<span class="hljs-string">"https://shopify.dev/apps/getting-started/add-functionality"</span>
                            <span class="hljs-attr">external</span>
                          /&gt;</span>
                        ),
                      }}
                    /&gt;
                  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">TextContainer</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">Stack.Item</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Stack.Item</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">padding:</span> "<span class="hljs-attr">0</span> <span class="hljs-attr">20px</span>" }}&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">Image</span>
                    <span class="hljs-attr">source</span>=<span class="hljs-string">{trophyImage}</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">{t(</span>"<span class="hljs-attr">HomePage.trophyAltText</span>")}
                    <span class="hljs-attr">width</span>=<span class="hljs-string">{120}</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">Stack.Item</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Stack</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Card</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Layout.Section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Layout.Section</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ProductsCard</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Layout.Section</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Page</span>&gt;</span></span>
  );
}
</code></pre>
<p>This is a simple react component that we will use to enable the tracker.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686670700544/c911a296-0a1b-4385-8f74-78e55436e069.png" alt class="image--center mx-auto" /></p>
<p>Now let's send some customer events to our backend.</p>
<p>Pixel API can subscribe to different events the most common ones are</p>
<ol>
<li><p>checkout_started - Fires on checkout start.</p>
</li>
<li><p>collection_viewed - Fires on collection page view.</p>
</li>
<li><p>page_viewed - Fires when customers view any page.</p>
</li>
<li><p>product_added_to_cart - Fires when the customer adds any product to the cart.</p>
</li>
<li><p>product_viewed - Fires when the customer visits the product page.</p>
</li>
<li><p>checkout_completed - Fires on checkout complete.</p>
</li>
</ol>
<p>With Every event, we get an event object which consists of different types of data depending on the event. For example, when product_added_to_cart event fires then we will get an event object. This object will contain information about the product that the customer has added.</p>
<p><code>./extensions/tracker/src/index.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { register } <span class="hljs-keyword">from</span> <span class="hljs-string">"@shopify/web-pixels-extension"</span>;

register(<span class="hljs-function">(<span class="hljs-params">{ analytics }</span>) =&gt;</span> {
  analytics.subscribe(<span class="hljs-string">"product_added_to_cart"</span>, <span class="hljs-keyword">async</span> (event) =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"event"</span>, event);
  });
});
</code></pre>
<p>Now when we add a product to our cart we will get an event of this type.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"sh-b52dc9da-35AC-47D6-3C29-8ABBBD3D7BBB"</span>,
    <span class="hljs-attr">"clientId"</span>: <span class="hljs-string">"76dfa2b795f37436170c42978f9dbaab"</span>,
    <span class="hljs-attr">"timestamp"</span>: <span class="hljs-string">"2023-06-13T14:34:12.629Z"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"product_added_to_cart"</span>,
    <span class="hljs-attr">"context"</span>: {
        <span class="hljs-attr">"document"</span>: {
            <span class="hljs-attr">"location"</span>: {
               <span class="hljs-comment">// window location related </span>
            },
        },
        <span class="hljs-attr">"navigator"</span>: {
            <span class="hljs-comment">// window navigator properties</span>
        },
        <span class="hljs-attr">"window"</span>: {
          <span class="hljs-comment">// some important window properties</span>
        }
    },
    <span class="hljs-attr">"data"</span>: {
        <span class="hljs-attr">"cartLine"</span>: {
            <span class="hljs-attr">"cost"</span>: {
                <span class="hljs-attr">"totalAmount"</span>: {
                    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">600</span>,
                    <span class="hljs-attr">"currencyCode"</span>: <span class="hljs-string">"INR"</span>
                }
            },
            <span class="hljs-attr">"merchandise"</span>: {
                <span class="hljs-attr">"id"</span>: <span class="hljs-string">"123456789"</span>,
                <span class="hljs-attr">"image"</span>: {
                    <span class="hljs-attr">"src"</span>: <span class="hljs-string">"https://something-random.com"</span>
                },
                <span class="hljs-attr">"price"</span>: {
                    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">600</span>,
                    <span class="hljs-attr">"currencyCode"</span>: <span class="hljs-string">"INR"</span>
                },
                <span class="hljs-attr">"product"</span>: {
                    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"8123348812079"</span>,
                    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"The Collection Snowboard: Hydrogen"</span>,
                    <span class="hljs-attr">"vendor"</span>: <span class="hljs-string">"Hydrogen Vendor"</span>,
                    <span class="hljs-attr">"type"</span>: <span class="hljs-string">""</span>,
                    <span class="hljs-attr">"untranslatedTitle"</span>: <span class="hljs-string">"The Collection Snowboard: Hydrogen"</span>
                },
                <span class="hljs-attr">"sku"</span>: <span class="hljs-string">""</span>,
                <span class="hljs-attr">"title"</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">"untranslatedTitle"</span>: <span class="hljs-literal">null</span>
            },
            <span class="hljs-attr">"quantity"</span>: <span class="hljs-number">1</span>
        }
    }
}
</code></pre>
<p>To send this event to the backend we will use window beacon API.</p>
<p><mark>The endpoint that pixel api request to send tracking data should be open. Pixel api request doesn't have the session token generated by the shopify app bridge. This session token validates the request.</mark></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { register } <span class="hljs-keyword">from</span> <span class="hljs-string">"@shopify/web-pixels-extension"</span>;
<span class="hljs-keyword">import</span> {API_URL} <span class="hljs-keyword">from</span> <span class="hljs-string">"./constants"</span>;

register(<span class="hljs-function">(<span class="hljs-params">{ analytics, browser }</span>) =&gt;</span> {
  analytics.subscribe(<span class="hljs-string">"product_added_to_cart"</span>, <span class="hljs-keyword">async</span> (event) =&gt; {
    <span class="hljs-keyword">const</span> url = <span class="hljs-string">`<span class="hljs-subst">${API_URL}</span>/track`</span>;
    <span class="hljs-keyword">const</span> isSuccessful = <span class="hljs-keyword">await</span> browser.sendBeacon(
      url,
      <span class="hljs-built_in">JSON</span>.stringify({ ...event })
    );
    <span class="hljs-keyword">if</span> (!isSuccessful) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Unable to send via Beacon API"</span>);
  });
});
</code></pre>
<p>We can now create a backend route for this endpoint.</p>
<pre><code class="lang-typescript">app.post(<span class="hljs-string">"/api/track"</span>, <span class="hljs-function">(<span class="hljs-params">_req, res</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(_req.body);
});
</code></pre>
<p>With this our simple tracker is finished!.</p>
]]></content:encoded></item><item><title><![CDATA[Shopify Webhook Processing in Nestjs]]></title><description><![CDATA[Webhooks are simple requests that many apps or websites use to be in sync with each other. An example would be working with Razorpay API. Razorpay will handle the payment processing of users and inform us about payment information. We can subscribe t...]]></description><link>https://blog.pulkitgangwar.com/shopify-webhook-processing-in-nestjs</link><guid isPermaLink="true">https://blog.pulkitgangwar.com/shopify-webhook-processing-in-nestjs</guid><category><![CDATA[shopify apps, ]]></category><category><![CDATA[shopify]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[shopify webhooks]]></category><dc:creator><![CDATA[Pulkit Gangwar]]></dc:creator><pubDate>Thu, 02 Feb 2023 12:46:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671431565380/3Qcdwm8Mg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Webhooks are simple requests that many apps or websites use to be in sync with each other. An example would be working with Razorpay API. Razorpay will handle the payment processing of users and inform us about payment information. We can subscribe to Razorpay webhooks to get information about users' payments.</p>
<p>Webhook endpoints are public and anyone can send a request to these endpoints. To protect these endpoints we process the request before handling them.</p>
<blockquote>
<p><strong>Prerequisites</strong></p>
<p>I am assuming that you have some knowledge of building Shopify apps. You have a basic understanding of how nestjs project works.</p>
<p>Shopify provides a method to process the webhook request i.e. <code>Shopify.Webhooks.Registry.process(request,response)</code> but let's see how to process it manually in nestjs.</p>
</blockquote>
<p>Shopify webhook processing method.</p>
<pre><code class="lang-typescript">
<span class="hljs-meta">@Controller</span>(<span class="hljs-string">'/api/webhook'</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookController {

  <span class="hljs-meta">@Post</span>(<span class="hljs-string">'/'</span>)
  <span class="hljs-keyword">async</span> processWebhook(
    <span class="hljs-meta">@Req</span>() req: RawBodyRequest&lt;IncomingMessage&gt;,
    <span class="hljs-meta">@Res</span>() res: Response,
  ) {
       <span class="hljs-keyword">await</span> Shopify.Webhooks.Registry.process(req, res);
       <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Webhook processed, returned status code 200`</span>);
       res.status(<span class="hljs-number">200</span>).send(<span class="hljs-string">"completed"</span>);
  }
}
</code></pre>
<p>Now let's see how to process Shopify webhooks. First, we have to create a Shopify webhook module with its controller and service.</p>
<p><code>src/shopify-webhook/shopify-webhook.module.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Module</span>({
  controllers: [ShopifyWebhookController],
  providers: [ShopifyWebhookService],
  <span class="hljs-built_in">exports</span>: [ShopifyWebhookService],
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookModule {}
</code></pre>
<p>We have exported the webhook service so that other modules can use this service. We can register and add handlers for Shopify webhooks in the <code>onModuleInit</code> function.</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Module</span>({
  controllers: [ShopifyWebhookController],
  providers: [ShopifyWebhookService],
  <span class="hljs-built_in">exports</span>: [ShopifyWebhookService],
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookModule <span class="hljs-keyword">implements</span> OnModuleInit {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> shopifyWebhookService: ShopifyWebhookService</span>) {}

  onModuleInit() {
    Shopify.Webhooks.Registry.addHandlers({
      APP_UNINSTALLED: {
        path: <span class="hljs-string">'/api/webhook/'</span>,
        webhookHandler: <span class="hljs-built_in">this</span>.shopifyWebhookService.processUninstall,
      },
    });
  }
}
</code></pre>
<p>This will add an app uninstall webhook to the local Shopify registry. To register the webhook we have to call <code>Shopify.Webhooks.Registry.register({topic:"",shop:"",accessToken:"",path:""})</code>.</p>
<p>The register function takes in the topic or type of webhook. Shop's name to register webhook for that shop. The access token that we get when we install the app. It also requires a path. When someone uninstalls the app, Shopify will send a request to this path.</p>
<p>To validate webhook requests we require a raw body. To get the raw body of the request we have to tell nest.js to provide it with every request. We can do this in nest bootstrap file</p>
<p><code>src/main.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NestFactory } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/core'</span>;
<span class="hljs-keyword">import</span> { AppModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.module'</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bootstrap</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> app = <span class="hljs-keyword">await</span> NestFactory.create(AppModule, {
    rawBody: <span class="hljs-literal">true</span>,
  });

  <span class="hljs-keyword">const</span> port = process.env.BACKEND_PORT || process.env.PORT;
  <span class="hljs-keyword">await</span> app.listen(port);
}
bootstrap();
</code></pre>
<p>Let's create the controller which will handle the webhook request.</p>
<p><code>src/shopify-webhook/shopify-webhook.controller.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Controller</span>(<span class="hljs-string">'/api/webhook'</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookController {
  <span class="hljs-meta">@Post</span>(<span class="hljs-string">'/'</span>)
  <span class="hljs-keyword">async</span> processWebhook(
    <span class="hljs-meta">@Req</span>() req: RawBodyRequest&lt;IncomingMessage&gt;,
    <span class="hljs-meta">@Res</span>() res: Response,
  ) {}
}
</code></pre>
<p>All the Shopify webhooks will hit this endpoint and this controller will handle it. We have to protect this endpoint from random requests. We can add a guard before our controller to protect the endpoint. Leave the controller empty for now.</p>
<p>Let's create the guard that will protect the controller.</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> VerifyWebhook <span class="hljs-keyword">implements</span> CanActivate {
  canActivate(context: ExecutionContext): <span class="hljs-built_in">boolean</span> | <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">boolean</span>&gt; {

  }
}
</code></pre>
<p>Shopify will send a hash with every webhook request that we can use to verify the request. Shopify creates this hash with the request's raw body and Shopify API secret. We have to generate a hash and compare it with the webhook request's hash. Let's see how to do it.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {
  BadRequestException,
  CanActivate,
  ExecutionContext,
  Injectable,
  InternalServerErrorException,
  RawBodyRequest,
  UnauthorizedException,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> Shopify <span class="hljs-keyword">from</span> <span class="hljs-string">'@shopify/shopify-api'</span>;
<span class="hljs-keyword">import</span> { createHmac, timingSafeEqual } <span class="hljs-keyword">from</span> <span class="hljs-string">'crypto'</span>;
<span class="hljs-keyword">import</span> { IncomingMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">'http'</span>;

<span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> VerifyWebhook <span class="hljs-keyword">implements</span> CanActivate {
  canActivate(context: ExecutionContext): <span class="hljs-built_in">boolean</span> | <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">boolean</span>&gt; {
    <span class="hljs-keyword">const</span> req = context
      .switchToHttp()
      .getRequest&lt;RawBodyRequest&lt;IncomingMessage&gt;&gt;();

    <span class="hljs-keyword">const</span> hmacHeader = req.headers[<span class="hljs-string">'X-Shopify-Hmac-Sha256'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>;

    <span class="hljs-keyword">if</span> (!hmacHeader || <span class="hljs-keyword">typeof</span> hmacHeader !== <span class="hljs-string">'string'</span>) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnauthorizedException(
        <span class="hljs-string">`Not Authorized`</span>,
      );
    }  

    <span class="hljs-keyword">if</span> (!req.rawBody) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InternalServerErrorException(
        <span class="hljs-string">`Raw body not found`</span>,
      );
    }

    <span class="hljs-keyword">const</span> generatedHash = createHmac(<span class="hljs-string">'sha256'</span>, <span class="hljs-string">'shopify api secret'</span>)
      .update(req.rawBody)
      .digest(<span class="hljs-string">'base64'</span>);

    <span class="hljs-keyword">const</span> generatedHashBuffer = Buffer.from(generatedHash);
    <span class="hljs-keyword">const</span> hmacBuffer = Buffer.from(hmacHeader);

    <span class="hljs-keyword">if</span> (generatedHashBuffer.length !== hmacBuffer.length) {
       <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnauthorizedException(
        <span class="hljs-string">`Not Authorized`</span>,
      );
    }

    <span class="hljs-keyword">if</span> (!timingSafeEqual(generatedHashBuffer, hmacBuffer)) {
       <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnauthorizedException(
        <span class="hljs-string">`Not Authorized`</span>,
      );
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
  }
}
</code></pre>
<p>Here we are using the request object from context. We are first checking whether the current request has a hash or not. If the request doesn't have a hash then we reject it. Then we are generating a hash from <code>createHmac</code> function by the crypto library. We are using both Shopify API secret and raw body to generate the hash. Finally, we just compare them with the <code>timingSafeEqual</code> function.</p>
<blockquote>
<p>To learn more about <code>createHmac</code> and <code>timingSafeEqual</code> you can check out nodejs crypto's documentation. (<a target="_blank" href="https://nodejs.org/api/crypto.html">Link</a>)</p>
</blockquote>
<p>Let's use this guard in Shopify webhook controller.</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@Controller</span>(<span class="hljs-string">'/api/webhook'</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookController {
  <span class="hljs-meta">@UseGuards</span>(VerifyWebhook)
  <span class="hljs-meta">@Post</span>(<span class="hljs-string">'/'</span>)
  <span class="hljs-keyword">async</span> processWebhook(
    <span class="hljs-meta">@Req</span>() req: RawBodyRequest&lt;IncomingMessage&gt;,
    <span class="hljs-meta">@Res</span>() res: Response,
  ) {}
}
</code></pre>
<p>Now we have to handle which service method will run depending on the webhook topic. So let's see how to do it</p>
<pre><code class="lang-typescript">
<span class="hljs-meta">@Controller</span>(<span class="hljs-string">'/api/webhook'</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookController {
  <span class="hljs-meta">@UseGuards</span>(VerifyWebhook)
  <span class="hljs-meta">@Post</span>(<span class="hljs-string">'/'</span>)
  <span class="hljs-keyword">async</span> processWebhook(
    <span class="hljs-meta">@Req</span>() req: RawBodyRequest&lt;IncomingMessage&gt;,
    <span class="hljs-meta">@Res</span>() res: Response,
  ) {
    <span class="hljs-keyword">const</span> topic = req.headers[<span class="hljs-string">'X-Shopify-Topic'</span>];

    <span class="hljs-keyword">if</span> (!req.rawBody) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InternalServerErrorException(
        <span class="hljs-string">'raw body not found'</span>,
      );
    }

    <span class="hljs-keyword">const</span> domain = req.headers[<span class="hljs-string">'X-Shopify-Shop-Domain'</span>];

    <span class="hljs-keyword">if</span> (!domain || !topic) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> BadRequestException(
        <span class="hljs-string">`something went wrong`</span>,
      );
    }

    <span class="hljs-keyword">const</span> graphqlTopic = topic.toUpperCase().replace(<span class="hljs-regexp">/\//g</span>, <span class="hljs-string">'_'</span>);
    <span class="hljs-keyword">const</span> webhookEntry = Shopify.Webhooks.Registry.getHandler(graphqlTopic);

    <span class="hljs-keyword">if</span> (!webhookEntry) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NotFoundException(
        <span class="hljs-string">`No handler found`</span>,
      );
    }

    <span class="hljs-keyword">await</span> webhookEntry.webhookHandler(
      graphqlTopic,
      domain <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
      rawBody.toString(),
    );

    res.status(<span class="hljs-number">200</span>).send(<span class="hljs-string">'completed'</span>);

  }
}
</code></pre>
<p>Here we are checking whether we have registered a handler to handle this topic. If we found a handler then we pass some arguments to it and call it. The arguments are the current topic, the shop's URL and data in raw body format. We can access these arguments in the handler.</p>
<p>Let's create the <code>processUninstall</code> handler in Shopify webhook service.</p>
<p><code>src/shopify-webhook/shopify-webhook.service.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> Shopify <span class="hljs-keyword">from</span> <span class="hljs-string">'@shopify/shopify-api'</span>;

<span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> ShopifyWebhookService {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {}
  <span class="hljs-keyword">async</span> processUninstall(topic: <span class="hljs-built_in">string</span>, shop: <span class="hljs-built_in">string</span>, body: <span class="hljs-built_in">any</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'process uninstall function'</span>);
  }
}
</code></pre>
<p>The <code>processUninstall</code> method will have three parameters. These are the same parameters that we passed to the handler above.</p>
<p>With that our custom webhook processing is complete.</p>
]]></content:encoded></item><item><title><![CDATA[Neovim configuration from scratch to LSP]]></title><description><![CDATA[I have been using vim motions with vscode for about a year now. Recently I switched to Neovim for my editor. This is my configuration for Neovim. I also use Tmux with Neovim but I will cover it in another blog.
This is what Neovim looks like without ...]]></description><link>https://blog.pulkitgangwar.com/neovim-configuration-from-scratch-to-lsp</link><guid isPermaLink="true">https://blog.pulkitgangwar.com/neovim-configuration-from-scratch-to-lsp</guid><category><![CDATA[neovim]]></category><category><![CDATA[vim]]></category><category><![CDATA[Lua]]></category><category><![CDATA[nvim, ]]></category><dc:creator><![CDATA[Pulkit Gangwar]]></dc:creator><pubDate>Fri, 20 Jan 2023 10:13:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673950801287/9f483ce5-5864-46df-9d05-81b1581339e0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been using vim motions with vscode for about a year now. Recently I switched to Neovim for my editor. This is my configuration for Neovim. I also use Tmux with Neovim but I will cover it in another blog.</p>
<p>This is what Neovim looks like without configuration.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673863492212/5d0f727c-d2b2-4bd1-9d52-bb6f9c026bf7.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039874616/3a923c2f-9574-4010-85b7-7d71dfeb7728.gif" alt class="image--center mx-auto" /></p>
<p>We will convert it to this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673863533545/246048ac-1a34-44aa-a480-91565749a89a.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039896133/d6931d07-364d-402a-b029-9c07bafaa649.gif" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Configuration is available on my GitHub repository. (<a target="_blank" href="https://github.com/pulkitgangwar/nvim-config">Link</a>)</p>
</blockquote>
<p>Let's first install Neovim on our system. We will be using Lua for configuration.</p>
<pre><code class="lang-bash">brew install neovim
</code></pre>
<blockquote>
<p>Brew installation only works for mac devices. If you have any other device you can refer to Neovim documentation. (<a target="_blank" href="https://github.com/neovim/neovim/wiki/Installing-Neovim">Link</a>)</p>
</blockquote>
<p>Now we have to create a <code>nvim</code> directory inside <code>.config</code> directory. The path will be <code>~/.config/nvim</code> . When Neovim executes, it will find configuration files in this folder. Neovim will search for <code>init.lua</code> file.</p>
<blockquote>
<p>For Windows users, the configuration location will be <code>C:\User\username\AppData\Local\nvim</code> .</p>
</blockquote>
<p>The file structure will be -</p>
<pre><code class="lang-plaintext">.
└── .config/
    └── nvim/
        ├── init.lua
        └── lua/
            └── pulkitgangwar/
                ├── core/
                │   ├── options.lua
                │   ├── remap.lua
                │   └── colorscheme.lua
                ├── plugins/
                │   └── //all-plugins-setup-files
                └── plugins-setup.lua
</code></pre>
<p>Let's start with adding remaps and options for Neovim.</p>
<p><code>/lua/pulkitgangwar/core/options.lua</code></p>
<pre><code class="lang-javascript">vim.opt.nu = <span class="hljs-literal">true</span>
vim.opt.relativenumber = <span class="hljs-literal">true</span>

vim.opt.tabstop = <span class="hljs-number">4</span>
vim.opt.softtabstop = <span class="hljs-number">4</span>
vim.opt.shiftwidth = <span class="hljs-number">4</span>
vim.opt.expandtab = <span class="hljs-literal">true</span>

vim.opt.smartindent = <span class="hljs-literal">true</span>

vim.opt.hlsearch = <span class="hljs-literal">false</span>
vim.opt.incsearch = <span class="hljs-literal">true</span>

vim.opt.termguicolors = <span class="hljs-literal">true</span>

vim.opt.scrolloff = <span class="hljs-number">8</span>
</code></pre>
<p><code>/lua/pulkitgangwar/core/remap.lua</code></p>
<pre><code class="lang-javascript">vim.g.mapleader = <span class="hljs-string">" "</span>

vim.keymap.set(<span class="hljs-string">"i"</span>, <span class="hljs-string">"kj"</span>, <span class="hljs-string">"&lt;Esc&gt;"</span>)

vim.keymap.set(<span class="hljs-string">"v"</span>, <span class="hljs-string">"J"</span>, <span class="hljs-string">":m '&gt;+1&lt;CR&gt;gv=gv"</span>)
vim.keymap.set(<span class="hljs-string">"v"</span>, <span class="hljs-string">"K"</span>, <span class="hljs-string">":m '&lt;-2&lt;CR&gt;gv=gv"</span>)


vim.keymap.set({ <span class="hljs-string">"n"</span>, <span class="hljs-string">"v"</span> }, <span class="hljs-string">"&lt;leader&gt;y"</span>, [[<span class="hljs-string">"+y]])
vim.keymap.set("</span>n<span class="hljs-string">", "</span>&lt;leader&gt;Y<span class="hljs-string">", [["</span>+Y]])

vim.keymap.set(<span class="hljs-string">"x"</span>, <span class="hljs-string">"&lt;leader&gt;p"</span>, [[<span class="hljs-string">"_dP]])


vim.keymap.set("</span>n<span class="hljs-string">", "</span>Q<span class="hljs-string">", "</span>&lt;nop&gt;<span class="hljs-string">")

vim.keymap.set("</span>n<span class="hljs-string">", "</span>&lt;leader&gt;f<span class="hljs-string">", vim.lsp.buf.format)</span>
</code></pre>
<p>After adding options and remaps we have to import these files in our <code>init.lua</code> file.</p>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039654478/f53dc53f-111e-4ad9-b142-8da90242a811.gif" alt class="image--center mx-auto" /></p>
<p>The first configuration we will do is for the plugin manager. We will use packer as our plugin manager.</p>
<p><code>/lua/pulkitgangwar/plugins-setup.lua</code> (Packer installation)</p>
<pre><code class="lang-javascript">vim.cmd [[packadd packer.nvim]]

<span class="hljs-keyword">return</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'packer'</span>).startup(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">use</span>)
    <span class="hljs-title">use</span> '<span class="hljs-title">wbthomason</span>/<span class="hljs-title">packer</span>.<span class="hljs-title">nvim</span>'

<span class="hljs-title">end</span>)</span>
</code></pre>
<p>Save the file with <code>:w</code> and reopen <code>plugins-setup.lua</code> file. Execute <code>:PackerSync</code> to sync all the packages inside the startup function.</p>
<p>The following script will install some plugins for different use cases.</p>
<pre><code class="lang-javascript">vim.cmd [[packadd packer.nvim]]

<span class="hljs-keyword">return</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'packer'</span>).startup(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">use</span>)
    <span class="hljs-title">use</span> '<span class="hljs-title">wbthomason</span>/<span class="hljs-title">packer</span>.<span class="hljs-title">nvim</span>'

     -- <span class="hljs-title">file</span> <span class="hljs-title">explorer</span>
    <span class="hljs-title">use</span> '<span class="hljs-title">nvim</span>-<span class="hljs-title">tree</span>/<span class="hljs-title">nvim</span>-<span class="hljs-title">tree</span>.<span class="hljs-title">lua</span>'
    <span class="hljs-title">use</span> '<span class="hljs-title">nvim</span>-<span class="hljs-title">tree</span>/<span class="hljs-title">nvim</span>-<span class="hljs-title">web</span>-<span class="hljs-title">devicons</span>'

    -- <span class="hljs-title">fuzzy</span> <span class="hljs-title">finding</span>
    <span class="hljs-title">use</span> </span>{
        <span class="hljs-string">'nvim-telescope/telescope.nvim'</span>, tag = <span class="hljs-string">'0.1.0'</span>,
        requires = { { <span class="hljs-string">'nvim-lua/plenary.nvim'</span> } }
    }
    -- color scheme
    use <span class="hljs-string">'folke/tokyonight.nvim'</span>

    -- treesitter
    use({
        <span class="hljs-string">"nvim-treesitter/nvim-treesitter"</span>,
        run = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)
            <span class="hljs-title">require</span>(<span class="hljs-params"><span class="hljs-string">"nvim-treesitter.install"</span></span>).<span class="hljs-title">update</span>(<span class="hljs-params">{ with_sync =    true }</span>)
        <span class="hljs-title">end</span>,
    })

    -- <span class="hljs-title">movement</span> <span class="hljs-title">between</span> <span class="hljs-title">splits</span>
    <span class="hljs-title">use</span> "<span class="hljs-title">christoomey</span>/<span class="hljs-title">vim</span>-<span class="hljs-title">tmux</span>-<span class="hljs-title">navigator</span>"

    -- <span class="hljs-title">buffer</span> <span class="hljs-title">line</span> <span class="hljs-title">support</span>
    <span class="hljs-title">use</span> "<span class="hljs-title">akinsho</span>/<span class="hljs-title">bufferline</span>.<span class="hljs-title">nvim</span>"

    -- <span class="hljs-title">auto</span> <span class="hljs-title">pairs</span>
    <span class="hljs-title">use</span> "<span class="hljs-title">windwp</span>/<span class="hljs-title">nvim</span>-<span class="hljs-title">autopairs</span>"

    -- <span class="hljs-title">bottom</span> <span class="hljs-title">informative</span> <span class="hljs-title">line</span>
    <span class="hljs-title">use</span> '<span class="hljs-title">nvim</span>-<span class="hljs-title">lualine</span>/<span class="hljs-title">lualine</span>.<span class="hljs-title">nvim</span>'

<span class="hljs-title">end</span>)</span>
</code></pre>
<p><code>nvim-tree</code> : It is a file explorer. Neovim has <code>netrw</code> but we will remove it to use <code>nvim-tree</code></p>
<p><code>nvim-web-devicons</code> : This plugin will show different icons. Remember to have patched fonts installed for your terminal.</p>
<p><code>telescope</code> : This plugin helps with fuzzy findings.</p>
<p><code>tokyonight</code> : This is the colour scheme for our editor.</p>
<p><code>treesitter</code> : It helps in syntax highlighting.</p>
<p><code>vim-tmux-navigator</code> : It helps in moving between splits.</p>
<p><code>bufferline</code> : It helps to show all the current opened buffers.</p>
<p><code>nvim-autopairs</code> : It helps in auto-closing brackets, quotations etc.</p>
<p><code>lualine</code> : It helps to show the bottom activity bar, like vs code.</p>
<p>Now we have to set up these plugins. To set up plugins, we will follow some steps. The first step is to install the plugin. After that, we have to create a file for that plugin.</p>
<p><code>/lua/pulkitgangwar/plugins/nvim-tree.lua</code> (nvim-tree setup)</p>
<pre><code class="lang-javascript">vim.g.loaded_netrw = <span class="hljs-number">1</span>
vim.g.loaded_netrwPlugin = <span class="hljs-number">1</span>

vim.opt.termguicolors = <span class="hljs-literal">true</span>

<span class="hljs-built_in">require</span>(<span class="hljs-string">"nvim-tree"</span>).setup({
    git = {
    enable = <span class="hljs-literal">true</span>,
    ignore = <span class="hljs-literal">false</span>,
    timeout = <span class="hljs-number">500</span>,
  },
})

vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"&lt;leader&gt;e"</span>, vim.cmd.NvimTreeToggle)
</code></pre>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039687685/364b88b4-2805-4c3f-ab82-1f3c07b4f876.gif" alt class="image--center mx-auto" /></p>
<p><code>/lua/pulkitgangwar/core/colorscheme.lua</code></p>
<pre><code class="lang-javascript">vim.o.background = <span class="hljs-string">"dark"</span> 

vim.cmd([[colorscheme tokyonight]])
</code></pre>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.colorscheme"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039716422/b211dd8d-fa04-4730-a218-49742d11554e.gif" alt class="image--center mx-auto" /></p>
<p><code>/lua/pulkitgangwar/plugins/telescope</code></p>
<pre><code class="lang-javascript">local builtin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'telescope.builtin'</span>)

vim.keymap.set(<span class="hljs-string">'n'</span>, <span class="hljs-string">'&lt;leader&gt;ff'</span>, builtin.find_files, {})
vim.keymap.set(<span class="hljs-string">'n'</span>, <span class="hljs-string">'&lt;leader&gt;fg'</span>, builtin.git_files, {})
vim.keymap.set(<span class="hljs-string">'n'</span>,<span class="hljs-string">'&lt;leader&gt;gd'</span>, builtin.diagnostics, {})
</code></pre>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.colorscheme"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.telescope"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039747838/9253ad21-63d8-44ab-ae38-b41c01a93a82.gif" alt class="image--center mx-auto" /></p>
<p><code>/lua/pulkitgangwar/plugins/nvim-treesitter.lua</code></p>
<pre><code class="lang-javascript">local treesitter_status,treesitter = pcall(<span class="hljs-built_in">require</span>,<span class="hljs-string">"nvim-treesitter.config"</span>)

<span class="hljs-keyword">if</span> not treesitter_status then print(<span class="hljs-string">"treesitter not found"</span>) <span class="hljs-keyword">return</span> end

treesitter.setup({
  ensure_installed = { <span class="hljs-string">"help"</span>, <span class="hljs-string">"javascript"</span>, <span class="hljs-string">"typescript"</span>, <span class="hljs-string">"c"</span>, <span class="hljs-string">"lua"</span>, <span class="hljs-string">"rust"</span> },
  sync_install = <span class="hljs-literal">false</span>,
  auto_install = <span class="hljs-literal">true</span>,
  highlight = {
    enable = <span class="hljs-literal">true</span>,
    additional_vim_regex_highlighting = <span class="hljs-literal">false</span>,
  },
})
</code></pre>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.colorscheme"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.telescope"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-treesitter"</span>)
</code></pre>
<p><code>/lua/pulkitgangwar/plugins/bufferline.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'bufferline'</span>).setup({
  options = {
    offsets = {
        {
            filetype = <span class="hljs-string">"NvimTree"</span>,
            text = <span class="hljs-string">"File Explorer"</span>,
            highlight = <span class="hljs-string">"Directory"</span>,
            separator = <span class="hljs-literal">true</span> 
        },
    }
  }
})

-- move between buffers or tabs
vim.keymap.set(<span class="hljs-string">"n"</span>,<span class="hljs-string">"&lt;S-l&gt;"</span>,vim.cmd.bnext)
vim.keymap.set(<span class="hljs-string">"n"</span>,<span class="hljs-string">"&lt;S-h&gt;"</span>,vim.cmd.bprev)
</code></pre>
<p><code>/lua/pulkitgangwar/plugins/nvim-autopairs.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"nvim-autopairs"</span>).setup()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039776312/47af102c-3ef1-4b1d-974c-ec4c03100c36.gif" alt class="image--center mx-auto" /></p>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.colorscheme"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.telescope"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-treesitter"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-autopairs"</span>)
</code></pre>
<p><code>/lua/pulkitgangwar/plugins/lualine.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"lualine"</span>).setup({
    options = {
        theme = <span class="hljs-string">"tokyonight"</span>,
    },
})
</code></pre>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.colorscheme"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.telescope"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-treesitter"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-autopairs"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.lualine"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039806859/38f61a3f-0d7b-4c06-9886-01e02f9454eb.gif" alt class="image--center mx-auto" /></p>
<p>Now we will work on LSP and suggestions. It was very difficult for me to set up LSP so I used a great plugin that abstracts most things. The name of the plugin is <code>lsp-zero</code> .</p>
<p><code>/lua/pulkitgangwar/plugin-setup.lua</code></p>
<pre><code class="lang-javascript">vim.cmd [[packadd packer.nvim]]

<span class="hljs-keyword">return</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'packer'</span>).startup(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">use</span>)
    <span class="hljs-title">use</span> '<span class="hljs-title">wbthomason</span>/<span class="hljs-title">packer</span>.<span class="hljs-title">nvim</span>'

     -- <span class="hljs-title">file</span> <span class="hljs-title">explorer</span>
    <span class="hljs-title">use</span> '<span class="hljs-title">nvim</span>-<span class="hljs-title">tree</span>/<span class="hljs-title">nvim</span>-<span class="hljs-title">tree</span>.<span class="hljs-title">lua</span>'
    <span class="hljs-title">use</span> '<span class="hljs-title">nvim</span>-<span class="hljs-title">tree</span>/<span class="hljs-title">nvim</span>-<span class="hljs-title">web</span>-<span class="hljs-title">devicons</span>'

    -- <span class="hljs-title">fuzzy</span> <span class="hljs-title">finding</span>
    <span class="hljs-title">use</span> </span>{
        <span class="hljs-string">'nvim-telescope/telescope.nvim'</span>, tag = <span class="hljs-string">'0.1.0'</span>,
        requires = { { <span class="hljs-string">'nvim-lua/plenary.nvim'</span> } }
    }
    -- color scheme
    use <span class="hljs-string">'folke/tokyonight.nvim'</span>

    -- treesitter
    use({
        <span class="hljs-string">"nvim-treesitter/nvim-treesitter"</span>,
        run = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)
            <span class="hljs-title">require</span>(<span class="hljs-params"><span class="hljs-string">"nvim-treesitter.install"</span></span>).<span class="hljs-title">update</span>(<span class="hljs-params">{ with_sync =    true }</span>)
        <span class="hljs-title">end</span>,
    })

    -- <span class="hljs-title">movement</span> <span class="hljs-title">between</span> <span class="hljs-title">splits</span>
    <span class="hljs-title">use</span> "<span class="hljs-title">christoomey</span>/<span class="hljs-title">vim</span>-<span class="hljs-title">tmux</span>-<span class="hljs-title">navigator</span>"

    -- <span class="hljs-title">buffer</span> <span class="hljs-title">line</span> <span class="hljs-title">support</span>
    <span class="hljs-title">use</span> "<span class="hljs-title">akinsho</span>/<span class="hljs-title">bufferline</span>.<span class="hljs-title">nvim</span>"

    -- <span class="hljs-title">auto</span> <span class="hljs-title">pairs</span>
    <span class="hljs-title">use</span> "<span class="hljs-title">windwp</span>/<span class="hljs-title">nvim</span>-<span class="hljs-title">autopairs</span>"

    -- <span class="hljs-title">bottom</span> <span class="hljs-title">informative</span> <span class="hljs-title">line</span>
    <span class="hljs-title">use</span> '<span class="hljs-title">nvim</span>-<span class="hljs-title">lualine</span>/<span class="hljs-title">lualine</span>.<span class="hljs-title">nvim</span>'


    -- <span class="hljs-title">lsp</span>, <span class="hljs-title">snippets</span>, <span class="hljs-title">autocomplete</span>
    <span class="hljs-title">use</span> </span>{
        <span class="hljs-string">'VonHeikemen/lsp-zero.nvim'</span>,
        requires = {
            -- LSP Support
            { <span class="hljs-string">'neovim/nvim-lspconfig'</span> },
            { <span class="hljs-string">'williamboman/mason.nvim'</span> },
            { <span class="hljs-string">'williamboman/mason-lspconfig.nvim'</span> },

            -- Autocompletion
            { <span class="hljs-string">'hrsh7th/nvim-cmp'</span> },
            { <span class="hljs-string">'hrsh7th/cmp-buffer'</span> },
            { <span class="hljs-string">'hrsh7th/cmp-path'</span> },
            { <span class="hljs-string">'saadparwaiz1/cmp_luasnip'</span> },
            { <span class="hljs-string">'hrsh7th/cmp-nvim-lsp'</span> },
            { <span class="hljs-string">'hrsh7th/cmp-nvim-lua'</span> },

            -- Snippets
            { <span class="hljs-string">'L3MON4D3/LuaSnip'</span> },
            { <span class="hljs-string">'rafamadriz/friendly-snippets'</span> },
        }
    }
end)
</code></pre>
<p><code>/lua/pulkitgangwar/plugins/lsp.lua</code></p>
<pre><code class="lang-javascript">local lsp = <span class="hljs-built_in">require</span>(<span class="hljs-string">"lsp-zero"</span>)

lsp.preset(<span class="hljs-string">"recommended"</span>)

lsp.ensure_installed({
  <span class="hljs-string">'tsserver'</span>,
  <span class="hljs-string">'eslint'</span>,
  <span class="hljs-string">'sumneko_lua'</span>,
  <span class="hljs-string">'rust_analyzer'</span>,
})

-- Fix Undefined <span class="hljs-built_in">global</span> <span class="hljs-string">'vim'</span>
lsp.configure(<span class="hljs-string">'sumneko_lua'</span>, {
    settings = {
        Lua = {
            diagnostics = {
                globals = { <span class="hljs-string">'vim'</span> }
            }
        }
    }
})


local cmp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cmp'</span>)
local cmp_select = {behavior = cmp.SelectBehavior.Select}
local cmp_mappings = lsp.defaults.cmp_mappings({
  [<span class="hljs-string">'&lt;C-p&gt;'</span>] = cmp.mapping.select_prev_item(cmp_select),
  [<span class="hljs-string">'&lt;C-n&gt;'</span>] = cmp.mapping.select_next_item(cmp_select),
  [<span class="hljs-string">'&lt;C-y&gt;'</span>] = cmp.mapping.confirm({ select = <span class="hljs-literal">true</span> }),
  [<span class="hljs-string">"&lt;C-Space&gt;"</span>] = cmp.mapping.complete(),
})

-- disable completion <span class="hljs-keyword">with</span> tab
-- <span class="hljs-built_in">this</span> helps <span class="hljs-keyword">with</span> copilot setup
cmp_mappings[<span class="hljs-string">'&lt;Tab&gt;'</span>] = nil
cmp_mappings[<span class="hljs-string">'&lt;S-Tab&gt;'</span>] = nil

lsp.setup_nvim_cmp({
  mapping = cmp_mappings
})

lsp.set_preferences({
    suggest_lsp_servers = <span class="hljs-literal">false</span>,
    sign_icons = {
        error = <span class="hljs-string">'E'</span>,
        warn = <span class="hljs-string">'W'</span>,
        hint = <span class="hljs-string">'H'</span>,
        info = <span class="hljs-string">'I'</span>
    }
})

lsp.on_attach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">client, bufnr</span>)
  <span class="hljs-title">local</span> <span class="hljs-title">opts</span> = </span>{buffer = bufnr, remap = <span class="hljs-literal">false</span>}

  <span class="hljs-keyword">if</span> client.name == <span class="hljs-string">"eslint"</span> then
      vim.cmd.LspStop(<span class="hljs-string">'eslint'</span>)
      <span class="hljs-keyword">return</span>
  end

  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"gd"</span>, vim.lsp.buf.definition, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"K"</span>, vim.lsp.buf.hover, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"&lt;leader&gt;vws"</span>, vim.lsp.buf.workspace_symbol, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"&lt;leader&gt;vd"</span>, vim.diagnostic.open_float, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"[d"</span>, vim.diagnostic.goto_next, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"]d"</span>, vim.diagnostic.goto_prev, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"&lt;leader&gt;vca"</span>, vim.lsp.buf.code_action, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"&lt;leader&gt;vrr"</span>, vim.lsp.buf.references, opts)
  vim.keymap.set(<span class="hljs-string">"n"</span>, <span class="hljs-string">"&lt;leader&gt;vrn"</span>, vim.lsp.buf.rename, opts)
  vim.keymap.set(<span class="hljs-string">"i"</span>, <span class="hljs-string">"&lt;C-h&gt;"</span>, vim.lsp.buf.signature_help, opts)
end)

lsp.setup()

vim.diagnostic.config({
    virtual_text = <span class="hljs-literal">true</span>,
})
</code></pre>
<p><code>~/.config/nvim/init.lua</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.options"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.remap"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.core.colorscheme"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-tree"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.telescope"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-treesitter"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.nvim-autopairs"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.lualine"</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">"pulkitgangwar.plugins.lsp"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674039824482/3071aa46-01c8-4973-9665-e203d55a3bca.gif" alt class="image--center mx-auto" /></p>
<blockquote>
<p>You can read more about every plugin from their repositories.</p>
<p><a target="_blank" href="https://github.com/nvim-tree/nvim-tree.lua">nvim-tree</a>, <a target="_blank" href="https://github.com/nvim-telescope/telescope.nvim">telescope</a>, <a target="_blank" href="https://github.com/folke/tokyonight.nvim">tokyonight</a>, <a target="_blank" href="https://github.com/nvim-treesitter/nvim-treesitter">treesitter</a>, <a target="_blank" href="https://github.com/christoomey/vim-tmux-navigator">vim-tmux-navigator</a>, <a target="_blank" href="https://github.com/akinsho/bufferline.nvim">bufferline</a>, <a target="_blank" href="https://github.com/windwp/nvim-autopairs">nvim-autopairs</a>, <a target="_blank" href="https://github.com/nvim-lualine/lualine.nvim">lualine</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[How to use Redux toolkit with React JS]]></title><description><![CDATA[Redux is a state management library which offers a lot of features. The downside is it adds up a lot of boilerplate code and complexity to applications. The Redux toolkit solves these problems for us.
Redux toolkit reduces the complexities and the co...]]></description><link>https://blog.pulkitgangwar.com/how-to-use-redux-toolkit-with-react-js</link><guid isPermaLink="true">https://blog.pulkitgangwar.com/how-to-use-redux-toolkit-with-react-js</guid><category><![CDATA[React]]></category><category><![CDATA[Redux]]></category><category><![CDATA[redux-toolkit]]></category><category><![CDATA[react redux]]></category><category><![CDATA[redux-thunk]]></category><dc:creator><![CDATA[Pulkit Gangwar]]></dc:creator><pubDate>Thu, 15 Dec 2022 17:16:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671011258015/ah7F2weJe.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Redux is a state management library which offers a lot of features. The downside is it adds up a lot of boilerplate code and complexity to applications. The Redux toolkit solves these problems for us.</p>
<p>Redux toolkit reduces the complexities and the code we write to setup up redux. The Redux toolkit has redux-thunk capabilities built-in. We can divide our state into smaller states so that they are easily manageable. Redux toolkit makes it very easy to work with redux in react applications.</p>
<p>So let's see how we can use the redux toolkit with react. To understand how the redux toolkit works we will be building a small todo application.</p>
<blockquote>
<p>if you are setting up a react project from scratch then you can directly use the create-react-app template for redux.</p>
<p>All the code is available on my GitHub repository (<a target="_blank" href="https://github.com/pulkitgangwar/react-with-redux-toolkit">Link</a>).</p>
</blockquote>
<p>First, let's start with initializing our project with create react app</p>
<pre><code class="lang-bash">yarn create react-app redux-with-react
</code></pre>
<p>Then we need to install some libraries</p>
<pre><code class="lang-bash">yarn add react-redux @reduxjs/toolkit
</code></pre>
<p>We don't need to install the redux library, <code>@reduxjs/toolkit</code> has redux as its dependency. <code>react-redux</code> is the library that provides components and hooks to work with redux.</p>
<p>Now we create a store in which all our mini stores (slices) will reside. For that let's create a store folder and in that create an index.js file</p>
<p><code>src/store/index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { configureStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> configureStore({
  <span class="hljs-attr">reducer</span>: {},
});
</code></pre>
<p>We will import the configure store function from the redux toolkit. This function combines our multiple small stores into a single large store. Let's leave the reducer empty for now.</p>
<p>After creating the store we need to inform redux to use this store. For that, we will give the store to the Provider component. This provider component will live at the root level of react so that every child can access the store.</p>
<p><code>src/index.js</code></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> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> { Provider <span class="hljs-keyword">as</span> ReduxStoreProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App"</span>;
<span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">"./store"</span>;

<span class="hljs-keyword">const</span> root = createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>));
root.render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ReduxStoreProvider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ReduxStoreProvider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>The react-redux library exports the provider component. Here we are importing the store we created earlier and passing it as a prop to the provider. Now every child can access the store regardless of how nested the child is.</p>
<p>Now let's create our first slice or mini-store. Create a file named todos.js inside the store folder. Redux toolkit calls mini-store as slices. To create a slice redux toolkit export a function named createSlice.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">const</span> todosSlice = createSlice({});
</code></pre>
<p>The <code>createSlice</code> function takes in an object as a parameter. It can be provided many properties to configure the store.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">const</span> todosSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"todos"</span>,
  <span class="hljs-attr">initialState</span>: {
    <span class="hljs-attr">todos</span>: [],
  },
  <span class="hljs-attr">reducers</span>: {

  },
});
</code></pre>
<p>name: name is used to name the slice</p>
<p>initialState: initial state is used to provide state initial state value to our slice, we only have a todos array right now but we can have multiple properties here.</p>
<p>reducers: we can define methods in reducer to modify state values. Let's leave it empty for now.</p>
<p>Finally, we have completed our to-dos slice. At this time our main store doesn't know about the todos slice. To include the todos slice in the main store we have to export the reducer of the todos slice. The main store will use the exported todos slice reducer in store/index.js.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">const</span> todosSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"todos"</span>,
  <span class="hljs-attr">initialState</span>: {
    <span class="hljs-attr">todos</span>: [],
  },
  <span class="hljs-attr">reducers</span>: {

  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> todosSlice.reducer;
</code></pre>
<p>We have exported the reducer from the todos slice.</p>
<p><code>src/store/index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { configureStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;
<span class="hljs-keyword">import</span> todosReducer <span class="hljs-keyword">from</span> <span class="hljs-string">"./todos"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> configureStore({
  <span class="hljs-attr">reducer</span>: {
    <span class="hljs-attr">todos</span>: todosReducer,
  },
});
</code></pre>
<p>First, we have to provide the name of the slice and then the reducer.</p>
<p>Then we have to create a component which will render all todos from the todos slice. Before that let's add some random todos in our initial state so that we have something to render.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">const</span> todosSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"todos"</span>,
  <span class="hljs-attr">initialState</span>: {
    <span class="hljs-attr">todos</span>: [
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"1"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"first todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"2"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"second todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
    ],
  },
  <span class="hljs-attr">reducers</span>: {

  },
});
</code></pre>
<p>Now let's create <code>Todos.jsx</code> component</p>
<p><code>src/components/Todos.jsx</code></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">const</span> Todos = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Todos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Todos;
</code></pre>
<p>To access our todos state we have to use hooks provided by the react-redux library. Hooks make it very easy to grab data from the redux store.</p>
<p><code>src/components/Todos.jsx</code></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> { useSelector } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;

<span class="hljs-keyword">const</span> Todos = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todos } = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.todos);

  <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>&gt;</span>Todos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {todos.map((todo) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Todos;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671089923454/T_b7X5BGJ.png" alt class="image--center mx-auto" /></p>
<p>The <code>react-redux</code> library export <code>useSelector</code> hook. Hook takes a callback function which gets the store's state. We can either return the whole state or the part of the state we want, so here we are only returning todos.</p>
<p>In the above code, we mention <code>state.todos</code>. We can do this because of the property name we provided to the main store object for the todos slice reducer.</p>
<p><code>src/store/index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { configureStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;
<span class="hljs-keyword">import</span> todosReducer <span class="hljs-keyword">from</span> <span class="hljs-string">"./todos"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> configureStore({
  <span class="hljs-attr">reducer</span>: {
    <span class="hljs-attr">todos</span>: todosReducer,
  },
});
</code></pre>
<p>Now let's see how to change our state. We will add the remove functionality to our app. Create a remove button and attach a click event listener for every todo element.</p>
<p><code>src/components/Todos.jsx</code></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> { useSelector } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;

<span class="hljs-keyword">const</span> Todos = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todos } = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.todos);

  <span class="hljs-keyword">const</span> handleRemove = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {};

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Todos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {todos.map((todo) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
              handleRemove(todo.id);
            }}
          &gt;
            Remove
          <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">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Todos;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671089982488/9Bf0z27aZ.png" alt class="image--center mx-auto" /></p>
<p>Now we have empty <code>handleRemove</code> for every to-do registered with the to-do id.</p>
<p>Let's create a reducer function to remove todo from the store's state.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">const</span> todosSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"todos"</span>,
  <span class="hljs-attr">initialState</span>: {
    <span class="hljs-attr">todos</span>: [
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"1"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"first todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"2"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"second todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
    ],
  },
  <span class="hljs-attr">reducers</span>: {
    remove(state, action) {
      state.todos = state.todos.filter(<span class="hljs-function">(<span class="hljs-params">todo</span>) =&gt;</span> todo.id !== action.payload);
    },
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> todosSlice.reducer;
</code></pre>
<p>Methods inside the reducers object will have two parameters. First will be the current state and the second will be the action with a payload. When we call this reducer we can provide arguments as payload with it.</p>
<p>To call this method we need action for the remove method so that we can dispatch the action. Slices will automatically create actions for us that we can export and use.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">const</span> todosSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"todos"</span>,
  <span class="hljs-attr">initialState</span>: {
    <span class="hljs-attr">todos</span>: [
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"1"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"first todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"2"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"second todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
    ],
  },
  <span class="hljs-attr">reducers</span>: {
    remove(state, action) {
      <span class="hljs-built_in">console</span>.log(action);
      state.todos = state.todos.filter(<span class="hljs-function">(<span class="hljs-params">todo</span>) =&gt;</span> todo.id !== action.payload);
    },
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> todosActions = todosSlice.actions;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> todosSlice.reducer;
</code></pre>
<p>Now we have exported the actions which can be dispatched from anywhere. Let's dispatch the action inside our <code>handleRemove</code> function.</p>
<p><code>src/components/Todos.jsx</code></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> { useDispatch, useSelector } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;
<span class="hljs-keyword">import</span> { todosActions } <span class="hljs-keyword">from</span> <span class="hljs-string">"../store/todos"</span>;

<span class="hljs-keyword">const</span> Todos = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todos } = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.todos);
  <span class="hljs-keyword">const</span> dispatch = useDispatch();

  <span class="hljs-keyword">const</span> handleRemove = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
    dispatch(todosActions.remove(id));
  };

  <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>&gt;</span>Todos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {todos.map((todo) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
              handleRemove(todo.id);
            }}
          &gt;
            Remove
          <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">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Todos;
</code></pre>
<p>First, we have to import <code>useDispatch</code> hook from <code>react-redux</code> .<code>useDispatch</code> hook returns a function to which we have to specify an action with the payload.</p>
<p>Hurray!! Our application can remove todos 😄.</p>
<p>Now to add some asynchronous code let's fetch todos from API. We cannot add async functions inside of the reducers object. We have to define our asynchronous function and create our reducer logic. To create an async function we can use <code>createAsyncThunk</code> function. We can pass an async function to this method and return a promise. The createAsyncThunk will create lifecycle methods for the state slice.</p>
<p><code>src/store/todos.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createAsyncThunk, createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getTodos = createAsyncThunk(<span class="hljs-string">"todos/getTodos"</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://jsonplaceholder.typicode.com/todos"</span>);
  <span class="hljs-keyword">const</span> todos = <span class="hljs-keyword">await</span> response.json();
  <span class="hljs-keyword">return</span> todos;
});

<span class="hljs-keyword">const</span> todosSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"todos"</span>,
  <span class="hljs-attr">initialState</span>: {
    <span class="hljs-attr">loading</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">todos</span>: [
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"1"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"first todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
      { <span class="hljs-attr">id</span>: <span class="hljs-string">"2"</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">"second todo"</span>, <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span> },
    ],
    <span class="hljs-attr">error</span>: <span class="hljs-literal">null</span>,
  },
  <span class="hljs-attr">reducers</span>: {
    remove(state, action) {
      <span class="hljs-built_in">console</span>.log(action);
      state.todos = state.todos.filter(<span class="hljs-function">(<span class="hljs-params">todo</span>) =&gt;</span> todo.id !== action.payload);
    },
  },
  <span class="hljs-attr">extraReducers</span>: {
    [getTodos.pending]:<span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
      state.loading = <span class="hljs-literal">true</span>;
    },
    [getTodos.fulfilled]:<span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
      state.todos = action.payload;
      state.loading = <span class="hljs-literal">false</span>;
      state.error = <span class="hljs-literal">null</span>;
    },
    [getTodos.rejected]:<span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
      state.loading = <span class="hljs-literal">false</span>;
      state.error = <span class="hljs-string">"something went wrong"</span>;
    },
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> todosActions = todosSlice.actions;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> todosSlice.reducer;
</code></pre>
<p>We have created the getTodos function which uses <code>createAsyncThunk</code> . The first argument requires the function name prefixed with the slice name. The second argument is an async function which should return a promise. This function doesn't create a reducer for us. We have to handle the reducer and lifecycle logic inside <code>extraReducers</code> property.</p>
<p>There are some extra initial state properties added (loading and error). The <code>getTodos</code> function is also exported and we can directly dispatch this method.</p>
<p><code>src/components/Todos.jsx</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useDispatch, useSelector } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;
<span class="hljs-keyword">import</span> { getTodos, todosActions } <span class="hljs-keyword">from</span> <span class="hljs-string">"../store/todos"</span>;

<span class="hljs-keyword">const</span> Todos = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { todos, loading, error } = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.todos);
  <span class="hljs-keyword">const</span> dispatch = useDispatch();

  <span class="hljs-keyword">const</span> handleRemove = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
    dispatch(todosActions.remove(id));
  };

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

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

  <span class="hljs-keyword">if</span> (!loading &amp;&amp; error) {
    <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>&gt;</span>Error<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{error}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
  }

  <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>&gt;</span>Todos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {todos.map((todo) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
              handleRemove(todo.id);
            }}
          &gt;
            Remove
          <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">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Todos;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1671090049176/TLlpaZnXe.png" alt class="image--center mx-auto" /></p>
<p>We just have to dispatch the <code>getTodos</code> method in <code>Todos.jsx</code></p>
<p>With that, we finished our application which is using the redux and redux toolkit 💥</p>
]]></content:encoded></item></channel></rss>