<?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[Theodorus Clarence's Blog]]></title><description><![CDATA[Theodorus Clarence's Blog]]></description><link>https://hashnode.theodorusclarence.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 05:15:44 GMT</lastBuildDate><atom:link href="https://hashnode.theodorusclarence.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Create Fully Reusable React Components]]></title><description><![CDATA[Introduction
Creating a React component is fairly easy, just a function like this, and it’s done.
export default function Card() {
  return <div>card</div>;
}

then you can call them using JSX like <Card />.
However, to do them correctly is the reaso...]]></description><link>https://hashnode.theodorusclarence.com/how-to-create-fully-reusable-react-components</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/how-to-create-fully-reusable-react-components</guid><category><![CDATA[Next.js]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Thu, 14 Dec 2023 12:56:43 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Creating a React component is fairly easy, just a function like this, and it’s done.</p>
<pre><code class="lang-ts"><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">Card</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> &lt;div&gt;card&lt;/div&gt;;
}
</code></pre>
<p>then you can call them using JSX like <code>&lt;Card /&gt;</code>.</p>
<p>However, <strong>to do them correctly</strong> is the reason that I write this blog. I’ve been wanting to write this for quite some time.</p>
<p>Doing them correctly may come naturally to developers who have been writing React for quite a while, but not for beginners. This is something that I learned the hard way over experience, nobody really taught me this. I want you who are currently reading to get them quickly.</p>
<h2 id="heading-the-common-flaw">The Common Flaw</h2>
<p>If you look at the Card component that I created above, it has one <strong>crucial</strong> flaw: it’s not fully reusable.</p>
<blockquote>
<p>But wait, it is reusable?!</p>
</blockquote>
<p>Well yes, you can use them multiple times in different files.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// order page</span>
&lt;Card /&gt;

<span class="hljs-comment">// product list page</span>
{[...].map(<span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> &lt;Card /&gt;)}
</code></pre>
<p>But they are not <strong>fully reusable</strong></p>
<p>A fully reusable component is what I call a component that is enjoyable to use. It’s not something that frustrates you, every time you need to customize the component. It’s something that actually helps you code quickly by being reusable.</p>
<h2 id="heading-flawed-component-in-action">Flawed Component in Action</h2>
<p>Let’s say you have a layout like this</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/fully-reusable-component/normal-layout-three" alt="normal-layout-three" /></p>
<p>We can easily achieve them using this code:</p>
<p>*I use Tailwind CSS for this demo, but the concept applies to all solutions.</p>
<pre><code class="lang-ts"><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">ProductListPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">'grid-cols-3'</span>&gt;
      {products.map(<span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> (
        &lt;Card key={product.id} product={product} /&gt;
      ))}
    &lt;/div&gt;
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ product }: { product: Product }</span>) </span>{
  <span class="hljs-keyword">return</span> &lt;div className=<span class="hljs-string">'border p-1'</span>&gt;{product.title}&lt;/div&gt;;
}
</code></pre>
<p>So we have a three-column grid, and we map all of the cards inside that grid. Inside the card we have a product props that renders the title. Pretty simple right?</p>
<p>Here’s how it went haywire.</p>
<h2 id="heading-customization-in-flawed-components">Customization in Flawed Components</h2>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/fully-reusable-component/layout-with-featured" alt="layout-with-featured" /></p>
<p>Then your lovely designer has requests:</p>
<ul>
<li>Make the first card span over 2 columns (it’s for featured products)</li>
<li>When we click a product with a title containing ‘yay’, I want them to shoot out confetti by calling <code>confetti()</code>.</li>
</ul>
<p>Well, you could create two brand new components called <code>FeatureCard</code> and <code>ConfettiCard</code>, but it’s <strong>counter-productive.</strong> Everything inside is totally the same, except that one uses two columns, and one shoots confetti.</p>
<p>Usually, when have this kind of situation, we rely upon custom props for each condition. But it goes downhill pretty quickly as the requirements grow.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> CardProps = {
  product: Product;
  isFeatured?: <span class="hljs-built_in">boolean</span>;
  shootsConfetti?: <span class="hljs-built_in">boolean</span>;

  isFeaturedThreeColumns?: <span class="hljs-built_in">boolean</span>;
  isFeaturedButMakeItPop?: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-comment">// 20 other props that your designer needs</span>
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ product, ...props }: CardProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div
      <span class="hljs-comment">// clsx is a simple library to combine string together</span>
      className={clsx([
        <span class="hljs-string">'border p-1'</span>,
        props.isFeatured &amp;&amp; <span class="hljs-string">'col-span-2'</span>,
        props.isFeaturedThreeColumns &amp;&amp; <span class="hljs-string">'col-span-3'</span>,
        props.isFeaturedButMakeItPop &amp;&amp; <span class="hljs-string">'bg-pink-500'</span>,
      ])}
      onClick={<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (props.shootsConfetti) {
          confetti();
        }
      }}
    &gt;
      {product.title}
    &lt;/div&gt;
  );
}
</code></pre>
<p>I agree that using custom props sometimes be the best solution, but we can totally this problem if we can directly pass <code>classNames</code> and <code>onClick</code> directly into the component.</p>
<h2 id="heading-fully-reusable-component-as-a-solution">Fully Reusable Component as A Solution</h2>
<p>So what we basically need, is to add all of the props that are in a div to the component, that concludes <code>className</code>, <code>onClick</code>, <code>onHover</code>, <code>title</code>, <code>aria-label</code>, <code>style</code>, <code>about</code>, <code>id</code>, <code>onMouseEnter</code>, <code>onMouseLeave</code>. Yeah, you got my point.</p>
<p>It’s A LOT.</p>
<p>Don’t worry we have a type for that, may I introduce <code>React.ComponentPropsWithoutRef&lt;'div'&gt;</code>. So instead of adding each and every component props, we can use this helpful type.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> CardProps = {
  product: Product;
} &amp; React.ComponentPropsWithoutRef&lt;<span class="hljs-string">'div'</span>&gt;;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ product, ...rest }: CardProps</span>) </span>{
  <span class="hljs-comment">// {...rest} is grabbing all of the props,</span>
  <span class="hljs-comment">// then spreading them back to our div</span>
  <span class="hljs-keyword">return</span> &lt;div {...rest}&gt;{product.title}&lt;/div&gt;;
}
</code></pre>
<p>This also applies to any element you’re using: <code>&lt;'input'&gt;</code>, <code>&lt;'button'&gt;</code>, anything!</p>
<p>The best thing is we now get autocomplete! Woohoo!</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/fully-reusable-component/autocomplete" alt="autocomplete" /></p>
<p>With the updated component, we can finally make our designer happy with this implementation</p>
<pre><code class="lang-ts"><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">ProductListPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">'grid-cols-3'</span>&gt;
      {products.map(<span class="hljs-function">(<span class="hljs-params">product, i</span>) =&gt;</span> (
        &lt;Card
          key={product.id}
          product={product}
          className={clsx([i === <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-string">'col-span-2'</span>])}
          onClick={<span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">if</span> (product.title.contains(<span class="hljs-string">'yay'</span>)) confetti();
          }}
        /&gt;
      ))}
    &lt;/div&gt;
  );
}
</code></pre>
<p>We can add the two-columns feature by adding a custom className, and the confetti feature by adding an onClick directly in the <code>Card</code> component.</p>
<h2 id="heading-common-pitfalls-amp-solutions">Common Pitfalls &amp; Solutions</h2>
<p>By using fully reusable components, there are some pitfalls that you might encounter. I compiled some of them along with the solutions that I came up with.</p>
<h3 id="heading-class-name-conflict">Class Name Conflict</h3>
<p>If you’re using Tailwind CSS, sometimes merging classes will cause a conflict</p>
<pre><code class="lang-ts"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{
  product,
  <span class="hljs-comment">// Don't forget to take className out of the rest parameter</span>
  className,
  ...rest
}: CardProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className={clsx([<span class="hljs-string">'mt-4'</span>, className])} {...rest}&gt;
      {product.title}
    &lt;/div&gt;
  );
}

&lt;Card className=<span class="hljs-string">'mt-12'</span> /&gt;;
</code></pre>
<p>We merge the <code>className</code> using <code>clsx</code> function, so any class that we pass outside of the component will be reflected in the final code.</p>
<p>However, in the rendered code we will have two different margin-top classes.</p>
<pre><code class="lang-ts">&lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">'mt-12 mt-4'</span>&gt;...&lt;/div&gt;
</code></pre>
<p>Which is not good. We can use <a target="_blank" href="https://www.npmjs.com/package/tailwind-merge">tailwind-merge</a> library to solve that.</p>
<p>tailwind-merge function will return the latest value in the parameter. So it will prioritize our <code>mt-12</code> over the <code>mt-4</code>. Basically what we need.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { twMerge } <span class="hljs-keyword">from</span> <span class="hljs-string">'tailwind-merge'</span>;

twMerge(<span class="hljs-string">'mt-4 bg-red hover:bg-dark-red'</span>, <span class="hljs-string">'mt-12 bg-[#B91C1C]'</span>);
<span class="hljs-comment">// → 'hover:bg-dark-red mt-12 bg-[#B91C1C]'</span>
</code></pre>
<p>I usually create a wrapper with <code>clsx</code> like this</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> clsx, { ClassValue } <span class="hljs-keyword">from</span> <span class="hljs-string">'clsx'</span>;
<span class="hljs-keyword">import</span> { twMerge } <span class="hljs-keyword">from</span> <span class="hljs-string">'tailwind-merge'</span>;

<span class="hljs-comment">/** Merge classes with tailwind-merge with clsx full feature */</span>
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cn</span>(<span class="hljs-params">...inputs: ClassValue[]</span>) </span>{
  <span class="hljs-keyword">return</span> twMerge(clsx(inputs));
}
</code></pre>
<p>So we now can merge conflicts and compose classes neatly.</p>
<p>You can safely customize your component now!</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> CardProps = {
  product: Product;
} &amp; React.ComponentPropsWithoutRef&lt;<span class="hljs-string">'div'</span>&gt;;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ product, className, ...rest }: CardProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className={cn([<span class="hljs-string">'mt-4'</span>, className])} {...rest}&gt;
      {product.title}
    &lt;/div&gt;
  );
}
</code></pre>
<h3 id="heading-multiple-class-name">Multiple Class Name</h3>
<p>When you start to have more items inside the component, it can be quite confusing as to how to pass a <code>className</code> to a specific element.</p>
<p>Let’s say our <code>Card</code> component has a title, description, and images. We already use <code>className</code> props for the wrapper div. How can we customize the title class?</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> CardProps = {
  product: Product;
} &amp; React.ComponentPropsWithoutRef&lt;<span class="hljs-string">'div'</span>&gt;;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ product, className, ...rest }: CardProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className={cn(<span class="hljs-string">'mt-4'</span>, className)} {...rest}&gt;
      &lt;h1&gt;{product.title}&lt;/h1&gt;
      &lt;p&gt;{product.description}&lt;/p&gt;
      &lt;img src={product.image} /&gt;
    &lt;/div&gt;
  );
}

<span class="hljs-comment">// How to access h1, p, and img?</span>
&lt;Card className=<span class="hljs-string">''</span> /&gt;;
</code></pre>
<p>Usually, for the normal <code>className</code> props, I always use it for the outermost element (wrapper). The solution is to create another object for a specific element that I might need.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> CardProps = {
  product: Product;
  classNames?: {
    title?: <span class="hljs-built_in">string</span>;
    description?: <span class="hljs-built_in">string</span>;
    image?: <span class="hljs-built_in">string</span>;
  };
} &amp; React.ComponentPropsWithoutRef&lt;<span class="hljs-string">'div'</span>&gt;;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ product, className, classNames, ...rest }: CardProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className={clsx(<span class="hljs-string">'mt-4'</span>, className)} {...rest}&gt;
      &lt;h1 className={cn(classNames?.title)}&gt;{product.title}&lt;/h1&gt;
      &lt;p className={cn(classNames?.description)}&gt;{product.description}&lt;/p&gt;
      &lt;img className={cn(classNames?.image)} src={product.image} /&gt;
    &lt;/div&gt;
  );
}

&lt;Card classNames={{ title: <span class="hljs-string">'text-red-500'</span>, image: <span class="hljs-string">'aspect-square'</span> }} /&gt;;
</code></pre>
<p>Here I created a <code>classNames</code> object with <code>title</code>, <code>description</code>, and <code>image</code> property. Then we can use them to merge the class in the respective element.</p>
<h3 id="heading-components-with-ref">Components With Ref</h3>
<p>You might notice that the type name is <code>ComponentPropsWithoutRef</code>, yes there is another type called <code>ComponentPropsWithRef</code>.</p>
<p>This is a needed case if you’re also forwarding ref to your component. I won’t explain in detail about ref forwarding, maybe in the next post (comment if you’d like me to write about it).</p>
<p>Simply, ref forwarding is needed when you want to access the ref value of your component. Usually external library like <a target="_blank" href="https://www.radix-ui.com/primitives">Radix</a> does.</p>
<p>You can add the type like this.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> ButtonProps = React.ComponentPropsWithRef&lt;<span class="hljs-string">'button'</span>&gt;;

<span class="hljs-keyword">const</span> Button = React.forwardRef&lt;HTMLButtonElement, ButtonProps&gt;(
  <span class="hljs-function">(<span class="hljs-params">{ className, children, ...rest }, ref</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      &lt;button ref={ref} className={cn(className)} {...rest}&gt;
        {children}
      &lt;/button&gt;
    );
  }
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>Only then you can access the ref.</p>
<pre><code class="lang-ts"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> buttonRef = React.useRef&lt;HTMLButtonElement&gt;(<span class="hljs-literal">null</span>);

  React.useEffect(<span class="hljs-function">() =&gt;</span> {
    buttonRef.current?.focus();
  }, []);

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;Button <span class="hljs-keyword">type</span>=<span class="hljs-string">'submit'</span> disabled ref={buttonRef}&gt;
        Submit
      &lt;/Button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p><em>See how I easily add type and disabled props because we’re using a fully reusable component? *chef’s kiss*</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Please use fully reusable components. Your teammates and your future-self will thank you.</p>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=hashnode">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[Next.js Authentication using Higher-Order Components]]></title><description><![CDATA[Introduction
Managing authentication in Next.js is quite tricky, with problems such as content flashing. In this blog, I won't address the problems and explain how to solve it in detail, because I've written a blog about that in Next.js Redirect With...]]></description><link>https://hashnode.theodorusclarence.com/nextjs-authentication-using-higher-order-components</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/nextjs-authentication-using-higher-order-components</guid><category><![CDATA[Next.js]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[React]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Sat, 18 Mar 2023 15:59:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/fDsCIIGdw9g/upload/686b69e49c2a48cc6e14efba6eecba38.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Managing authentication in Next.js is quite tricky, with problems such as content flashing. In this blog, I won't address the problems and explain how to solve it in detail, because I've written a blog about that in <a target="_blank" href="https://theodorusclarence.com/blog/nextjs-redirect-no-flashing">Next.js Redirect Without Flashing Content</a>.</p>
<p>In this blog, I'll cover how to handle them cleanly using Higher Order Components.</p>
<h2 id="heading-the-usual-way-amp-the-problem">The Usual Way &amp; The Problem</h2>
<p>Usually for the authentication in Next.js, we define routes that need to be blocked like so:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> protectedRoutes = [<span class="hljs-string">'/block-component'</span>, <span class="hljs-string">'/profile'</span>];
</code></pre>
<p>Then we have a component that checks the route like this:</p>
<pre><code class="lang-jsx"><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">PrivateRoute</span>(<span class="hljs-params">{ protectedRoutes, children }</span>) </span>{
  <span class="hljs-keyword">const</span> router = useRouter();
  <span class="hljs-keyword">const</span> { isAuthenticated, isLoading } = useAuth();

  <span class="hljs-keyword">const</span> pathIsProtected = protectedRoutes.indexOf(router.pathname) !== <span class="hljs-number">-1</span>;

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!isLoading &amp;&amp; !isAuthenticated &amp;&amp; pathIsProtected) {
      <span class="hljs-comment">// Redirect route, you can point this to /login</span>
      router.push(<span class="hljs-string">'/'</span>);
    }
  }, [isLoading, isAuthenticated, pathIsProtected]);

  <span class="hljs-keyword">if</span> ((isLoading || !isAuthenticated) &amp;&amp; pathIsProtected) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FullPageLoader</span> /&gt;</span></span>;
  }

  <span class="hljs-keyword">return</span> children;
}
</code></pre>
<p>This works, but there are several problems:</p>
<ol>
<li>It's <strong>not colocated</strong>, the placement of authentication is not located in the page itself, instead in another component such as <code>PrivateRoute</code></li>
<li><p><strong>Error Prone</strong>, when you're doing route changes, for example: if you're moving the <code>pages/blocked-component.tsx</code> file to <code>pages/blocked/component.tsx</code>, you will have to change the <code>protectedRoutes</code> variable into the new route.</p>
<p>This is quite dangerous because with the <code>protectedRoutes</code> variable, there are no type checking because there is no way for TypeScript to know if that's the right path. (<a target="_blank" href="https://nextjs.org/blog/next-13-2#statically-typed-links">maybe soon</a>)</p>
</li>
</ol>
<h2 id="heading-higher-order-component">Higher-Order Component</h2>
<p>My friend and I built a higher-order component that we can put inside the page like so:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> withAuth(ProtectedPage);
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProtectedPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">/* react component here */</span>
}
</code></pre>
<p>With this implementation, it's now colocated within the page and it won't be a problem if you change the file name.</p>
<h2 id="heading-adding-several-types-of-pages">Adding Several Types of Pages</h2>
<p>In my experience of building simple authenticated apps, there are 3 type of authenticated pages that we need to support</p>
<blockquote>
<p>For the demo, you can try it yourself on the <a target="_blank" href="https://auth-hoc.thcl.dev/">demo page</a></p>
</blockquote>
<h3 id="heading-1-simple-protected-pages">1. Simple Protected Pages</h3>
<p>It's for pages that need protection, such as dashboard, edit profile page, etc.</p>
<p><strong>Behavior</strong></p>
<ul>
<li><strong>Unauthenticated users</strong> will be redirected to <code>LOGIN_ROUTE</code> (default: <code>/login</code>), without any content flashing</li>
<li><strong>Authenticated users</strong> will see this page in this following scenario:<ul>
<li><strong>Direct visit using link</strong> → user will see a loading page while the <code>withAuth</code> component checks the token, then this page will be shown</li>
<li><strong>Visit from other pages</strong> (<code>router.push</code>) → user will see this page immediately</li>
</ul>
</li>
</ul>
<h3 id="heading-2-authentication-pages-login">2. Authentication Pages (Login)</h3>
<p>It's for pages such as Login and Register or any other page that suits with the behavior.</p>
<p><strong>Behavior:</strong></p>
<ul>
<li><strong>Unauthenticated users</strong> can access this page without any loading indicator</li>
<li><strong>Authenticated users</strong> will be redirected to <code>HOME_ROUTE</code> (default: <code>/</code>).<ul>
<li>We're assuming that authenticated users won't need to see login anymore. Instead, they should be redirected to the <code>HOME_ROUTE</code>.</li>
<li>It's also best to hide all links back to the login page when the users is already authenticated.</li>
</ul>
</li>
</ul>
<h3 id="heading-3-optional-page">3. Optional Page</h3>
<p>This is a more specific use case, but sometimes there are pages that you don't need to be authenticated to visit, but you still need to show the users details if they are authenticated.</p>
<p><strong>Behavior:</strong></p>
<ul>
<li>This page is accessible to all users</li>
<li>You can get the user from <code>useAuthStore.useUser()</code></li>
</ul>
<h2 id="heading-page-focus-synchronization">Page Focus Synchronization</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=RyUgDondT6A">https://www.youtube.com/watch?v=RyUgDondT6A</a></div>
<p>We also added a page focus listener. When you open several tabs, the authentication will be synced across tabs.</p>
<pre><code class="lang-ts">React.useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// run checkAuth every page visit</span>
  checkAuth();

  <span class="hljs-comment">// run checkAuth every focus changes</span>
  <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'focus'</span>, checkAuth);
  <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">'focus'</span>, checkAuth);
  };
}, [checkAuth]);
</code></pre>
<h2 id="heading-source-codes">Source Codes</h2>
<p>We use Zustand to store authentication data globally</p>
<h3 id="heading-zustand-store">Zustand Store</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { createSelectorHooks } <span class="hljs-keyword">from</span> <span class="hljs-string">'auto-zustand-selectors-hook'</span>;
<span class="hljs-keyword">import</span> produce <span class="hljs-keyword">from</span> <span class="hljs-string">'immer'</span>;
<span class="hljs-keyword">import</span> create <span class="hljs-keyword">from</span> <span class="hljs-string">'zustand'</span>;

<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/auth'</span>;

<span class="hljs-keyword">type</span> AuthStoreType = {
  user: User | <span class="hljs-literal">null</span>;
  isAuthenticated: <span class="hljs-built_in">boolean</span>;
  isLoading: <span class="hljs-built_in">boolean</span>;
  login: <span class="hljs-function">(<span class="hljs-params">user: User</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  logout: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  stopLoading: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
};

<span class="hljs-keyword">const</span> useAuthStoreBase = create&lt;AuthStoreType&gt;(<span class="hljs-function">(<span class="hljs-params">set</span>) =&gt;</span> ({
  user: <span class="hljs-literal">null</span>,
  isAuthenticated: <span class="hljs-literal">false</span>,
  isLoading: <span class="hljs-literal">true</span>,
  login: <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'token'</span>, user.token);
    set(
      produce&lt;AuthStoreType&gt;(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
        state.isAuthenticated = <span class="hljs-literal">true</span>;
        state.user = user;
      })
    );
  },
  logout: <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">'token'</span>);
    set(
      produce&lt;AuthStoreType&gt;(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
        state.isAuthenticated = <span class="hljs-literal">false</span>;
        state.user = <span class="hljs-literal">null</span>;
      })
    );
  },
  stopLoading: <span class="hljs-function">() =&gt;</span> {
    set(
      produce&lt;AuthStoreType&gt;(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
        state.isLoading = <span class="hljs-literal">false</span>;
      })
    );
  },
}));

<span class="hljs-keyword">const</span> useAuthStore = createSelectorHooks(useAuthStoreBase);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> useAuthStore;
</code></pre>
<h3 id="heading-withauth-hoc-component">withAuth HOC Component</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/router'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { ImSpinner8 } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-icons/im'</span>;

<span class="hljs-keyword">import</span> apiMock <span class="hljs-keyword">from</span> <span class="hljs-string">'@/lib/axios-mock'</span>;
<span class="hljs-keyword">import</span> { getFromLocalStorage } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/lib/helper'</span>;

<span class="hljs-keyword">import</span> useAuthStore <span class="hljs-keyword">from</span> <span class="hljs-string">'@/store/useAuthStore'</span>;

<span class="hljs-keyword">import</span> { ApiReturn } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/api'</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/auth'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> WithAuthProps {
  user: User;
}

<span class="hljs-keyword">const</span> HOME_ROUTE = <span class="hljs-string">'/'</span>;
<span class="hljs-keyword">const</span> LOGIN_ROUTE = <span class="hljs-string">'/login'</span>;

<span class="hljs-keyword">const</span> ROUTE_ROLES = [
  <span class="hljs-comment">/**
   * For authentication pages
   * @example /login /register
   */</span>
  <span class="hljs-string">'auth'</span>,
  <span class="hljs-comment">/**
   * Optional authentication
   * It doesn't push to login page if user is not authenticated
   */</span>
  <span class="hljs-string">'optional'</span>,
  <span class="hljs-comment">/**
   * For all authenticated user
   * will push to login if user is not authenticated
   */</span>
  <span class="hljs-string">'all'</span>,
] <span class="hljs-keyword">as</span> <span class="hljs-keyword">const</span>;
<span class="hljs-keyword">type</span> RouteRole = (<span class="hljs-keyword">typeof</span> ROUTE_ROLES)[<span class="hljs-built_in">number</span>];

<span class="hljs-comment">/**
 * Add role-based access control to a component
 *
 * @see https://react-typescript-cheatsheet.netlify.app/docs/hoc/full_example/
 * @see https://github.com/mxthevs/nextjs-auth/blob/main/src/components/withAuth.tsx
 */</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">withAuth</span>&lt;<span class="hljs-title">T</span> <span class="hljs-title">extends</span> <span class="hljs-title">WithAuthProps</span> = <span class="hljs-title">WithAuthProps</span>&gt;(<span class="hljs-params">
  Component: React.ComponentType&lt;T&gt;,
  routeRole: RouteRole
</span>) </span>{
  <span class="hljs-keyword">const</span> ComponentWithAuth = <span class="hljs-function">(<span class="hljs-params">props: Omit&lt;T, keyof WithAuthProps&gt;</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> router = useRouter();
    <span class="hljs-keyword">const</span> { query } = router;

    <span class="hljs-comment">//#region  //*=========== STORE ===========</span>
    <span class="hljs-keyword">const</span> isAuthenticated = useAuthStore.useIsAuthenticated();
    <span class="hljs-keyword">const</span> isLoading = useAuthStore.useIsLoading();
    <span class="hljs-keyword">const</span> login = useAuthStore.useLogin();
    <span class="hljs-keyword">const</span> logout = useAuthStore.useLogout();
    <span class="hljs-keyword">const</span> stopLoading = useAuthStore.useStopLoading();
    <span class="hljs-keyword">const</span> user = useAuthStore.useUser();
    <span class="hljs-comment">//#endregion  //*======== STORE ===========</span>

    <span class="hljs-keyword">const</span> checkAuth = React.useCallback(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> token = getFromLocalStorage(<span class="hljs-string">'token'</span>);
      <span class="hljs-keyword">if</span> (!token) {
        isAuthenticated &amp;&amp; logout();
        stopLoading();
        <span class="hljs-keyword">return</span>;
      }
      <span class="hljs-keyword">const</span> loadUser = <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> apiMock.get&lt;ApiReturn&lt;User&gt;&gt;(<span class="hljs-string">'/me'</span>);

          login({
            ...res.data.data,
            token: token + <span class="hljs-string">''</span>,
          });
        } <span class="hljs-keyword">catch</span> (err) {
          <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">'token'</span>);
        } <span class="hljs-keyword">finally</span> {
          stopLoading();
        }
      };

      <span class="hljs-keyword">if</span> (!isAuthenticated) {
        loadUser();
      }
    }, [isAuthenticated, login, logout, stopLoading]);

    React.useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-comment">// run checkAuth every page visit</span>
      checkAuth();

      <span class="hljs-comment">// run checkAuth every focus changes</span>
      <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'focus'</span>, checkAuth);
      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">'focus'</span>, checkAuth);
      };
    }, [checkAuth]);

    React.useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">if</span> (!isLoading) {
        <span class="hljs-keyword">if</span> (isAuthenticated) {
          <span class="hljs-comment">// Prevent authenticated user from accessing auth or other role pages</span>
          <span class="hljs-keyword">if</span> (routeRole === <span class="hljs-string">'auth'</span>) {
            <span class="hljs-keyword">if</span> (query?.redirect) {
              router.replace(query.redirect <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>);
            } <span class="hljs-keyword">else</span> {
              router.replace(HOME_ROUTE);
            }
          }
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-comment">// Prevent unauthenticated user from accessing protected pages</span>
          <span class="hljs-keyword">if</span> (routeRole !== <span class="hljs-string">'auth'</span> &amp;&amp; routeRole !== <span class="hljs-string">'optional'</span>) {
            router.replace(
              <span class="hljs-string">`<span class="hljs-subst">${LOGIN_ROUTE}</span>?redirect=<span class="hljs-subst">${router.asPath}</span>`</span>,
              <span class="hljs-string">`<span class="hljs-subst">${LOGIN_ROUTE}</span>`</span>
            );
          }
        }
      }
    }, [isAuthenticated, isLoading, query, router, user]);

    <span class="hljs-keyword">if</span> (
      <span class="hljs-comment">// If unauthenticated user want to access protected pages</span>
      (isLoading || !isAuthenticated) &amp;&amp;
      <span class="hljs-comment">// auth pages and optional pages are allowed to access without login</span>
      routeRole !== <span class="hljs-string">'auth'</span> &amp;&amp;
      routeRole !== <span class="hljs-string">'optional'</span>
    ) {
      <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">'flex min-h-screen flex-col items-center justify-center text-gray-800'</span>&gt;
          &lt;ImSpinner8 className=<span class="hljs-string">'mb-4 animate-spin text-4xl'</span> /&gt;
          &lt;p&gt;Loading...&lt;/p&gt;
        &lt;/div&gt;
      );
    }

    <span class="hljs-keyword">return</span> &lt;Component {...(props <span class="hljs-keyword">as</span> T)} user={user} /&gt;;
  };

  <span class="hljs-keyword">return</span> ComponentWithAuth;
}
</code></pre>
<p>For more code and implementation examples check out the code on <a target="_blank" href="https://github.com/theodorusclarence/nextjs-with-auth-hoc">GitHub</a></p>
<h2 id="heading-attribution">Attribution</h2>
<ul>
<li><a target="_blank" href="https://rizqitsani.com">Rizqi Tsani</a>, co-creator of this code.</li>
<li><a target="_blank" href="https://next-auth.js.org/">Next Auth</a>, for the inspiration and the idea of using HOC to handle authentication.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This will be a great addition to your code, making it cleaner and more efficient. You should colocate your code as much as possible, and this will be a step to do that.</p>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=hashnode">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Swift's Value and Reference Types]]></title><description><![CDATA[Introduction
There are two kinds of types in Swift, which are Value and Reference Types. These types and their characteristics sometimes can be hard to remember and understand. Through this post, I'll try to explain it using a mental model and analog...]]></description><link>https://hashnode.theodorusclarence.com/understanding-swifts-value-and-reference-types</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/understanding-swifts-value-and-reference-types</guid><category><![CDATA[Swift]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Wed, 11 May 2022 08:07:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1652256411124/Ya0mc0Ksy.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>There are <strong>two kinds of types</strong> in Swift, which are Value and Reference Types. These types and their characteristics sometimes can be hard to remember and understand. Through this post, I'll try to explain it using a mental model and analogy which will help you easily master swift types.</p>
<h2 id="heading-primitives">Primitives</h2>
<p>If you use common programming languages (Java, JavaScript, etc) before, you must be familiar with <strong>primitives and non-primitives data types</strong>. I'm not going to jump into the details of primitives, but here are some illustrations I got from google.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/other-primitive-types_vfmjxc" alt="other-primitive-types" /></p>
<p>Usually, primitives conclude specific data types such as boolean, char, integer, and float.</p>
<h3 id="heading-does-swift-have-primitive-types">Does Swift have primitive types?</h3>
<p>No, <strong>Swift doesn't have primitive types.</strong> In a sense. Swift <strong>still provides 'primitive-like' data</strong> types such as Int, Bool, Double, etc. However, they are made with <strong>struct</strong>.</p>
<p>If you look into Swift's <code>Int</code> type definition, you can see that it is made with a <code>struct</code></p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/primitive-made-with-struct_k2psql" alt="primitive-made-with-struct" /></p>
<p>Interesting right?</p>
<hr />
<h2 id="heading-quick-intro-to-mental-model">Quick Intro to Mental Model</h2>
<blockquote>
<p>A mental model is an explanation of someone's <strong>thought process</strong> about how something works in the real world. It is a <strong>representation of the surrounding world</strong>. - <a target="_blank" href="https://en.wikipedia.org/wiki/Mental_model">Wikipedia</a></p>
</blockquote>
<p>You might be familiar with this variable box analogy:</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/box-analogy_ay0zra" alt="box-analogy" /></p>
<blockquote>
<p>We think of variables as containers that hold information and allow us to access them later. We will think of this as a <strong>box</strong> that has a <strong>label</strong> on it. - <a target="_blank" href="https://studeappsblog.medium.com/what-is-a-variable-dd7e539bf388">StudeApps</a></p>
</blockquote>
<p>This works wonders when you are trying to understand what a variable does.</p>
<p><strong>That is a mental model.</strong> You create a certain type of analogy to help you understand a concept.</p>
<p>The prior analogy is not a one-size-fits-all, I won't be using it to explain value &amp; reference type. So prepare for some changes 💪</p>
<hr />
<h2 id="heading-value-and-reference-types">Value and Reference Types</h2>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/types-in-swift_culrrx" alt="types-in-swift" /></p>
<p>There are two kinds of types in Swift which are <strong>Value Types</strong>, and <strong>Reference Types.</strong> Value types are usually defined as <code>struct</code>, <code>enum</code>, and <code>tuple</code>. Whereas the latter is usually defined as a <code>class</code></p>
<h3 id="heading-wire-analogy">Wire Analogy</h3>
<p>I'm going to use a new mental model for variables, which uses a wire to <strong>point</strong> to the value it holds.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/wire-analogy_b0nblr" alt="wire-analogy" /></p>
<p>Therefore each variable can point to a single value according to its data type.</p>
<hr />
<h2 id="heading-value-types">Value Types</h2>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/value-types_ofai6i" alt="value-types" /></p>
<blockquote>
<p>A value type is a type whose value is <strong>copied</strong> when it's assigned to a variable or constant, or when it's passed to a function - <a target="_blank" href="https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html">Swift Docs</a></p>
</blockquote>
<p>Remember that 'primitive' data types like Int, Double, String, etc. are made with <strong>struct.</strong> So they follow the value type mental model.</p>
<h3 id="heading-mental-model">Mental Model</h3>
<p>Let's say we have a struct of Animal (the behavior is also the same with enum, tuple, also Int, String because they're made with struct)</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">var</span> legs = <span class="hljs-number">4</span>
}

<span class="hljs-keyword">var</span> sheep = <span class="hljs-type">Animal</span>()
</code></pre>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/types-model-1_jwbgaz" alt="types-model-1" /></p>
<p>Then, we are assigning the <code>cow</code> variables with the value of <code>sheep</code></p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> cow = sheep
</code></pre>
<p>Key point: <strong>the value will be copied</strong>.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/types-model-2_uguz9k" alt="types-model-2" /></p>
<h3 id="heading-effect-of-copying">Effect of Copying</h3>
<p>After we copy, the <code>sheep</code> and <code>cow</code> variables now points to <strong>two different struct.</strong> Therefore if we mutate the <code>cow</code>, the <code>sheep</code> <strong>won't get affected</strong>, and vice versa.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">var</span> legs = <span class="hljs-number">4</span>
}

<span class="hljs-keyword">var</span> sheep = <span class="hljs-type">Animal</span>()
<span class="hljs-keyword">var</span> cow = sheep

<span class="hljs-comment">// mutating cow's property</span>
cow.legs = <span class="hljs-number">3</span>

<span class="hljs-built_in">print</span>(sheep.legs) <span class="hljs-comment">// 4</span>
<span class="hljs-built_in">print</span>(cow.legs) <span class="hljs-comment">// 3</span>
</code></pre>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/types-copy-effect_g8v4gn" alt="types-copy-effect" /></p>
<h2 id="heading-reference-types">Reference Types</h2>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/reference-types_cwxcvo" alt="reference-types" /></p>
<blockquote>
<p>A reference types is where instances <strong>share a single copy</strong> of the data when they're assigned to a variable or constant, or when they're passed to a function.</p>
</blockquote>
<p>In the wire analogy, <strong>it will point to the same value</strong>. We're using a class that behaves as a reference type.</p>
<h3 id="heading-mental-model">Mental Model</h3>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">var</span> legs = <span class="hljs-number">4</span>
}

<span class="hljs-keyword">var</span> sheep = <span class="hljs-type">Animal</span>()
<span class="hljs-keyword">var</span> cow = sheep
</code></pre>
<p>Key Point: <strong>It will share a single copy</strong></p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/reference-model_oj5b3u" alt="reference-model" /></p>
<h3 id="heading-effect-of-sharing-a-single-copy">Effect of Sharing A Single Copy</h3>
<p>I believe you already guessed correctly how it will behave. If we <strong>mutate</strong> one variable, <strong>both will be affected</strong>.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">var</span> legs = <span class="hljs-number">4</span>
}

<span class="hljs-keyword">var</span> sheep = <span class="hljs-type">Animal</span>()
<span class="hljs-keyword">var</span> cow = sheep

<span class="hljs-comment">// mutating cow's property</span>
cow.legs = <span class="hljs-number">3</span>

<span class="hljs-built_in">print</span>(sheep.legs) <span class="hljs-comment">// 3</span>
<span class="hljs-built_in">print</span>(cow.legs) <span class="hljs-comment">// 3</span>
</code></pre>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/reference-copy-effect_khsni9" alt="reference-copy-effect" /></p>
<h3 id="heading-proof">Proof</h3>
<p>To prove that it is sharing a single copy, we can use <code>===</code> (<a target="_blank" href="https://developer.apple.com/documentation/swift/1538988">identity equality</a>). It will return true if two reference point to the same object instance.</p>
<p>Let's throw in a new instance called <code>pig</code></p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> sheep = <span class="hljs-type">Animal</span>()
<span class="hljs-keyword">var</span> cow = sheep

<span class="hljs-comment">// created a new instance</span>
<span class="hljs-keyword">var</span> pig = <span class="hljs-type">Animal</span>()
</code></pre>
<p>Here's the wire</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/reference-proof_pkg0tg" alt="reference-proof" /></p>
<p>Then we can compare them using identity equality</p>
<pre><code class="lang-swift"><span class="hljs-built_in">print</span>(sheep === cow) <span class="hljs-comment">// true</span>
<span class="hljs-built_in">print</span>(sheep === pig) <span class="hljs-comment">// false</span>
</code></pre>
<p>When in doubt, draw the wire analogy to help you. I'm using <a target="_blank" href="https://excalidraw.com/">excalidraw</a> for the illustration</p>
<h2 id="heading-reference-types-inside-of-value-types">Reference Types Inside of Value Types</h2>
<p>Important thing to note is: If you are referencing a class inside of a struct, then <strong>that variable will still behave like the reference type</strong></p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Leg</span> </span>{
    <span class="hljs-keyword">var</span> <span class="hljs-built_in">count</span> = <span class="hljs-number">4</span>
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> legs = <span class="hljs-type">Leg</span>()
}

<span class="hljs-keyword">var</span> sheep = <span class="hljs-type">Animal</span>(name: <span class="hljs-string">"Sheep"</span>)
<span class="hljs-keyword">var</span> cow = sheep

sheep.legs.<span class="hljs-built_in">count</span> = <span class="hljs-number">3</span>

<span class="hljs-built_in">print</span>(sheep.legs.<span class="hljs-built_in">count</span>) <span class="hljs-comment">// 3</span>
<span class="hljs-built_in">print</span>(cow.legs.<span class="hljs-built_in">count</span>) <span class="hljs-comment">// 3</span>

<span class="hljs-comment">// referencing the same class</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"\(sheep.legs === sheep.legs)"</span>) <span class="hljs-comment">// true</span>
</code></pre>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/reference-inside-value_fg6n91" alt="reference-inside-value" /></p>
<h2 id="heading-additional-emphasis">Additional Emphasis</h2>
<p>I need to emphasize this in case you're coming from <strong>a JavaScript</strong> background.</p>
<p>In Swift, <strong>Array and Dictionary are all value types</strong>.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/array-struct_xfz7eu" alt="array-struct" /></p>
<p>It is still made with struct 😬</p>
<h2 id="heading-how-to-choose">How to Choose?</h2>
<p>I don't have much experience with this yet, so I'll <a target="_blank" href="https://developer.apple.com/swift/blog/?id=10">quote an article</a> instead</p>
<p>Use a value type when:</p>
<ul>
<li>Comparing instance data with <code>==</code> makes sense</li>
<li>You want copies to have an independent state</li>
<li>The data will be used in code across multiple threads</li>
</ul>
<p>Use a reference type (e.g. use a class) when:</p>
<ul>
<li>Comparing instance identity with <code>===</code> makes sense</li>
<li>You want to create a shared, mutable state</li>
</ul>
<p>I believe that using value type for overall use will be sufficient. We can trust that when we change one variable/property, it won't affect the others. Thus, creating <strong>a sense of safety and reliability</strong>.</p>
<p>Keep a note that this difference only happens when you mutate. <strong>In absence of mutation, values and references act exactly the same way.</strong></p>
<h2 id="heading-functions-andamp-in-out">Functions &amp; In-Out</h2>
<p>Function parameter follows <strong>value types.</strong> This means you can't mutate the parameter and change the value.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/swift-value-reference/function-unable-to-mutate_jk9sbh" alt="function-unable-to-mutate" /></p>
<p><strong>Swift won't even let you mutate them.</strong> Because what is passed in the parameter will be converted into a <code>let</code> variable.</p>
<p>You can <strong>imitate reference types</strong> on function parameter by using <code>inout</code></p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> numbers = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>]

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">foo</span><span class="hljs-params">(<span class="hljs-number">_</span> arr: <span class="hljs-keyword">inout</span> Array&lt;Int&gt;)</span></span>  {
    arr.removeLast()
}

foo(&amp;numbers)
<span class="hljs-built_in">print</span>(numbers) <span class="hljs-comment">// [1,2]</span>
</code></pre>
<p>Notice the <code>&amp;</code>(ampersand) which is an explicit recognition that you're aware it is being used as <code>inout</code>.</p>
<p>Under the hood, the In-Out parameter <strong>doesn't use reference types.</strong></p>
<blockquote>
<p>This behavior is known as <em>copy-in copy-out</em> or <em>call by value result</em>. For example, when a computed property or a property with observers is passed as an in-out parameter, its getter is called as part of the function call and its setter is called as part of the function return. - <a target="_blank" href="https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID545">Swift Docs</a></p>
</blockquote>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You now understand that:</p>
<ul>
<li>Swift 'primitive-like' variables are made with a struct</li>
<li>Value types will copy the value if assigned to a variable or passed into a function</li>
<li>Reference types will share a single instance if assigned to a variable or passed into a function</li>
<li>Mutating value types won't affect the other copy, on the other hand, mutating reference types will affect the single instance</li>
<li>Function parameters follows value types, but can imitate reference types by using the in-out parameter</li>
</ul>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=hashnode">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[Back To Basic: Mental Model to Understand Flexbox]]></title><description><![CDATA[Mental models are personal, internal representations of external reality that people use to interact with the world around them. They are constructed by individuals based on their unique life experiences, perceptions, and understandings of the world....]]></description><link>https://hashnode.theodorusclarence.com/back-to-basic-mental-model-to-understand-flexbox</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/back-to-basic-mental-model-to-understand-flexbox</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[CSS]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[flexbox]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Sun, 16 Jan 2022 03:54:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/PpgY7sjpf_0/upload/v1642305152140/20LH_q3zn.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Mental models are personal, internal representations of external reality that people use to interact with the world around them. They are constructed by individuals based on their unique life experiences, perceptions, and understandings of the world. These are the mental model that I use to really understand flexbox, and I hope these can help you to understand too.</p>
<p>In this flexbox tutorial we will try to understand these properties &amp; topics</p>
<ul>
<li>Flex Direction</li>
<li>Justify Content</li>
<li>Align Items</li>
<li>How Flexbox Divides its children</li>
<li>Flex Grow vs Width: 100%</li>
</ul>
<h2 id="heading-flex-direction">Flex Direction</h2>
<p>There are 4 values for <code>flex-direction</code> property which are:</p>
<ul>
<li>column</li>
<li>row</li>
<li>column-reversed</li>
<li>row-reversed</li>
</ul>
<p><a target="_blank" href="https://flexbox.help/">flexbox.help</a> has a great interactive illustration if you want to check it out. These are all the output I reproduced:</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/Flex_Direction_wp5tpx.png" alt="Flex Direction" /></p>
<p>Looking at the property's value, it is kind of confusing, because if we assign <code>flex-direction: row</code> it is stacked to the right.</p>
<p>But here is the mental model to understand and to remember it. I want you to remember that we are putting the <code>flex-direction</code> to the red box, not the items. According to that, we are basically telling the red box to be a single row. Think of it as an excel spreadsheet. If the red box is a single row, we can only put elements to the right, like the excel spreadsheet.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/Spreadsheet_pljkqc.png" alt="Spreadsheet" /></p>
<p>Got it?</p>
<p><code>flex-direction: column</code> is basically the same, you are telling the red box to be a single column in a spreadsheet, then you can only add items to the bottom.</p>
<p>As for the column-reverse, and the row-reverse, I think you already got the hang of it, just reverse it.</p>
<h2 id="heading-justify-content">Justify Content</h2>
<p>Justify content is the property to move items around on the <strong>main axis</strong>. What is the main axis? The main axis is the direction we declare using <code>flex-direction</code>. For example with the same illustration on top, when we declare <code>flex-direction: row</code>, then the main axis is horizontal or to left-right.</p>
<p>Here is a great illustration from <a target="_blank" href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">css-tricks</a>.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/justify_content_uelnhn" alt="justify" /></p>
<blockquote>
<p>The mental model I use to remember this is, Justify Content moves around items on the main axis.</p>
</blockquote>
<p>I think the values of the property is very clear on the illustration.</p>
<h2 id="heading-align-items">Align Items</h2>
<p>This is the opposite of <code>justify-content</code> but <code>align-items</code> do not have all property from justify-content. Align items work on the <strong>cross axis</strong>. Which is the opposite of our main axis.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/align-items_mnzy9w" alt="align-items" /></p>
<blockquote>
<p>Same with justify-content mental model, you only need to remember that Align Items work on the cross axis.</p>
</blockquote>
<h2 id="heading-how-flexbox-divide-its-children">How Flexbox divide its children</h2>
<p>We have a red box with 500px width, and 5 items with each 100px width.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/flexbox-divide_xupnpf" alt="flexbox-divide" /></p>
<p>with code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"red-box"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>2<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>3<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>4<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"item"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>5<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
  <span class="hljs-selector-class">.red-box</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">500px</span>;
    <span class="hljs-attribute">display</span>: flex;
  }
  <span class="hljs-selector-class">.item</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100px</span>;
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>I like to create a mental model where each item is requesting to the red box (which will we call <strong>Reddy</strong> for short) how much do they need.</p>
<p>In the beginning Reddy is 500px wide, and each items is requesting 100px.</p>
<pre><code class="lang-text">Reddy: 500px

Request List:
1,2,3,4,5: 100px
</code></pre>
<p>Because Reddy has 500px space, so Reddy gives each item the requested amount, which is 100px.</p>
<h3 id="heading-case-1-when-reddys-width-is-larger-than-the-requested-amount">Case 1: When Reddy's width is larger than the requested amount</h3>
<p>Let's say that Reddy is 700px, Reddy will still only give the items the requested amount which is 100px and has some spare spaces.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/larger_jkzrud" alt="larger" /></p>
<h3 id="heading-case-2-when-reddys-width-is-smaller-than-the-requested-amount">Case 2: When Reddy's width is smaller than the requested amount</h3>
<p>When Reddy has limited width, Reddy must give each item a <strong>fair</strong> amount of width according to the <strong>request ratio</strong>.</p>
<p>Now, how do we calculate the ratio? That's quite easy, we need to find the ratio of all the requests, so:</p>
<p>Item1 : Item2 : Item3 : Item4 : Item5 = 1 : 1 : 1 : 1 : 1</p>
<p>So, from the ratio, Reddy will give each items equally.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/divide_equal_nnad59" alt="divide" /></p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/firefox-devtools_nzmwpm" alt="firefox-devtools" /></p>
<p>Firefox Devtool also has a nice information on how much each elements shrinks.</p>
<p><strong>Now, how small will Reddy give to each item?</strong> The items really care about the content they have, they don't want the content that they bring to get shrunk into non-existent</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o9yjttj1eyppyev2sju.gif" alt="flex-shrink-behavior" /></p>
<p>This illustration demonstrates that items won't shrink smaller than their content size, which in this case is 35px. Shrinking to content will create overflow.</p>
<h3 id="heading-case-3-different-request-ratio">Case 3: Different Request Ratio</h3>
<p>We already know if the ratio is 1:1:1:1:1, it will shrink the items into equal sizes, let's see how they work when the item requests are not identical.</p>
<pre><code class="lang-text">Reddy: 700px

Request List:
1,3,5: 100px
2,4: 200px
</code></pre>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/different_request_xoxazj" alt="different" /></p>
<p>Let's calculate the <strong>request ratio</strong>:</p>
<pre><code class="lang-text">Item Number = Ratio
1:2:3:4:5 = 100px:200px:100px:200px:100px
simplified,
1:2:3:4:5 = 1:2:1:2:1
</code></pre>
<p>So, Reddy will give the items according to that list.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pqlfi8uaxpz35ud1nmiq.gif" alt="different-ratio" /></p>
<p>As we can see, when Reddy gets shrunk to 350px, the item sizes will be: 50 : 100 : 50 : 100 : 50, identical to the <strong>request ratio</strong>.</p>
<p>You can also force the item to not shrink according to the requested width using <code>flex-shrink: 0</code> on the <code>.item</code>. Reddy will recognize the item as a VIP Member, and won't shrink it no matter what.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/33b5ltmqlalddkcorusr.gif" alt="shrink" /></p>
<h2 id="heading-flex-grow-vs-width-100">Flex Grow vs Width 100%</h2>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/btb-flex-mental-model/flex-grow-width_p8sksq" alt="flex-grow-width" /></p>
<p>Using <code>width: 100%</code> basically will translate to 100% of the container which is 500px. By doing that, this means that width: 100% <strong>will get calculated</strong> in the <strong>request ratio</strong> which is 1:5:1.</p>
<p>But, using <code>flex-grow: 1</code>, the items <strong>will not get calculated</strong> in the <strong>request ratio</strong>, but takes the rest of whatever is left.</p>
<h2 id="heading-summary">Summary</h2>
<p>There you go, after I understand how flexbox actually works, working with flexbox is a breeze. I also have a blog post on <a target="_blank" href="https://theodorusclarence.com/blog/btb-flexbox-grid">how to choose between flex and grid</a>, you might want to check it out.</p>
<p>Let me know if you guys have some question!</p>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=hashnode">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[How to Show Now Playing in Spotify with Next.js]]></title><description><![CDATA[On this website, I use Spotify API to show what song is currently playing. Now, I will try to explain on how do I do that.
Next.js has a built-in API in a single application, so choosing Next.js is a perfect choice so we don't expose our API keys.

W...]]></description><link>https://hashnode.theodorusclarence.com/how-to-show-now-playing-in-spotify-with-nextjs</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/how-to-show-now-playing-in-spotify-with-nextjs</guid><category><![CDATA[React]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Thu, 13 Jan 2022 15:19:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/PDX_a_82obo/upload/v1642087074051/miqJ4_Ppe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On this website, I use Spotify API to show what song is currently playing. Now, I will try to explain on how do I do that.</p>
<p>Next.js has a built-in API in a single application, so choosing Next.js is a perfect choice so we don't expose our API keys.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/spotify-playing_sx0rej" alt="spotify-playing" /></p>
<p><a target="_blank" href="https://spotify-playing.theodorusclarence.com">Website Demo</a></p>
<hr />
<h2 id="heading-1-create-an-application-on-spotify-developer">1. Create an Application on Spotify Developer</h2>
<ul>
<li>Go to <a target="_blank" href="https://developer.spotify.com/dashboard/applications">Spotify Developer</a> Website.</li>
<li>Click <strong>Create An App</strong></li>
<li>Fill in App Name, App description, then <strong>Create</strong></li>
</ul>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/2_jbfz2i.png" alt="Dashboard" /></p>
<ul>
<li>Next, you will be provided with <code>Client ID</code> and <code>Client Secret</code> (Don't give this secret to anyone)</li>
</ul>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/3_pcmoan.png" alt="Client Info" /></p>
<ul>
<li>Open edit settings, then fill out <code>http://localhost:3000</code> on RedirectURIs</li>
</ul>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/4_c4ol4r.png" alt="Edit Settings" /></p>
<p>The first step is done! Now, you need to do authentication to get access &amp; refresh token</p>
<hr />
<h2 id="heading-2-authenticate-your-account">2. Authenticate Your Account</h2>
<p>To do authentication, we need to prepare <code>CLIENT_ID</code> and put it into this link below:</p>
<pre><code class="lang-text">https://accounts.spotify.com/authorize?client_id=CLIENT_ID_HERE&amp;response_type=code&amp;redirect_uri=http
%3A%2F%2Flocalhost:3000&amp;scope=user-read-currently-playing
</code></pre>
<p>example:</p>
<pre><code class="lang-text">https://accounts.spotify.com/authorize?client_id=eaccb97f6d0e405897adf1dd80b95c01&amp;response_type=code&amp;redirect_uri=http
%3A%2F%2Flocalhost:3000&amp;scope=user-read-currently-playing
</code></pre>
<p>Open that link in a browser, then you will get redirected into a uri, copy the website link</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/5_ynbc2q.png" alt="Redirect URI" /></p>
<p>Example of the redirect link:</p>
<pre><code class="lang-text">http://localhost:3000/?code=AQBeA9SD7QbA9hUfv_TfmatYxT51CY87msMnOZmMbhf7ZaxfbvG7oKEsATOJBxDyFap0Aq6uftY0v4Hq1QSy3MgQBfAHhmrifty-62rfDRlFnd0AzXRBOMpoOSA6SNw_uTPp7AixAE5zosgiIIf7efhzf1QOJfLh1HUYi248z8jk1x2jjKG2YLvMyJuP0rjB5tP5UHjoFGBvKbULpchkF6yiJHnS
</code></pre>
<p><code>code</code> that you have is the one after (=). Don't forget to write it down.</p>
<p>Next we need to get authorization client that is already encrypted with base64, use this <a target="_blank" href="https://www.base64encode.org">website</a> to encrypt with the format of <code>client_id:client_secret</code></p>
<p>For example:</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/6_jkbqaa.png" alt="Base64 Encode" /></p>
<p>Next, write down the encrypted <code>base64</code></p>
<p>After that, open up terminal/cmd, and run this command, don't forget to change the <code>base64</code> and the <code>code</code> to yours.</p>
<pre><code class="lang-bash">curl -H <span class="hljs-string">"Authorization: Basic CHANGE_BASE64_HERE"</span>
-d grant_type=authorization_code -d code=CHANGE_CODE_HERE -d redirect_uri=http%3A
%2F%2Flocalhost:3000 https://accounts.spotify.com/api/token
</code></pre>
<p>*make sure the command is in one line</p>
<p>Example:</p>
<pre><code class="lang-bash">curl -H <span class="hljs-string">"Authorization: Basic ZWFjY2I5N2Y2ZDBlNDA1ODk3YWRmMWRkODBiOTVjMDE6YTQxOTVjMmQwYTQyNDM2MDllNjk3ZTYwMmU3MGI3NjI="</span> -d grant_type=authorization_code -d code=AQBeA9SD7QbA9hUfv_TfmatYxT51CY87msMnOZmMbhf7ZaxfbvG7oKEsATOJBxDyFap0Aq6uftY0v4Hq1QSy3MgQBfAHhmrifty-62rfDRlFnd0AzXRBOMpoOSA6SNw_uTPp7AixAE5zosgiIIf7efhzf1QOJfLh1HUYi248z8jk1x2jjKG2YLvMyJuP0rjB5tP5UHjoFGBvKbULpchkF6yiJHnS -d redirect_uri=http%3A%2F%2Flocalhost:3000 https://accounts.spotify.com/api/token
</code></pre>
<p>You will get a JSON like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"access_token"</span>: <span class="hljs-string">"BQDKxO7h1I1wA3esGK9zCFWn97XORJEPjwAHAEIxCnDXcmy9GbEuPacquwWvpiM4d33gJVHVOP9KUxY8AXkpXc-_zRFZBfneHM2vEeV1Fbfr-0Mw94oimlNf77dRiyxPpm4IUVNLloUWgYcfkAO0"</span>,
  <span class="hljs-attr">"token_type"</span>: <span class="hljs-string">"Bearer"</span>,
  <span class="hljs-attr">"expires_in"</span>: <span class="hljs-number">3600</span>,
  <span class="hljs-attr">"refresh_token"</span>: <span class="hljs-string">"AQAtxXvnzRTt4c2-2_Av2WyJQKWxUW_hMVN6QNiqv2i8A2ZElVarmvdhqyc8Pf-Z5n827FTFxTpHq5E3kOsrlRWM3TuJWxjVQsW0icR0zo3BXRFLt2FB2Qfj-pFaZwY-qc8"</span>,
  <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"user-read-currently-playing"</span>
}
</code></pre>
<p>Example on the terminal:</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/spotify-now-playing/7_rud11r.png" alt="Terminal" /></p>
<p>what we need to write down is the <code>refresh_token</code>. That token will last infinitely</p>
<p>Now you can do the request with Next.js or another backend application</p>
<hr />
<h2 id="heading-3-creating-api-routes-in-nextjs">3. Creating API routes in Next.js</h2>
<p>If you need a reference to create the app, you can <a target="_blank" href="https://github.com/theodorusclarence/now-playing-spotify">check my repository</a>. Don't forget to star it!</p>
<p>You can also use my <a target="_blank" href="https://github.com/theodorusclarence/nextjs-tailwind-starter">nextjs-tailwind-starter</a> by using:</p>
<pre><code class="lang-bash">npx create-next-app -e https://github.com/theodorusclarence/nextjs-tailwind-starter project-name
</code></pre>
<p>After all app is installed, add querystring dependency</p>
<pre><code class="lang-bash">yarn add querystring

or

npm i querystring
</code></pre>
<p>Next, make an api route in /pages/api/spotify.js</p>
<p>If you want the TypeScript version, checkout the code on <a target="_blank" href="https://github.com/theodorusclarence/theodorusclarence.com/blob/main/src/pages/api/spotify.ts">GitHub</a></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// /pages/api/spotify.js</span>
<span class="hljs-keyword">import</span> querystring <span class="hljs-keyword">from</span> <span class="hljs-string">'querystring'</span>;

<span class="hljs-keyword">const</span> {
  <span class="hljs-attr">SPOTIFY_CLIENT_ID</span>: client_id,
  <span class="hljs-attr">SPOTIFY_CLIENT_SECRET</span>: client_secret,
  <span class="hljs-attr">SPOTIFY_REFRESH_TOKEN</span>: refresh_token,
} = process.env;

<span class="hljs-keyword">const</span> basic = Buffer.from(<span class="hljs-string">`<span class="hljs-subst">${client_id}</span>:<span class="hljs-subst">${client_secret}</span>`</span>).toString(<span class="hljs-string">'base64'</span>);
<span class="hljs-keyword">const</span> NOW_PLAYING_ENDPOINT = <span class="hljs-string">`https://api.spotify.com/v1/me/player/currently-playing`</span>;
<span class="hljs-keyword">const</span> TOKEN_ENDPOINT = <span class="hljs-string">`https://accounts.spotify.com/api/token`</span>;

<span class="hljs-keyword">const</span> getAccessToken = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(TOKEN_ENDPOINT, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-attr">Authorization</span>: <span class="hljs-string">`Basic <span class="hljs-subst">${basic}</span>`</span>,
      <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/x-www-form-urlencoded'</span>,
    },
    <span class="hljs-attr">body</span>: querystring.stringify({
      <span class="hljs-attr">grant_type</span>: <span class="hljs-string">'refresh_token'</span>,
      refresh_token,
    }),
  });

  <span class="hljs-keyword">return</span> response.json();
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getNowPlaying = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> { access_token } = <span class="hljs-keyword">await</span> getAccessToken();

  <span class="hljs-keyword">return</span> fetch(NOW_PLAYING_ENDPOINT, {
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-attr">Authorization</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${access_token}</span>`</span>,
    },
  });
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (_, res) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> getNowPlaying();

  <span class="hljs-keyword">if</span> (
    response.status === <span class="hljs-number">204</span> ||
    response.status &gt; <span class="hljs-number">400</span> ||
    response.data.currently_playing_type !== <span class="hljs-string">'track'</span>
  ) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">isPlaying</span>: <span class="hljs-literal">false</span> });
  }

  <span class="hljs-keyword">const</span> data = {
    <span class="hljs-attr">isPlaying</span>: response.data.is_playing,
    <span class="hljs-attr">title</span>: response.data.item.name,
    <span class="hljs-attr">album</span>: response.data.item.album.name,
    <span class="hljs-attr">artist</span>: response.data.item.album.artists
      .map(<span class="hljs-function">(<span class="hljs-params">artist</span>) =&gt;</span> artist.name)
      .join(<span class="hljs-string">', '</span>),
    <span class="hljs-attr">albumImageUrl</span>: response.data.item.album.images[<span class="hljs-number">0</span>].url,
    <span class="hljs-attr">songUrl</span>: response.data.item.external_urls.spotify,
  };

  res.status(<span class="hljs-number">200</span>).json(data);
};
</code></pre>
<p>Add .env.local with authorization data that we have written (change the data below with yours)</p>
<pre><code class="lang-text">SPOTIFY_CLIENT_ID=eaccb97f6d0e405897adf1dd80b95c01
SPOTIFY_CLIENT_SECRET=a4195c2d0a4243609e697e602e70b7
SPOTIFY_REFRESH_TOKEN=AQAtxXvnzRTt4c2-2_Av2WyJQKWxUW_hMVN6QNiqv2i8A2ZElVarmvdhqyc8Pf-Z5n827FTFxTpHq5E3kOsrlRWM3TuJWxjVQsW0icR0zo3BXRFLt2FB2Qfj-pFaZwY-qc8
</code></pre>
<p>Your API is ready with route: <code>GET https://localhost:3000/api/spotify</code></p>
<hr />
<h2 id="heading-4-use-api-with-nextjs">4. Use API with Next.js</h2>
<p>For data fetching, we can use <a target="_blank" href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjm6NXChrfuAhVEeX0KHZB4BjwQFjAAegQICxAC&amp;url=https%3A%2F%2Fswr.vercel.app%2F&amp;usg=AOvVaw0gJGo0q67gd371C_Ax1J7k">SWR</a>. SWR is a great library that can fetch the API periodically. SWR will do refetching each time we refocus a the window. Also, install react-icons to get the spotify logo.</p>
<pre><code class="lang-bash">yarn add swr react-icons

or

npm i swr react-icons
</code></pre>
<p>Add SWR in pages/index.jsx like this:</p>
<p><a target="_blank" href="https://github.com/theodorusclarence/theodorusclarence.com/blob/main/src/components/layout/Spotify.tsx">TypeScript Version</a></p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> useSWR <span class="hljs-keyword">from</span> <span class="hljs-string">'swr'</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">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> fetcher = <span class="hljs-function">(<span class="hljs-params">url</span>) =&gt;</span> fetch(url).then(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.json());
  <span class="hljs-keyword">const</span> { data } = useSWR(<span class="hljs-string">'/api/spotify'</span>, fetcher);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-gray-600'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex justify-center items-center'</span>&gt;</span>
          {console.log(data)}
        <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<p>There will be 2 types of JSON data that will be sent by the Spotify API:</p>
<ol>
<li><p>When there is no song playing</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"isPlaying"</span>: <span class="hljs-literal">false</span>
}
</code></pre>
</li>
<li><p>When there is a song playing</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"album"</span>: <span class="hljs-string">"Menari Dengan Bayangan"</span>,
  <span class="hljs-attr">"albumImageUrl"</span>: <span class="hljs-string">"https://i.scdn.co/image/ab67616d0000b273d623688488865906052ef96b"</span>,
  <span class="hljs-attr">"artist"</span>: <span class="hljs-string">"Hindia"</span>,
  <span class="hljs-attr">"isPlaying"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"songUrl"</span>: <span class="hljs-string">"https://open.spotify.com/track/08OPqLv99g8avzmxQepmiw"</span>,
  <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Besok Mungkin Kita Sampai"</span>
}
</code></pre>
</li>
</ol>
<p>So, we can conditionally render the data like this:</p>
<pre><code class="lang-jsx">&lt;a
  target=<span class="hljs-string">'_blank'</span>
  rel=<span class="hljs-string">'noopener noreferer'</span>
  href={
    data?.isPlaying
      ? data.songUrl
      : <span class="hljs-string">'https://open.spotify.com/user/erence21?si=yTsrZT5JSHOp7tn3ist7Ig'</span>
  }
  className=<span class="hljs-string">'flex relative items-center p-5 space-x-4 w-72 rounded-md border transition-shadow hover:shadow-md'</span>
&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-16'</span>&gt;</span>
    {data?.isPlaying ? (
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">'w-16 shadow-sm'</span>
        <span class="hljs-attr">src</span>=<span class="hljs-string">{data?.albumImageUrl}</span>
        <span class="hljs-attr">alt</span>=<span class="hljs-string">{data?.album}</span>
      /&gt;</span>
    ) : (
      <span class="hljs-tag">&lt;<span class="hljs-name">SiSpotify</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{64}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{</span>'#<span class="hljs-attr">1ED760</span>'} /&gt;</span>
    )}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex-1'</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'component font-bold'</span>&gt;</span>
      {data?.isPlaying ? data.title : 'Not Listening'}
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'font-dark text-xs'</span>&gt;</span>
      {data?.isPlaying ? data.artist : 'Spotify'}
    <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="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'absolute right-1.5 bottom-1.5'</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">SiSpotify</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{</span>'#<span class="hljs-attr">1ED760</span>'} /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/a&gt;
</code></pre>
<p>You can check out the live deployment on <a target="_blank" href="https://spotify-playing.theodorusclarence.com">spotify-playing.theodorusclarence.com</a></p>
<h3 id="heading-thanks-and-goodluck">Thanks and Goodluck!</h3>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=hashnode">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Next.js Data Fetching (CSR, SSR, SSG, ISR)]]></title><description><![CDATA[Introduction
When I started to learn Next.js, I got overwhelmed with the list of abbreviations that looks similar, I didn't know what it is and what is the difference. It is quite confusing because when using Create React App, we usually only use 1 s...]]></description><link>https://hashnode.theodorusclarence.com/understanding-nextjs-data-fetching-csr-ssr-ssg-isr</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/understanding-nextjs-data-fetching-csr-ssr-ssg-isr</guid><category><![CDATA[Next.js]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Tue, 11 Jan 2022 09:08:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/eGRXI53lRvQ/upload/v1641892014004/aHz6gsUUT.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>When I started to learn Next.js, I got overwhelmed with the list of abbreviations that looks similar, I didn't know what it is and what is the difference. It is quite confusing because when using Create React App, we usually only use 1 strategy to fetch data from API which is using <code>useEffect</code>.</p>
<p>Next.js has many data fetching strategies. Although initially Next.js was well known to be a Server-Side Rendering Framework, it turns out that Next.js has 4 methods of Data Fetching. Here is the short explanation each so you get familiar with the abbreviation of CSR, SSR, SSG, ISR.</p>
<ul>
<li>CSR - Client-Side Rendering, this is the usual kind of data fetching using <code>useEffect</code>, it will fetch the data from the API every single page request on the <strong>client-side</strong> (after the page is rendered, then the function will run).</li>
<li>SSR - Server-Side Rendering, will run a <strong>special function</strong> to fetch data from API every page request on the <strong>server-side</strong> (before the page is loaded, that special function will run first, creating a delay, then after that, it will serve the page)<strong>.</strong></li>
<li>SSG - Static Site Generation, will run a <strong>special function</strong> to fetch data <strong>once</strong> when that page builds.</li>
<li>ISR – Incremental Static Regeneration, this is a new thing, shortly put, a combination of SSG, and SSR, where it served statically, but at a <strong>certain time and certain condition</strong> that page will rebuild and fetch the data from the API again.</li>
</ul>
<p>Don't worry if you didn't get that, because I will be explaining it thoroughly, just familiarize the words first.</p>
<hr />
<p>I mentioned before that there is a special function that will run when using a specific data fetching method. Keep that in mind as I will show you what is that special function.</p>
<p>This code example will fetch a date-time from an API using axios, then render it on the page. It is useful to see the date-time so we can truly know when the API is hit.</p>
<h2 id="heading-client-side-rendering-csr">Client-Side Rendering (CSR)</h2>
<p>Special Function: <code>useEffect</code></p>
<p><a target="_blank" href="https://next-render.theodorusclarence.com/render/csr">Demo Site</a></p>
<h3 id="heading-code-example">Code Example</h3>
<pre><code class="lang-ts"><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">CSRPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [dateTime, setDateTime] = React.useState&lt;<span class="hljs-built_in">string</span>&gt;();

  React.useEffect(<span class="hljs-function">() =&gt;</span> {
    axios
      .get(<span class="hljs-string">'https://worldtimeapi.org/api/ip'</span>)
      .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
        setDateTime(res.data.datetime);
      })
      .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> <span class="hljs-built_in">console</span>.error(error));
  }, []);

  <span class="hljs-keyword">return</span> (
    &lt;main&gt;
      &lt;TimeSection dateTime={dateTime} /&gt;
    &lt;/main&gt;
  );
}
</code></pre>
<h3 id="heading-demo">Demo</h3>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/CSR_x4o5as" alt="CSR" /></p>
<p>Terms:</p>
<ul>
<li>PT → Preview Time, the time shown when the API is hit. Can be seen in the middle.</li>
<li>RT → Real-Time, the real ticking time updating every second, can be seen on the right bottom corner</li>
</ul>
<p>Video Description:</p>
<ol>
<li>Page reloads on 15:46:03 Real-Time (RT), then a LOADING indicator is shown</li>
<li>After about 1s, Preview Time is showing 15:46:04(PT)</li>
</ol>
<h3 id="heading-keys-to-emphasize">Keys to Emphasize</h3>
<ol>
<li><strong>useEffect function</strong>, this function is the key indicator that a page is using Client-Side Rendering.</li>
<li><strong>LOADING indicator</strong>, because the data fetching runs after the page is rendered, the data is not fetched instantly, therefore showing a loading state.</li>
<li><strong>Data is fetched on every page request</strong>, which is why the time shown is different for each reloads.</li>
</ol>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/0-csr-illustration_aoxeou" alt="0-csr-illustration" /></p>
<hr />
<h2 id="heading-server-side-rendering-ssr">Server Side Rendering (SSR)</h2>
<p>Special Function: <code>getServerSideProps</code></p>
<p><a target="_blank" href="https://next-render.theodorusclarence.com/render/ssr">Demo Site</a></p>
<h3 id="heading-code-example">Code Example</h3>
<pre><code class="lang-ts"><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">SSRPage</span>(<span class="hljs-params">{ dateTime }: SSRPageProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;main&gt;
      &lt;TimeSection dateTime={dateTime} /&gt;
    &lt;/main&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getServerSideProps: GetServerSideProps = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://worldtimeapi.org/api/ip'</span>);

  <span class="hljs-keyword">return</span> {
    props: { dateTime: res.data.datetime },
  };
};
</code></pre>
<h3 id="heading-demo">Demo</h3>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/SSR_ififit" alt="SSR" /></p>
<p>Video Description:</p>
<ol>
<li>Clicked the link on 16:32:38(RT), a slight pause for 2s, then page loads showing 16:02:40(PT)</li>
</ol>
<h3 id="heading-keys-to-emphasize">Keys to Emphasize</h3>
<ol>
<li><strong>getServerSideProps function</strong>, this function is the key indicator that a page is using Server-Side Rendering.</li>
<li><strong>DELAY before render, and no LOADING indicator</strong>, the data is fetched before the page is rendered, so there will be a slight <strong>delay</strong> where the <strong>API is being hit</strong> at the moment, then it will show the page without loading indicator</li>
<li><strong>Data is fetched on every page request</strong>, which is why the time shown is different for each reloads.</li>
</ol>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/1-ssr-illustration_gnjcbe" alt="1-ssr-illustration" /></p>
<hr />
<h2 id="heading-csr-vs-ssr">CSR vs SSR</h2>
<p>Here is the difference between CSR vs SSR, keep an eye on <strong>delay</strong> and <strong>loading</strong> indicators.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/CSRvsSSR_fkzhia" alt="CSRvsSSR" /></p>
<p>Video Description:</p>
<ol>
<li>When clicking CSR, with no delay a LOADING text is visible for a second, then the Preview Time loads.</li>
<li>When clicking SSR, a slight delay happened, then the page loads.</li>
</ol>
<h3 id="heading-keys-to-emphasize">Keys to Emphasize</h3>
<ol>
<li><strong>CSR hit the API after the page loads.</strong></li>
<li><strong>SSR hit the API before the page loads.</strong></li>
</ol>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/2-csr-vs-ssr_seegng" alt="2-csr-vs-ssr" /></p>
<h3 id="heading-short-addition">Short addition</h3>
<p>I will probably create a new post about the pros and cons of each method, but when using <strong>CSR</strong> the SEO is not really great because the data is only fetched after the page renders. This is useful and convenient when we are creating a page with a gated authentication, as you don't really need SEO for pages like the dashboard, edit profile page, etc.</p>
<p>But, for the SSR, although it creates a delay, data that was fetched is injected and helps SEO. This is quite useful for a thread or post that we need to get traffic into, like Reddit or some sort.</p>
<hr />
<h2 id="heading-static-site-generation-ssg">Static Site Generation (SSG)</h2>
<p>Special function: <code>getStaticProps</code></p>
<p><a target="_blank" href="https://next-render.theodorusclarence.com/render/ssg">Demo Site</a></p>
<h3 id="heading-code-example">Code Example</h3>
<pre><code class="lang-ts"><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">SSGPage</span>(<span class="hljs-params">{ dateTime }: SSGPageProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;main&gt;
      &lt;TimeSection dateTime={dateTime} /&gt;
    &lt;/main&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getStaticProps: GetStaticProps = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://worldtimeapi.org/api/ip'</span>);

  <span class="hljs-keyword">return</span> {
    props: { dateTime: res.data.datetime },
  };
};
</code></pre>
<h3 id="heading-demo">Demo</h3>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/SSG_v1uyiy" alt="SSG" /></p>
<p>Video Description:</p>
<ol>
<li>Preview Time is showing 13:39:36(PT). But the real-time is 16:16:59(RT), about 3 hours late.</li>
<li>Reloading and going back and forth to the home page did not change anything.</li>
</ol>
<h3 id="heading-keys-to-emphasize">Keys to Emphasize</h3>
<ol>
<li><strong>getStaticProps function</strong>, this function is the key indicator that a page is using Static Site Generation.</li>
<li><strong>Fetched when running</strong> <code>yarn build</code>, the API will be hit <strong>ONLY</strong> when the application is building. This is why the time is at 13:39, while the real-time is 16:17.</li>
<li><strong>Data will not change because no further fetch</strong>, which is why the time shown is the same for each reloads.</li>
</ol>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/3-ssg-illustration_bzfog3" alt="3-ssg-illustration" /></p>
<hr />
<h2 id="heading-incremental-static-regeneration">Incremental Static Regeneration</h2>
<p>Special function: <code>getStaticProps</code> + <code>revalidate</code></p>
<p><a target="_blank" href="https://next-render.theodorusclarence.com/render/isr-20">Demo Site</a></p>
<h3 id="heading-code-example">Code Example</h3>
<pre><code class="lang-ts"><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">ISR20Page</span>(<span class="hljs-params">{ dateTime }: ISR20PageProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;main&gt;
      &lt;TimeSection dateTime={dateTime} /&gt;
    &lt;/main&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getStaticProps: GetStaticProps = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://worldtimeapi.org/api/ip'</span>);

  <span class="hljs-keyword">return</span> {
    props: { dateTime: res.data.datetime },
    revalidate: <span class="hljs-number">20</span>,
  };
};
</code></pre>
<h3 id="heading-demo">Demo</h3>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/ISR_nhhlaa" alt="ISR" /></p>
<p>Disclaimer: Revalidate time is set to 20 seconds.</p>
<p>Video Description:</p>
<ol>
<li>At first, it was 16:40:12(PT), and real-time when reloading is 16:40:25(RT) and 16:40:29(RT). In those 2 reload, Preview Time (PT) did not change.</li>
<li>Then, when 16:40:32(RT) (20s after initial), reload is done twice, the first time on 16:40:36(RT) and 16:40:40(RT). The Preview Time change to 16:40:37(PT) after the <strong>second reload.</strong></li>
</ol>
<h3 id="heading-keys-to-emphasize">Keys to Emphasize</h3>
<p>Now, this is might be confusing for you, but here is the key I want you to look at.</p>
<ol>
<li><strong>When in a 20-second cooldown span–16:40:12(RT) - 16:40:32(RT),</strong> reloading doesn't trigger changes. This is because the page is in a <strong>cooldown</strong> state, as we set on the <code>revalidate</code> key.</li>
<li><strong>After the 20-second cooldown–16:40:32(RT),</strong> we did 2 reloads.<ol>
<li>First Reload at 16:40:36(RT), we know that it is not on the cooldown state anymore. The first visit after the cooldown state is <strong>off,</strong> is going to trigger <strong>page rebuild</strong>. Page rebuild meaning, only this certain page is going to be rebuild. Not the whole application. The fetch API will run in the background, but there will be <strong>no changes</strong> on the Preview Time</li>
<li>Second Full Reload at 16:40:40(RT), the Preview Time change to 16:40:37(PT). Exactly a second after the page rebuild (which means the rebuild takes about 1s). This second reload is going to serve that rebuilt page from the previous reload.</li>
</ol>
</li>
</ol>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/4-cooldown-on-isr_uvkhlm" alt="4-cooldown-on-isr" /></p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/5-cooldown-off-isr_ximpem" alt="5-cooldown-off-isr" /></p>
<h3 id="heading-revisiting-page-vs-full-reload">Revisiting Page vs Full Reload</h3>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/isr-revisit-reload_oi7buw" alt="isr-revisit-reload" /></p>
<p>Terms:</p>
<ol>
<li>Revisiting pages → navigating using next/link (going back to home, then to that page again)</li>
<li>Full reload → doing reload at a website (command+r)</li>
</ol>
<p>Video Description:</p>
<ol>
<li>Revisiting pages at the first time 18:38:45(RT), will <strong>trigger rebuild,</strong> but after the <strong>second</strong> revisit, the Preview Time <strong>did not change</strong>.</li>
<li>After a <strong>full reload,</strong> then Preview Time is changed to 18:38:45(PT)</li>
</ol>
<p>Note:</p>
<ol>
<li><strong>The first reload does not have to be a full reload</strong>, we can go back to the home page, then to that page again (revisit), it will trigger the rebuild as long as we are not in the cooldown state.</li>
<li><strong>But, the second reload must be a full reload.</strong> Going back to the home page, then to the page again won't change the new Preview Time.</li>
</ol>
<p>Now, this is a case where we are assuming that only 1 person is accessing the website. But, that reloads will happen <strong>every person</strong> visit while still respecting the cooldown state.</p>
<h3 id="heading-is-it-going-to-be-rebuilt-every-20s-then">Is it going to be rebuilt every 20s then?</h3>
<p><strong>Nope.</strong></p>
<p>When the cooldown is off, if no one visits the page, then that page <strong>will not rebuild</strong>, even after long past the 20s.</p>
<p>But, the <strong>first</strong> person that visits when the <strong>cooldown state is off</strong>, is going to <strong>trigger a rebuild.</strong> That person won't be seeing changes. But, the changes will be served for <strong>the next full reload</strong>.</p>
<p><img src="https://res.cloudinary.com/theodorusclarence/image/upload/q_auto,f_auto/theodorusclarence/blogs/nextjs-fetch-method/6-cooldown-illustration_kdwbe5" alt="6-cooldown-illustration" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That's all, folks!</p>
<p>If you have understood this material, I suggest you to read more about <a target="_blank" href="https://theodorusclarence.com/blog/nextjs-fetch-usecase">How to choose between them</a>. I provide 4 metrics for you to consider and some example!</p>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=hashnode">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[Step by step: How to setup Storybook with Next.js and Tailwind CSS]]></title><description><![CDATA[Introduction
Storybook is a great way to maintain and preview isolated components. I usually add it as a 'nice-to-have' feature. During set up, I found that the resource about setting up a storybook with Next.js and Tailwind CSS is scarce. There are ...]]></description><link>https://hashnode.theodorusclarence.com/step-by-step-how-to-setup-storybook-with-nextjs-and-tailwind-css</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/step-by-step-how-to-setup-storybook-with-nextjs-and-tailwind-css</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Tue, 11 Jan 2022 03:19:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/YLSwjSy7stw/upload/v1641876692204/vI92GhOfO.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Storybook is a great way to maintain and preview isolated components. I usually add it as a 'nice-to-have' feature. During set up, I found that the resource about setting up a storybook with Next.js and Tailwind CSS is <strong>scarce</strong>. There are a bunch of bugs, and finding the solution is like finding a needle in a haystack. Hopefully, this tutorial can help you set it up smoothly.</p>
<h2 id="heading-version">Version</h2>
<p>This step-by-step tutorial is written with <strong>Storybook v6.4.9 &amp; TypeScript</strong>, if you found the tutorial is not working after some minor update, please leave a comment below.</p>
<h2 id="heading-initializing-storybook">Initializing Storybook</h2>
<p>This command will install the storybook to your Next.js repository, there will be a prompt asking if you want to additionally install an eslint plugin, I suggest you accept.</p>
<pre><code class="lang-bash">npx -y sb init --builder webpack5
</code></pre>
<h2 id="heading-installing-postcss-add-ons">Installing PostCSS Add-ons</h2>
<p>Tailwind CSS needs PostCSS to work, so we need to integrate it with Storybook using one of their pre-built add-ons.</p>
<pre><code class="lang-bash">yarn add -D @storybook/addon-postcss
</code></pre>
<h2 id="heading-adding-webpack-as-a-resolution-dependency">Adding Webpack as a resolution dependency</h2>
<p>We need this to ensure the webpack is installed as a dependency, somehow this will cause a bug if we don’t install it</p>
<p>Append this to your package.json</p>
<pre><code class="lang-json"><span class="hljs-comment">// package.json</span>

<span class="hljs-string">"resolutions"</span>: {
    <span class="hljs-attr">"webpack"</span>: <span class="hljs-string">"^5"</span>
}
</code></pre>
<p>Then install the webpack resolutions with</p>
<pre><code class="lang-json">yarn
</code></pre>
<h2 id="heading-replace-storybookmainjs">Replace <code>.storybook/main.js</code></h2>
<p>Here is the custom main.js config that you can use</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// .storybook/main.js</span>

<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">stories</span>: [<span class="hljs-string">'../src/**/*.stories.mdx'</span>, <span class="hljs-string">'../src/**/*.stories.@(js|jsx|ts|tsx)'</span>],
  <span class="hljs-attr">staticDirs</span>: [<span class="hljs-string">'../public'</span>],
  <span class="hljs-attr">addons</span>: [
    <span class="hljs-string">'@storybook/addon-links'</span>,
    <span class="hljs-string">'@storybook/addon-essentials'</span>,
    {
      <span class="hljs-comment">/**
       * <span class="hljs-doctag">NOTE:</span> fix Storybook issue with PostCSS@8
       * <span class="hljs-doctag">@see </span>https://github.com/storybookjs/storybook/issues/12668#issuecomment-773958085
       */</span>
      <span class="hljs-attr">name</span>: <span class="hljs-string">'@storybook/addon-postcss'</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">postcssLoaderOptions</span>: {
          <span class="hljs-attr">implementation</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'postcss'</span>),
        },
      },
    },
  ],
  <span class="hljs-attr">core</span>: {
    <span class="hljs-attr">builder</span>: <span class="hljs-string">'webpack5'</span>,
  },
  <span class="hljs-attr">webpackFinal</span>: <span class="hljs-function">(<span class="hljs-params">config</span>) =&gt;</span> {
    <span class="hljs-comment">/**
     * Add support for alias-imports
     * @see https://github.com/storybookjs/storybook/issues/11989#issuecomment-715524391
     */</span>
    config.resolve.alias = {
      ...config.resolve?.alias,
      <span class="hljs-string">'@'</span>: [path.resolve(__dirname, <span class="hljs-string">'../src/'</span>), path.resolve(__dirname, <span class="hljs-string">'../'</span>)],
    };

    <span class="hljs-comment">/**
     * Fixes font import with /
     * @see https://github.com/storybookjs/storybook/issues/12844#issuecomment-867544160
     */</span>
    config.resolve.roots = [
      path.resolve(__dirname, <span class="hljs-string">'../public'</span>),
      <span class="hljs-string">'node_modules'</span>,
    ];

    <span class="hljs-keyword">return</span> config;
  },
};
</code></pre>
<p>I've provided some comments about the bug fixes with the link.</p>
<p>Confirm the <code>stories</code> and <code>staticDirs</code> if they match up with your folder structure. By specifying <code>staticDirs</code> we can use assets from public folders.</p>
<h2 id="heading-replace-storybookpreviewjs">Replace <code>.storybook/preview.js</code></h2>
<pre><code class="lang-jsx"><span class="hljs-comment">// .storybook/preview.js</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'../src/styles/globals.css'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> NextImage <span class="hljs-keyword">from</span> <span class="hljs-string">'next/image'</span>;

<span class="hljs-keyword">const</span> OriginalNextImage = NextImage.default;

<span class="hljs-built_in">Object</span>.defineProperty(NextImage, <span class="hljs-string">'default'</span>, {
  <span class="hljs-attr">configurable</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">value</span>: <span class="hljs-function">(<span class="hljs-params">props</span>) =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">OriginalNextImage</span> {<span class="hljs-attr">...props</span>} <span class="hljs-attr">unoptimized</span> /&gt;</span></span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> parameters = {
  <span class="hljs-attr">actions</span>: { <span class="hljs-attr">argTypesRegex</span>: <span class="hljs-string">'^on[A-Z].*'</span> },
  <span class="hljs-attr">controls</span>: {
    <span class="hljs-attr">matchers</span>: {
      <span class="hljs-attr">color</span>: <span class="hljs-regexp">/(background|color)$/i</span>,
      date: <span class="hljs-regexp">/Date$/</span>,
    },
  },
  <span class="hljs-attr">previewTabs</span>: {
    <span class="hljs-string">'storybook/docs/panel'</span>: { <span class="hljs-attr">index</span>: <span class="hljs-number">-1</span> },
  },
};
</code></pre>
<p>I like the docs panel to be the default so I added the <code>previewTabs</code> config. You can remove it if you want.</p>
<p>This file will load the Tailwind CSS from <code>globals.css</code> and mock <code>NextImage</code> to work with Storybook.</p>
<h2 id="heading-running-the-storybook">Running the storybook</h2>
<p>Run <code>yarn storybook</code>to start up the dev server.</p>
<p>Then you can start adding some story, here is an example</p>
<h3 id="heading-story-example">Story example</h3>
<pre><code class="lang-tsx">// src/components/buttons/__stories__/Button.stories.tsx

import { ComponentMeta, ComponentStory } from '@storybook/react';
import * as React from 'react';
import { HiSearch } from 'react-icons/hi';

import Button from '@/components/buttons/Button';

export default {
  title: 'Components/Buttons/Button',
  component: Button,
  argTypes: {
    children: {
      control: { type: 'text' },
    },
  },
} as ComponentMeta&lt;typeof Button&gt;;

const Template: ComponentStory&lt;typeof Button&gt; = (args) =&gt; &lt;Button {...args} /&gt;;

export const Default = Template.bind({});
Default.args = {
  children: 'Button',
  variants: 'primary',
};

export const WithIcon = Template.bind({});
WithIcon.args = {
  children: (
    &lt;div className='flex gap-2 items-center'&gt;
      &lt;HiSearch /&gt;
      &lt;span&gt;Search&lt;/span&gt;
    &lt;/div&gt;
  ),
};
</code></pre>
<h3 id="heading-type-definition">Type Definition</h3>
<pre><code class="lang-tsx">type ButtonProps = {
  /** Button children element */
  children: React.ReactNode;
  /** Show loading spinner and disable button */
  isLoading?: boolean;
  /** Button color variant */
  variants?: 'primary' | 'secondary';
  /** Disable the button and add not-allowed cursor */
  disabled?: boolean;
} &amp; React.ComponentPropsWithoutRef&lt;'button'&gt;;
</code></pre>
<p>If you are using TypeScript, you can directly add JSDoc to the type definition, and it will automatically generate the props description 🤯</p>
<h3 id="heading-screenshot">Screenshot</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641876504799/q_7KCAMTU.png" alt="Image description" /></p>
<p>Awesome.</p>
<h2 id="heading-deploying-on-vercel">Deploying on Vercel</h2>
<p>To deploy on Vercel, you need to create a separate deployment with your Next.js page, then add this custom configuration</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641876506916/LLNtcH0ug.png" alt="Image description" /> </p>
<h2 id="heading-hate-config-use-my-starter-andamp-expansion">Hate config? Use my starter &amp; expansion</h2>
<p>This is a battery-packed starter with installable expansion just by using a single command in the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641876509336/zf-YF5htq.png" alt="Image description" /></p>
<p>Check out the <a target="_blank" href="https://github.com/theodorusclarence/ts-nextjs-tailwind-starter">ts-nextjs-tailwind-starter</a>!</p>
<h3 id="heading-storybook-expansion">Storybook Expansion</h3>
<p>I created this expansion to easily configure the project, this will do all of the above tutorials, suitable to be used with my starter, you <em>probably</em> can use it to an existing project. No promise ✌</p>
<pre><code class="lang-bash">curl -s https://raw.githubusercontent.com/theodorusclarence/expansion-pack/main/storybook/trigger.sh | bash -s
</code></pre>
<p><a target="_blank" href="https://github.com/theodorusclarence/expansion-pack#storybook">Expansion pack repository</a></p>
<h3 id="heading-plop-generator">Plop Generator</h3>
<p>The expansion pack is also equipped with a <a target="_blank" href="http://plopjs.com?ref=theodorusclarence.com">plop</a> generator to easily create a storybook file for your component</p>
<p>{% youtube t-MnpB7rOhg %}</p>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=devto">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=devto">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=devto">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=devto">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item><item><title><![CDATA[12 Features to Maximize Efficiency on Next.js & Tailwind CSS Projects]]></title><description><![CDATA[Introduction
I made the ts-nextjs-tailwind-starter after I got tired of setting up a new project and have to initialize Tailwind CSS every single time. After some months, this starter has grown and is filled with a lot of development automation and t...]]></description><link>https://hashnode.theodorusclarence.com/12-features-to-maximize-efficiency-on-nextjs-and-tailwind-css-projects</link><guid isPermaLink="true">https://hashnode.theodorusclarence.com/12-features-to-maximize-efficiency-on-nextjs-and-tailwind-css-projects</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Theodorus Clarence]]></dc:creator><pubDate>Tue, 11 Jan 2022 02:05:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/8_9Rix4OvrM/upload/v1641866649223/DM5OEJ85W.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>I made the <a target="_blank" href="https://github.com/theodorusclarence/ts-nextjs-tailwind-starter">ts-nextjs-tailwind-starter</a> after I got tired of setting up a new project and have to initialize Tailwind CSS every single time. After some months, this starter has grown and is filled with a lot of development automation and tools that help me when I'm developing.</p>
<p>This is something that I use every project init, features are carefully curated, and put into this repository.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/theodorusclarence/ts-nextjs-tailwind-starter">https://github.com/theodorusclarence/ts-nextjs-tailwind-starter</a></div>
<h2 id="heading-features">Features</h2>
<p>According to my list, these are all the features that I incorporate on ts-nextjs-tailwind-starter:</p>
<ul>
<li>⚡️ Next.js 12</li>
<li>⚛️ React 17</li>
<li>✨ TypeScript</li>
<li>💨 Tailwind CSS 3 — Configured with CSS Variables to extend the <strong>primary</strong> color</li>
<li>💎 Pre-built Components — Components that will <strong>automatically adapt</strong> with your brand color</li>
<li>🃏 Jest — Configured for unit testing</li>
<li>📈 Absolute Import and Path Alias — Import components using <code>@/</code> prefix</li>
<li>📏 ESLint — Find and fix problems in your code, also will <strong>auto-sort</strong> your imports</li>
<li>💖 Prettier — Format your code and <strong>Tailwind CSS classes</strong> consistently</li>
<li>🐶 Husky &amp; Lint Staged — Run scripts on your staged files before they are committed</li>
<li>🤖 Conventional Commit Lint — Make sure you &amp; your teammates follow the conventional commit</li>
<li>⏰ Standard Version Changelog — Generate your changelog using <code>yarn release</code></li>
<li>👷 Github Actions — Lint your code on PR</li>
<li>🚘 Automatic Branch and Issue Autolink — Branch will be automatically created on issue <strong>assigned</strong>, and auto-linked on PR</li>
<li>🔥 Snippets — A collection of useful snippets</li>
<li>👀 Default Open Graph — Awesome open graph generated using <a target="_blank" href="https://github.com/theodorusclarence/og">og.thcl.dev</a>, fork it, and deploy!</li>
<li>🗺 Site Map — Automatically generate sitemap.xml</li>
<li>📦 Expansion Pack — Easily install common libraries, additional components, and configs</li>
</ul>
<p>Quite a lot huh? I'm going to take an in-depth look at each feature and automation with this post.</p>
<hr />
<h2 id="heading-easy-initial-config">Easy Initial Config</h2>
<p>Don't you hate it when you use a starter, then you see some branding or default configs left out unchanged?</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vzsra4q233gng0yqgpuu.png" alt="Image description" /></p>
<p>I prepared a unique word that you can find, with some guide of what to override. You can remove the comments after you override them, and leave them if you haven't. Treat them as a to-do comment.</p>
<h2 id="heading-pre-built-components">Pre-built Components</h2>
<p>I prepared a set of components that is neutral and can be used to help boost your speed in development. These are components that have a <strong>high chance of being used</strong>, not just getting deleted after you finished cloning the repository.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=xawHHhqIVVo">https://www.youtube.com/watch?v=xawHHhqIVVo</a></div>
<p>All animations are configured to be <strong>motion-safe</strong>.</p>
<h3 id="heading-dont-like-the-theme">Don't like the theme?</h3>
<p>You can change it with CSS Variables. I prepared <strong>all Tailwind CSS colors</strong> converted to CSS Variables in the <code>styles/colors.css</code> file that you can copy and use.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqiyurl4atgh2rcaxtur.png" alt="Image description" /></p>
<p>See more details about components on the <a target="_blank" href="https://tsnext-tw.thcl.dev/components">demo page</a></p>
<h2 id="heading-seo-enhancement">SEO Enhancement</h2>
<p>Do you want your project to be indexed to search engines? Yeah, me too. I optimized the SEO by preparing a custom Seo component and adding <a target="_blank" href="https://www.npmjs.com/package/next-sitemap">next-sitemap</a>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkgbd4a1u6v3dbq4l4ev.png" alt="Image description" /></p>
<p>If you want to use the default meta tag, just add <code>&lt;Seo /&gt;</code> on top of your page.</p>
<p>You can also customize it per page by overriding the title, description as props</p>
<pre><code class="lang-tsx">&lt;Seo title='Next.js Tailwind Starter' description='your description' /&gt;
</code></pre>
<p>or if you want to still keep the site title like <code>%s | Next.js Tailwind Starter</code>, you can use <code>templateTitle</code> props.</p>
<h2 id="heading-minimal-dependencies">Minimal Dependencies</h2>
<p>I tried to keep the dependencies small, not the devDeps tho, you'll see why after you see a bunch of automation I create. Here are the 3 dependencies (excluding Next.js and React deps)</p>
<ol>
<li><a target="_blank" href="https://bundlephobia.com/package/clsx@latest">clsx</a>, a utility for constructing <code>className</code> strings conditionally.</li>
<li><a target="_blank" href="https://bundlephobia.com/package/react-icons@latest">react-icons</a>, easily import popular icon packs in SVG format.</li>
<li><a target="_blank" href="https://github.com/dcastil/tailwind-merge">tailwind-merge</a>, override tailwind CSS classes without using !important.</li>
</ol>
<p>The tailwind-merge can be used by importing <code>clsxm</code> from <code>@/lib/clsxm</code>. All of the pre-built components are using clsxm to ease the use of the reusable components.</p>
<p>Here is a thread that I made about tailwind-merge:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/th_clarence/status/1475685363003768836">https://twitter.com/th_clarence/status/1475685363003768836</a></div>
<h2 id="heading-absolute-import-andamp-path-alias">Absolute Import &amp; Path Alias</h2>
<pre><code class="lang-tsx">import Header from '../../../components/Header';

// simplified to

import Header from '@/components/Header';
</code></pre>
<p>Reduce the complexity of importing components by using absolute import, and a nice path alias to differentiate <strong>your</strong> <strong>code</strong> and <strong>external libraries.</strong></p>
<h2 id="heading-prettier-with-tailwind-css-class-sorter">Prettier with Tailwind CSS Class Sorter</h2>
<p>In this repository, I set it up so it will automatically sort class based on the custom config file. It even works with clsx or classnames! You don't need to ask your colleague to download another VS Code extension. All of it is bound to the repository.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=AJt5na1E7DE">https://www.youtube.com/watch?v=AJt5na1E7DE</a></div>
<h2 id="heading-eslint">ESLint</h2>
<p>This repository is prepared with ESLint to reduce human errors during development. Don't worry there won't be any annoying <strong>styling lint</strong> because all of it is taken care of with Prettier. Some cool features such as:</p>
<h3 id="heading-auto-import-sort-andamp-unused-import-removal">Auto Import Sort &amp; Unused Import Removal</h3>
<p>Don't you hate it when you have to revise your code because your reviewer told you to <strong>reorder imports</strong>? If they haven't automated it, do yourself a favor by recommending this starter.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=6W6FSJ6mq6c">https://www.youtube.com/watch?v=6W6FSJ6mq6c</a></div>
<h2 id="heading-husky-andamp-lint-staged">Husky &amp; Lint Staged</h2>
<p>There are 3 Husky hooks that will help you with the automation of:</p>
<ol>
<li><strong>pre-commit</strong>, run <strong>eslint check</strong> with 0 tolerance to warnings and errors and <strong>format the code</strong> using prettier</li>
<li><strong>commit-msg</strong>, run commitlint to ensure the use of the <a target="_blank" href="https://theodorusclarence.com/library/conventional-commit-readme">Conventional Commit</a> for commit messages</li>
<li><strong>post-merge</strong>, running <code>yarn</code> every <code>git pull</code> or after merging to ensure all new packages are installed and ready to go</li>
</ol>
<p>Oh right, you also don't have to wait that long because husky only the code that you <strong>stage</strong> (using <a target="_blank" href="https://github.com/okonet/lint-staged">lint-staged</a>).</p>
<p>What about the type check, or if the staged code made the other fail? Don't burden your efficiency, just chuck it into Github Actions to run in the background.</p>
<h2 id="heading-github-actions">Github Actions</h2>
<p>I love automation and I put some useful workflows that you can use.</p>
<h3 id="heading-type-check-whole-eslint-andamp-prettier">Type Check, Whole ESLint &amp; Prettier</h3>
<p>For the sake of efficiency, we don't run whole project checks on your local machine. That takes too long just to commit simple changes. We will run it on Github Actions instead, then you can continue working while waiting for it to complete.</p>
<p>Here are the complete lists that will be checked:</p>
<ol>
<li><strong>ESLint</strong> - will fail if there are any warnings and errors</li>
<li><strong>Type Check</strong> - will fail on <code>tsc</code> error</li>
<li><strong>Prettier Check</strong> - will fail if there are any formatting errors</li>
<li><strong>Test</strong> - will fail if there are any test failures</li>
</ol>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsacxdlt6t8ogshjfmrt.png" alt="Image description" /></p>
<p>Github also provides useful inline warnings in the Files Changed tab on the PR.</p>
<h3 id="heading-auto-create-branch">Auto Create Branch</h3>
<p>We can automatically create a branch from the latest main branch after you <strong>assign</strong> an issue.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=b2TWOdF2kW0">https://www.youtube.com/watch?v=b2TWOdF2kW0</a></div>
<p>This will create a <strong>consistent branch name</strong> with the issue number in front of them, and some issue context.</p>
<p>p.s. You have to install the app for your organization/account/repository from the <a target="_blank" href="https://github.com/marketplace/create-issue-branch">GitHub Marketplace</a> for this to work</p>
<h2 id="heading-auto-link-pr-to-issue">Auto Link PR to Issue</h2>
<p>We automated the branch creation, the lint &amp; the test process on the PR, what's next? Yep, linking PR to issue. That is <strong>super annoying</strong> and I always <strong>forgot</strong> to do it. We'll automate it using our consistent branch name.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=lOWtCVXq3os">https://www.youtube.com/watch?v=lOWtCVXq3os</a></div>
<p>It also provides a nice description of related issues, so your reviewer can check the issue first before reviewing.</p>
<h2 id="heading-open-graph-generator">Open Graph Generator</h2>
<p>I also provided an open graph application that you can <a target="_blank" href="https://github.com/theodorusclarence/og">fork and deploy to vercel</a> for <strong>free</strong>. It is automatically used with the SEO component and will generate a dynamic open graph based on the page title and description.</p>
<p>It defaults to my deployment, but please <strong>fork it</strong> and self-host. Because I might change the API without notice and could break your app's opengraph.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yaywheagpcp6ctbg8dgt.png" alt="Image description" /></p>
<p>You can play around with the API on <a target="_blank" href="https://og.thcl.dev/">og.thcl.dev</a>. You can even customize it with your own brand based on the repo!</p>
<h2 id="heading-snippets">Snippets</h2>
<p>Snippets are crucial if you want to make a consistent convention across the application. I prepared some snippets that can help you code faster and more effectively.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/atuyulv88dc3er39xzdq.png" alt="Image description" /></p>
<p>See more detail on <a target="_blank" href="https://github.com/theodorusclarence/ts-nextjs-tailwind-starter/blob/main/.vscode/typescriptreact.code-snippets">this file</a></p>
<h3 id="heading-regions">Regions</h3>
<p>You might notice the <code>#region</code> with green highlight comments. This is something that can be used with <code>reg</code> snippets. You can easily separate your logic into named regions, then fold them if they are insignificant.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9czf0va3v0ajndrybj6.png" alt="Image description" /></p>
<p>The lesser noise the better. You can also use <code>⌘K + ⌘8</code> to fold all regions.</p>
<h3 id="heading-snippets-wrap">Snippets Wrap</h3>
<p>This is something that I recently added because it is annoying to wrap a component with React Fragment or refactoring className with clsx. So I created a snippet pattern called <strong>Snippets Wrap</strong> that can help you with that.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=baCi6IfJzvo">https://www.youtube.com/watch?v=baCi6IfJzvo</a></div>
<h2 id="heading-expansion-pack">Expansion Pack</h2>
<p>I have to keep this starter minimal, but there are some libraries that I often use in every project. So I created a bash script to <strong>install, config, and add additional components</strong> to this starter.</p>
<p>Currently, there are some packs in the <a target="_blank" href="https://github.com/theodorusclarence/expansion-pack">expansion-pack repository</a></p>
<ul>
<li>React Hook Form + Form Input Components</li>
<li><a target="_blank" href="https://theodorusclarence.com/blog/nextjs-storybook-tailwind">Storybook</a></li>
<li>Cypress + workflow to run on Vercel preview on push</li>
<li><a target="_blank" href="https://theodorusclarence.com/blog/react-loading-state-pattern">Toast with React Query / SWR</a></li>
<li><a target="_blank" href="https://github.com/theodorusclarence/dialog-manager">Dialog Manager with Zustand</a></li>
<li>NProgress</li>
</ul>
<p>Here is a demo for the React Hook Form one</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=fna-ekaU0A4">https://www.youtube.com/watch?v=fna-ekaU0A4</a></div>
<p>Hit the terminal then grab some coffee. You're back with complete components also a <strong>sandbox</strong> page to see how to implement the library.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/theodorusclarence/expansion-pack">https://github.com/theodorusclarence/expansion-pack</a></div>
<p>For more demo, check out the <a target="_blank" href="https://github.com/theodorusclarence/expansion-pack">repository readme</a></p>
<h2 id="heading-star-the-repository">Star the repository</h2>
<p>Liking the features? This repository basically grows with me, so the features will go through changes and improvement. If you got anything in mind, leave a comment below, or <a target="_blank" href="https://github.com/theodorusclarence/ts-nextjs-tailwind-starter/discussions">open a discussion</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/theodorusclarence/ts-nextjs-tailwind-starter">https://github.com/theodorusclarence/ts-nextjs-tailwind-starter</a></div>
<p>Finally, I would be <strong>thrilled</strong> if you give a <strong>star</strong> to the repository.</p>
<hr />
<blockquote>
<p>Originally posted on <a target="_blank" href="https://theodorusclarence.com/?ref=hashnode">my personal site</a>, find more <a target="_blank" href="https://theodorusclarence.com/blog?ref=hashnode">blog posts</a> and <a target="_blank" href="https://theodorusclarence.com/library?ref=hashnode">code snippets library</a> I put up for easy access on my site 🚀</p>
</blockquote>
<p>Like this post? <a target="_blank" href="https://theodorusclarence.com/subscribe?ref=devto">Subscribe to my newsletter</a> to get notified every time a new post is out!</p>
]]></content:encoded></item></channel></rss>