<?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[imjulian]]></title><description><![CDATA[I'm an engineer that loves JavaScript technologies and Linux. In this blog, you'll find articles about the problems I faced and how I did to solve them, technologies I like, and my unpopular opinions.]]></description><link>https://blog.imjulian.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 09 Jun 2026 12:44:26 GMT</lastBuildDate><atom:link href="https://blog.imjulian.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[RBAC & Private Routes in Next.js with Clerk using NextShield.]]></title><description><![CDATA[Hello everyone! I'm Julián. 
 
The past few weeks  I've introduced my library called NextShield and show an  example using it with Supabase. Today's example is using  NextShield with Clerk, so let's get started 🚀.

Get a Clerk account.

Go to Clerk'...]]></description><link>https://blog.imjulian.com/nextshield-clerk</link><guid isPermaLink="true">https://blog.imjulian.com/nextshield-clerk</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Clerk.dev]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Jorge Acero]]></dc:creator><pubDate>Thu, 21 Oct 2021 16:04:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1634187120822/mCCWvnP55.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello everyone! I'm Julián. 
<img src="https://media.giphy.com/media/hvRJCLFzcasrR4ia7z/giphy.gif" alt="hello" /> </p>
<p>The past few weeks  <a target="_blank" href="https://blog.imjulian.com/rbac-and-private-routes-in-nextjs">I've introduced my library called NextShield</a> and show an  <a target="_blank" href="https://blog.imjulian.com/nextshield-supabase">example using it with Supabase</a>. Today's example is using  <a target="_blank" href="https://imjulianeral.github.io/next-shield/">NextShield</a> with <a target="_blank" href="https://clerk.dev/">Clerk</a>, so let's get started 🚀.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634784544155/1IPJBFbPT.gif" alt="writing fast.gif" /></p>
<h2 id="get-a-clerk-account">Get a Clerk account.</h2>
<ul>
<li>Go to <a target="_blank" href="https://clerk.dev/">Clerk's site</a> and click on <code>Sign Up</code>:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634785092097/d8LKr7PsR.png" alt="clerk sign up.png" /></p>
<ul>
<li>And create your account:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634785142446/2p7EAYAt1.png" alt="clerk create account.png" /></p>
<h2 id="create-a-clerk-app">Create a Clerk App.</h2>
<ul>
<li>Go to your Dashboard and click on <code>Create application</code>:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634785553405/l0fFHlsrr.png" alt="create app" /></p>
<ul>
<li>Choose <code>Standard Setup</code>:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634785651355/AxZTRHqpw.png" alt="setup" /></p>
<ul>
<li>Put your App's name and choose a color:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634785877324/_A3Ij7Gjw.png" alt="app name" /></p>
<p>That's it, let's create a Next.js app with NextShield.</p>
<h2 id="create-a-nextjs-app-with-nextshield">Create a Next.js App with NextShield.</h2>
<p>For this tutorial you need to create a Next.js app configured with NextShield, to do so, you can follow the instructions of <a target="_blank" href="https://blog.imjulian.com/rbac-and-private-routes-in-nextjs">my first post</a> or <a target="_blank" href="https://github.com/imjulianeral/nextshield-examples">just clone the repo</a> (clone the main branch). </p>
<p>That's it, let's set up the NextShield with Clerk.</p>
<h2 id="set-up-nextshield-with-clerk">Set Up NextShield with Clerk.</h2>
<h3 id="instal-dependencies">Instal Dependencies.</h3>
<ul>
<li>First install the clerk package:</li>
</ul>
<pre><code class="lang-bash">npm i @clerk/nextjs
</code></pre>
<h3 id="get-api-key">Get API Key.</h3>
<ul>
<li>Then go to your dashboard and select your app:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634787401999/8N1JyBjkP.png" alt="dashboard" /></p>
<ul>
<li>Select the development environment:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634787480217/kzIewAuXr.png" alt="dev env" /></p>
<ul>
<li>Scroll down a little bit to see the <code>Instance Configuration</code> section to get the <code>Frontend API</code> key:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634787671424/U8uspNWqfM.png" alt="frontend API key" /></p>
<ul>
<li>Go to your Next.js app and create a <code>.env.local</code> file at the root of the project and setup the following variable with your key:</li>
</ul>
<pre><code class="lang-env">NEXT_PUBLIC_CLERK_FRONTEND_API="FRONTEND API KEY HERE"
</code></pre>
<h3 id="set-urls">Set URLs.</h3>
<ul>
<li>Now go back to your Clerk app and click on <code>Settings -&gt; URL &amp; Redirects -&gt; Component URLs</code>:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634788519463/HGkYQ6Psu.png" alt="redirects.png" /></p>
<p>Enable <code>Use Custom URL</code> on each field and write the following values:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634788690105/MXs7xaarX.png" alt="custom url" /></p>
<h3 id="auth-providers">Auth Providers.</h3>
<ul>
<li>Also in <code>Settings -&gt; User management</code> select your preferred providers.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634788885730/69Md8lPaL.png" alt="providers" /></p>
<h3 id="bind-nextjs-and-clerk">Bind Next.js &amp; Clerk.</h3>
<ul>
<li>Open your file <code>_app.tsx</code> and put the <code>ClerkProvider</code>:</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> type { AppProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/app'</span>

<span class="hljs-keyword">import</span> { ClerkProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</span>

<span class="hljs-keyword">import</span> { Shield } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Shield'</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'@/styles/globals.css'</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">MyApp</span>(<span class="hljs-params">{ Component, pageProps }: AppProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ClerkProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Shield</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Shield</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ClerkProvider</span>&gt;</span></span>
  )
}
</code></pre>
<ul>
<li>Update the <code>pages</code> directory with the following folder structure:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634789619283/Hqz5hMbkS.png" alt="pages directory" /></p>
<ul>
<li>Also update the <code>Nav</code> component adding the sign in &amp; out routes:</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">'next/link'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Nav</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/sign-up"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Sign Up<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/sign-in"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Sign In<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/pricing"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Pricing<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/profile"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Profile<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/users"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Users<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/users/1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Single User<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  )
}
</code></pre>
<ul>
<li>Add the following content for the <code>index.tsx</code> and <code>pricing.tsx</code> files:</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</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">PageName</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"PageName"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>PageName<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<ul>
<li>For the <code>dashboard.tsx</code>, <code>users/index.tsx</code> and <code>users/[id].tsx</code> files, update the content to:</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { UserButton } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</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">PageName</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"PageName"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>PageName<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UserButton</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">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<p>Finally, add the Clerk components on the pages that we defined in our dashboard:</p>
<ul>
<li>For <code>sign-up/[[...index]].tsx</code>:</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { SignUp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</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">SignUpPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SignUp</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/sign-up"</span> <span class="hljs-attr">routing</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">signInUrl</span>=<span class="hljs-string">"/sign-in"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<ul>
<li>For <code>sign-in/[[...index]].tsx</code>:</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { SignIn } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</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">SignInPage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SignIn</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/sign-in"</span> <span class="hljs-attr">routing</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">signUpUrl</span>=<span class="hljs-string">"/sign-up"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p>And the <code>profile/[[...index]].tsx</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { UserProfile } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/clerk-react'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Profile</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Profile"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">UserProfile</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/profile"</span> <span class="hljs-attr">routing</span>=<span class="hljs-string">"path"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<h3 id="bind-nextshield-and-clerk">Bind NextShield &amp; Clerk.</h3>
<p>Finally the interesting part 🥳.</p>
<ul>
<li>Go to the <code>Shield</code> component and import:</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</span>
</code></pre>
<ul>
<li>Use this hook passing the parameter <code>withAssertions</code> set to <code>true</code>, this is because in that way we can access to <code>isLoading</code> and <code>isSignedIn</code> functions, which are must to have in NextShield parameters.</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { user, isLoading, isSignedIn } = useUser({ <span class="hljs-attr">withAssertions</span>: <span class="hljs-literal">true</span> })
</code></pre>
<ul>
<li>Then update the <code>NextShieldProps</code> with the following:</li>
</ul>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/sign-in'</span>, <span class="hljs-string">'/sign-up'</span>]
  &gt; = {
    router,
    <span class="hljs-attr">isAuth</span>: isSignedIn(user),
    <span class="hljs-attr">isLoading</span>: isLoading(user),
    <span class="hljs-attr">privateRoutes</span>: [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    <span class="hljs-attr">publicRoutes</span>: [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/sign-in'</span>, <span class="hljs-string">'/sign-up'</span>],
    <span class="hljs-attr">hybridRoutes</span>: [<span class="hljs-string">'/pricing'</span>],
    <span class="hljs-attr">loginRoute</span>: <span class="hljs-string">'/sign-in'</span>,
    <span class="hljs-attr">accessRoute</span>: <span class="hljs-string">'/dashboard'</span>,
    <span class="hljs-attr">LoadingComponent</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loading</span> /&gt;</span></span>,
  }
</code></pre>
<ul>
<li>The complete component should look like this:</li>
</ul>
<pre><code class="lang-jsx"><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> { NextShield, NextShieldProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-shield'</span>
<span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</span>

<span class="hljs-keyword">import</span> { Children } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/Components'</span>
<span class="hljs-keyword">import</span> { Loading } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Loading'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Shield</span>(<span class="hljs-params">{ children }: Children</span>) </span>{
  <span class="hljs-keyword">const</span> router = useRouter()
  <span class="hljs-keyword">const</span> { user, isLoading, isSignedIn } = useUser({ <span class="hljs-attr">withAssertions</span>: <span class="hljs-literal">true</span> })

  <span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/sign-in'</span>, <span class="hljs-string">'/sign-up'</span>]
  &gt; = {
    router,
    <span class="hljs-attr">isAuth</span>: isSignedIn(user),
    <span class="hljs-attr">isLoading</span>: isLoading(user),
    <span class="hljs-attr">privateRoutes</span>: [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    <span class="hljs-attr">publicRoutes</span>: [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/sign-in'</span>, <span class="hljs-string">'/sign-up'</span>],
    <span class="hljs-attr">hybridRoutes</span>: [<span class="hljs-string">'/pricing'</span>],
    <span class="hljs-attr">loginRoute</span>: <span class="hljs-string">'/sign-in'</span>,
    <span class="hljs-attr">accessRoute</span>: <span class="hljs-string">'/dashboard'</span>,
    <span class="hljs-attr">LoadingComponent</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loading</span> /&gt;</span></span>,    
  }

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NextShield</span> {<span class="hljs-attr">...shieldProps</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">NextShield</span>&gt;</span></span>
}
</code></pre>
<ul>
<li>By now, you should have something like this, you only have access to public and hybrid routes when you're logged out:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634792258670/ug_SeY_yR.gif" alt="logout.gif" /></p>
<ul>
<li>And when logged in you have access to private and hybrid routes, the public routes are inaccessible:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634792460423/V0gvh7kIb.gif" alt="login.gif" /></p>
<p>Let's move things further and add RBAC.</p>
<h3 id="add-rbac">Add RBAC.</h3>
<ul>
<li>Go to <code>Users</code> section on your dashboard and select your user:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634792606501/SsWP0waSS.png" alt="users" /></p>
<ul>
<li>Scroll down until the <code>Metadata</code> section and edit the public data:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634792785259/7lldn9coK.png" alt="metadata" /></p>
<ul>
<li>Add the following content and save the changes:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634792920743/BHWjBvkwk.png" alt="role" /></p>
<ul>
<li>Go back to the Next.js app and create a new type under <code>types/User.ts</code>:</li>
</ul>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> Metadata =
  | {
      role?: <span class="hljs-built_in">string</span>
    }
  | <span class="hljs-literal">undefined</span>
</code></pre>
<ul>
<li>That's it, import the type in the <code>Shield</code> component, and create a constant with that type:</li>
</ul>
<pre><code class="lang-ts">  <span class="hljs-keyword">const</span> { user, isLoading, isSignedIn } = useUser({ withAssertions: <span class="hljs-literal">true</span> })
  <span class="hljs-keyword">const</span> userMetadata: Metadata = user?.publicMetadata
</code></pre>
<ul>
<li>And define the roles in NextShield 😎</li>
</ul>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/sign-in'</span>, <span class="hljs-string">'/sign-up'</span>]
  &gt; = {
    router,
    isAuth: isSignedIn(user),
    isLoading: isLoading(user),
    privateRoutes: [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    publicRoutes: [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/sign-in'</span>, <span class="hljs-string">'/sign-up'</span>],
    hybridRoutes: [<span class="hljs-string">'/pricing'</span>],
    loginRoute: <span class="hljs-string">'/sign-in'</span>,
    LoadingComponent: &lt;Loading /&gt;,
    RBAC: {
      ADMIN: {
        grantedRoutes: [<span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
        accessRoute: <span class="hljs-string">'/dashboard'</span>,
      },
      EMPLOYEE: {
        grantedRoutes: [<span class="hljs-string">'/profile/[[...index]]'</span>, <span class="hljs-string">'/dashboard'</span>],
        accessRoute: <span class="hljs-string">'/profile/[[...index]]'</span>,
      },
    },
    userRole: userMetadata?.role,
  }
</code></pre>
<ul>
<li>The final app should look like this:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634793668894/n3y7XDExf.gif" alt="rbac.gif" /></p>
<h2 id="protect-your-componentes">Protect your componentes.</h2>
<p>You can move things one step further and protect your components depending on the user's role or a custom condition, even if that user has access to that route.</p>
<p>Test this code in your pricing page and tell me what you think in the comments 😏:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/clerk-react'</span>
<span class="hljs-keyword">import</span> { ComponentShield } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-shield'</span>

<span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/User'</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">Pricing</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { user, isLoading, isSignedIn } = useUser({ <span class="hljs-attr">withAssertions</span>: <span class="hljs-literal">true</span> })
  <span class="hljs-keyword">const</span> userMetadata: Metadata = user?.publicMetadata

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Pricing"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Pricing<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">ComponentShield</span>
        <span class="hljs-attr">showIf</span>=<span class="hljs-string">{isSignedIn(user)</span> &amp;&amp; !<span class="hljs-attr">isLoading</span>(<span class="hljs-attr">user</span>)}
        <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">p</span>&gt;</span>You are unauthorized<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}
      &gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>You are authorized<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ComponentShield</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">ComponentShield</span> <span class="hljs-attr">RBAC</span> <span class="hljs-attr">showForRole</span>=<span class="hljs-string">"ADMIN"</span> <span class="hljs-attr">userRole</span>=<span class="hljs-string">{userMetadata?.role}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>You are an admin<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ComponentShield</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<h2 id="conclusion">Conclusion.</h2>
<p>NextShield &amp; Clerk make a perfect symbiosis!!!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634794075204/_O8HVgXJ3.gif" alt="venom.gif" /></p>
<ul>
<li>Real-time role change.</li>
<li>Redirects automatically.</li>
<li>Absolute 0 flashy content.</li>
<li>Perfectly protected app.</li>
</ul>
<p>You can get the completed example <a target="_blank" href="https://github.com/imjulianeral/nextshield-examples/tree/clerk">here</a>.</p>
<p> <a target="_blank" href="https://blog.imjulian.com/series/next-shield">Also you can read the complete NextShield series.
</a> </p>
<h1 id="bye">Bye!</h1>
<p>Hope you like it, see you next time!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634794616734/jIgQIdPAq.gif" alt="bye.gif" /></p>
]]></content:encoded></item><item><title><![CDATA[RBAC & Private Routes in Next.js with Supabase using NextShield.]]></title><description><![CDATA[Hello everyone! Hope you liked the  previous post. Today we are going to build a Supabase example with NextShield, so let's get started.

Create a new Supabase project.
Go to  supabase website  and click on "Start your project":

Sign in and click on...]]></description><link>https://blog.imjulian.com/nextshield-supabase</link><guid isPermaLink="true">https://blog.imjulian.com/nextshield-supabase</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[#hacktoberfest ]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Jorge Acero]]></dc:creator><pubDate>Thu, 07 Oct 2021 17:00:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1633584129074/vXvVAdghc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello everyone! Hope you liked the  <a target="_blank" href="https://blog.imjulian.com/rbac-and-private-routes-in-nextjs">previous post</a>. Today we are going to build a Supabase example with NextShield, so let's get started.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633614589667/db5LONWSl.gif" alt="sweet dreams" /></p>
<h2 id="create-a-new-supabase-project">Create a new Supabase project.</h2>
<p>Go to  <a target="_blank" href="https://supabase.io/">supabase website</a>  and click on "Start your project":</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633572218920/-pXCamGWI.png" alt="image.png" /></p>
<p>Sign in and click on "New Project":</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633572367869/5shF50ZfB.png" alt="image.png" /></p>
<p>Fill the form and create the project:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633572504955/UTDygYDla.png" alt="image.png" /></p>
<p>Wait until the project is created:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633572611215/zqInPrVP9.png" alt="image.png" /></p>
<h3 id="configure-supabase">Configure Supabase.</h3>
<p>We need to configure supabase to satisfy our needs with RBAC, so go to the SQL menu and click on new query:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633574196477/QkOUgbxVX.png" alt="image.png" /></p>
<p>Create the profiles table:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> public.profiles (
  <span class="hljs-keyword">id</span> <span class="hljs-keyword">uuid</span> <span class="hljs-keyword">references</span> auth.users <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
  <span class="hljs-keyword">role</span> <span class="hljs-built_in">text</span>,

  primary <span class="hljs-keyword">key</span> (<span class="hljs-keyword">id</span>)
);
</code></pre>
<p>And enable row level security to restrict the access:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">alter</span> <span class="hljs-keyword">table</span> public.profiles <span class="hljs-keyword">enable</span> <span class="hljs-keyword">row</span> <span class="hljs-keyword">level</span> <span class="hljs-keyword">security</span>;
</code></pre>
<p>Then add the following policies to grant user access to their data:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Users can insert their own profile."</span>
  <span class="hljs-keyword">on</span> <span class="hljs-keyword">profiles</span> <span class="hljs-keyword">for</span> <span class="hljs-keyword">insert</span>
  <span class="hljs-keyword">with</span> <span class="hljs-keyword">check</span> ( auth.uid() = <span class="hljs-keyword">id</span> );

<span class="hljs-keyword">create</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Users can update own profile."</span>
  <span class="hljs-keyword">on</span> <span class="hljs-keyword">profiles</span> <span class="hljs-keyword">for</span> <span class="hljs-keyword">update</span>
  <span class="hljs-keyword">using</span> ( auth.uid() = <span class="hljs-keyword">id</span> );
</code></pre>
<p>Finally, create a trigger to insert a new record when a new user is created:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- inserts a row into public.profiles</span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">function</span> public.handle_new_user() 
<span class="hljs-keyword">returns</span> <span class="hljs-keyword">trigger</span> 
<span class="hljs-keyword">language</span> plpgsql 
<span class="hljs-keyword">security</span> definer 
<span class="hljs-keyword">as</span> $$
<span class="hljs-keyword">begin</span>
  <span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> public.profiles (<span class="hljs-keyword">id</span>, <span class="hljs-keyword">role</span>)
  <span class="hljs-keyword">values</span> (new.id, <span class="hljs-string">"EMPLOYEE"</span>);
  return new;
<span class="hljs-keyword">end</span>;
$$;

<span class="hljs-comment">-- trigger the function every time a user is created</span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">trigger</span> on_auth_user_created
  <span class="hljs-keyword">after</span> <span class="hljs-keyword">insert</span> <span class="hljs-keyword">on</span> auth.users
  <span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> <span class="hljs-keyword">row</span> <span class="hljs-keyword">execute</span> <span class="hljs-keyword">procedure</span> public.handle_new_user();
</code></pre>
<p><strong>For a real project, you want this trigger to be well-tested because if it fails it could block the user sign ups.</strong></p>
<h2 id="create-a-new-nextjs-project">Create a new Next.js Project:</h2>
<p>To avoid writing boilerplate code you can use the project that we wrote in the <a target="_blank" href="https://blog.imjulian.com/rbac-and-private-routes-in-nextjs">previous post</a>, but if you didn't follow that one, you can just <a target="_blank" href="https://github.com/imjulianeral/nextshield-examples">clone the repo (clone the main branch)</a>.</p>
<h3 id="install-dependencies">Install Dependencies.</h3>
<pre><code class="lang-shell">npm i next-shield @supabase/supabase-js valtio
</code></pre>
<h3 id="change-the-styles">Change the styles.</h3>
<p>Replace the code under <code>styles/globals.css</code> with:</p>
<pre><code class="lang-css">* {
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">html</span>,
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
    Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  <span class="hljs-attribute">background-color</span>: black;
  <span class="hljs-attribute">color</span>: white;
}

<span class="hljs-selector-tag">nav</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">min-width:</span> <span class="hljs-number">768px</span>) {
  <span class="hljs-selector-tag">nav</span> {
    <span class="hljs-attribute">flex-direction</span>: row;
    <span class="hljs-attribute">justify-content</span>: space-evenly;
  }
}

<span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">transition</span>: all ease-in-out <span class="hljs-number">0.3s</span>;
}

<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">color</span>: black;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#3cce8d</span>;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">background-color</span>: white;
  <span class="hljs-attribute">color</span>: black;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
  <span class="hljs-attribute">transition</span>: all ease-in-out <span class="hljs-number">0.3s</span>;
}

<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#3cce8d</span>;
}

<span class="hljs-selector-class">.center</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">40vh</span>;
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-attribute">place-items</span>: center;
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">min-width:</span> <span class="hljs-number">768px</span>) {
  <span class="hljs-selector-class">.center</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">90vh</span>;
  }
}

<span class="hljs-selector-class">.loading</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">40vh</span> auto;
}
</code></pre>
<h3 id="setup-supabase-in-nextjs">Setup Supabase in Next.js.</h3>
<p>Create a file in the root of the project called <code>.env.local</code> with the following values:</p>
<pre><code class="lang-env">NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_KEY=""
</code></pre>
<p>Then go to your supabase project, scroll down and search for those values:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633573633424/cPslL8Ko4.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633573774991/DV_4RBTs1.png" alt="image.png" /></p>
<p>Create a folder called <code>db</code> with the file <code>client.ts</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633573299019/JyUzR_xOv.png" alt="image.png" /></p>
<p>And Initialize the client</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@supabase/supabase-js'</span>

<span class="hljs-comment">// Create a single supabase client for interacting with your database</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
  process.env.NEXT_PUBLIC_SUPABASE_KEY <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>
)
</code></pre>
<h2 id="supabase-auth-provider">Supabase Auth Provider.</h2>
<p>Supabase gives you a way of having OAuth2 by providing the secret keys of the auth provider that you're using (Google, Twitter, GitHub, etc.), you can use whatever you want in this example even passwordless auth, in my case I used the google provider, to get the keys you can see the following tutorial:</p>
<p><a target="_blank" href="https://youtu.be/_XM9ziOzWk4">Get the Google keys</a></p>
<h2 id="define-the-user-types">Define the User types.</h2>
<p>Go to the file <code>types/User.ts</code> and replace its content with:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> UserProfile {
  id: <span class="hljs-built_in">string</span>
  role: <span class="hljs-built_in">string</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Auth {
  isAuth: <span class="hljs-built_in">boolean</span>
  isLoading: <span class="hljs-built_in">boolean</span>
  user: UserProfile | <span class="hljs-literal">null</span> | <span class="hljs-literal">undefined</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> AuthStore = {
  updateAuth: <span class="hljs-function">(<span class="hljs-params">params: Auth</span>) =&gt;</span> <span class="hljs-built_in">void</span>
} &amp; Auth
</code></pre>
<h2 id="store">Store.</h2>
<p>Create a store folder with an <code>index.ts</code> file:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633575529525/UJr3aprc3.png" alt="image.png" /></p>
<p>Then we are going to use the <code>valtio</code> state manager for our store, with the following code:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { proxy } <span class="hljs-keyword">from</span> <span class="hljs-string">'valtio'</span>

<span class="hljs-keyword">import</span> { supabase } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/db/client'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { AuthStore } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/User'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authStore = proxy&lt;AuthStore&gt;({
  user: <span class="hljs-literal">undefined</span>,
  isAuth: !!supabase.auth.user(),
  isLoading: <span class="hljs-literal">false</span>,
  updateAuth({ isAuth, isLoading, user }) {
    authStore.isAuth = isAuth
    authStore.isLoading = isLoading
    authStore.user = user
  },
})
</code></pre>
<p>That's it, the main reason for using <code>valtio</code> is the simplicity of usage and the improvements that it has to avoid unnecessary rerenders.</p>
<h2 id="useauth-hook"><code>useAuth</code> Hook.</h2>
<p>Create a folder called hooks with an <code>auth.ts</code> file:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633575896912/DKvCQR4Bs.png" alt="image.png" /></p>
<p>Create a function called useAuth:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useAuth</span>(<span class="hljs-params"></span>) </span>{}
</code></pre>
<p>Inside of it use the <code>useSnapshot</code> to access to our store:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> { updateAuth, ...state } = useSnapshot(authStore)
</code></pre>
<p>Then define the sign in &amp; out functions:</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">const</span> signIn = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> supabase.auth.signIn({ provider: <span class="hljs-string">'google'</span> })
  }, [])

  <span class="hljs-keyword">const</span> signOut = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> supabase.auth.signOut()
  }, [])
</code></pre>
<p>After that add an <code>useEffect</code> and pass the <code>updateAuth</code> method as the only dependency:</p>
<pre><code class="lang-ts">  useEffect(<span class="hljs-function">() =&gt;</span> {

  }, [updateAuth])
</code></pre>
<p>Then create a function called <code>getUserProfile</code> to query the user profile:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> getUserProfile = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">const</span> { data: profile } = <span class="hljs-keyword">await</span> supabase
        .from&lt;UserProfile&gt;(<span class="hljs-string">'profiles'</span>)
        .select(<span class="hljs-string">'id, role'</span>)
        .single()

      <span class="hljs-keyword">if</span> (profile) {
        updateAuth({
          user: profile,
          isAuth: <span class="hljs-literal">true</span>,
          isLoading: <span class="hljs-literal">false</span>,
        })

        <span class="hljs-keyword">return</span>
      }

      updateAuth({
        user: <span class="hljs-literal">null</span>,
        isAuth: <span class="hljs-literal">false</span>,
        isLoading: <span class="hljs-literal">false</span>,
      })
    }
</code></pre>
<p>The reason why we are not using a filter to get a specific user is because we already defined in our policies that each user owns their data and nobody else.</p>
<p>After defining this function, execute it to make a request when the component mounts and also execute it inside the <code>onAuthStateChange</code> to request the user's data whenever the auth state changes:</p>
<pre><code class="lang-ts">getUserProfile()

<span class="hljs-keyword">const</span> { data } = supabase.auth.onAuthStateChange(<span class="hljs-function">() =&gt;</span> {
   getUserProfile()
})
</code></pre>
<p>And also create a subscription to get realtime data:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> profile = supabase
      .from&lt;UserProfile&gt;(<span class="hljs-string">'profiles'</span>)
      .on(<span class="hljs-string">'UPDATE'</span>, <span class="hljs-function"><span class="hljs-params">payload</span> =&gt;</span> {
        updateAuth({
          user: payload.new,
          isAuth: <span class="hljs-literal">true</span>,
          isLoading: <span class="hljs-literal">false</span>,
        })
      })
      .subscribe()
</code></pre>
<p>And finally unsubscribe when the component unmounts:</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      data?.unsubscribe()
      supabase.removeSubscription(profile)
    }
</code></pre>
<p>Then just return the functions that we created alongside the state:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">return</span> {
    signIn,
    signOut,
    ...state,
  }
</code></pre>
<h2 id="update-the-pages">Update the pages.</h2>
<p>In <code>dashboard.tsx</code>, <code>users/index.tsx</code> and <code>users/[id].tsx</code> add the sign out:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/hooks/auth'</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">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { signOut } = useAuth()

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{signOut}</span>&gt;</span>Sign Out<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<p>Add the sign in to the <code>login.tsx</code> page:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/hooks/auth'</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">Login</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { signIn } = useAuth()

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Login"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{signIn}</span>&gt;</span>Sign In<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<p>Add the <code>changeRole</code> functionality in the <code>profile.tsx</code> page:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { supabase } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/db/client'</span>
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/hooks/auth'</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">Profile</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { signOut, user } = useAuth()
  <span class="hljs-keyword">const</span> role = user?.role === <span class="hljs-string">'EMPLOYEE'</span> ? <span class="hljs-string">'ADMIN'</span> : <span class="hljs-string">'EMPLOYEE'</span>

  <span class="hljs-keyword">const</span> changeRole = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> supabase.from(<span class="hljs-string">'profiles'</span>).update({ role })
  }, [role])

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Profile"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Profile<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{signOut}</span>&gt;</span>Sign Out<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{changeRole}</span>&gt;</span>Change my user role to {role}<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<p>Add  <a target="_blank" href="https://imjulianeral.github.io/next-shield/docs/protect-components/ComponentShield"><code>ComponentShield</code></a>  in the pricing page:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { ComponentShield } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-shield'</span>
<span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</span>
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/hooks/auth'</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">Pricing</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { user, isAuth, isLoading } = useAuth()

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Pricing"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Pricing<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">ComponentShield</span> <span class="hljs-attr">showIf</span>=<span class="hljs-string">{isAuth</span> &amp;&amp; !<span class="hljs-attr">isLoading</span>}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>You are authenticated<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ComponentShield</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ComponentShield</span> <span class="hljs-attr">RBAC</span> <span class="hljs-attr">showForRole</span>=<span class="hljs-string">"ADMIN"</span> <span class="hljs-attr">userRole</span>=<span class="hljs-string">{user?.role}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>You are an ADMIN<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ComponentShield</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ComponentShield</span> <span class="hljs-attr">RBAC</span> <span class="hljs-attr">showForRole</span>=<span class="hljs-string">"EMPLOYEE"</span> <span class="hljs-attr">userRole</span>=<span class="hljs-string">{user?.role}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>You are an EMPLOYEE<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ComponentShield</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<h2 id="combine-supabase-and-nextshield">Combine Supabase and NextShield.</h2>
<p>Go to the <code>Shield.tsx</code> component, import the <code>useAuth</code> hook, and replace the values.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> { user, isAuth, isLoading } = useAuth()

<span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>]
  &gt; = {
    router,
    isAuth,
    isLoading,
    privateRoutes: [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    publicRoutes: [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>],
    hybridRoutes: [<span class="hljs-string">'/pricing'</span>],
    loginRoute: <span class="hljs-string">'/login'</span>,
    LoadingComponent: &lt;Loading /&gt;,
    RBAC: {
      ADMIN: {
        grantedRoutes: [<span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
        accessRoute: <span class="hljs-string">'/dashboard'</span>,
      },
      EMPLOYEE: {
        grantedRoutes: [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>],
        accessRoute: <span class="hljs-string">'/profile'</span>,
      },
    },
    userRole: user?.role, <span class="hljs-comment">// Must be undefined when isAuth is false &amp; defined when is true</span>
  }
</code></pre>
<h2 id="result">Result.</h2>
<p>As an unauthenticated user, you only can access public and hybrid routes:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633579671192/yUGnMbBQr.gif" alt="unauth.gif" /></p>
<p>And when you're authenticated, you only can access hybrid and private routes where you have granted access:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633580572274/S6cIS95GU.gif" alt="auth.gif" /></p>
<p>That's it, really simple, you can check the  <a target="_blank" href="https://github.com/imjulianeral/nextshield-examples/tree/supabase">github repo</a> to download this example, and also don't forget to read the <a target="_blank" href="https://imjulianeral.github.io/next-shield/">docs</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633621858885/9KVcWsXhW.gif" alt="surprised.gif" /></p>
<p>Hope you like it, I'm going to publish an example per week with the most popular auth providers (Clerk, Auth0, etc.), so if you don't wanna miss any article please follow me ;D.</p>
<h3 id="see-you-next-time">See you next time!</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633621603452/z09_yFVo-I.gif" alt="brainexplode" /></p>
]]></content:encoded></item><item><title><![CDATA[RBAC & Private Routes in Next.js using NextShield]]></title><description><![CDATA[Protecting routes and hiding features whether the user is authenticated or authorized is a common thing to do since always but even so in the react world it's quite frustrating; the rerenders that React has, is a behavior easy to understand but hard ...]]></description><link>https://blog.imjulian.com/rbac-and-private-routes-in-nextjs</link><guid isPermaLink="true">https://blog.imjulian.com/rbac-and-private-routes-in-nextjs</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Jorge Acero]]></dc:creator><pubDate>Wed, 29 Sep 2021 02:36:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1632927447023/-Cf6LRjd9H.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Protecting routes and hiding features whether the <strong>user</strong> is authenticated or authorized is a common thing to do since always but even so in the react world it's quite frustrating; the rerenders that React has, is a behavior easy to understand but hard to master, when you don't have control of it you can get a lot of issues like the  <strong>flashy content</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632413694415/VIwhYE1BZ.gif" alt="Peek 2021-09-23 11-13.gif" /></p>
<p>This happens because in the first render React read the auth state as <code>null</code> Why? because in the first render the API request was not resolved yet, is until the second render when React has resolved the request making the change to the state, triggering a rerender but now with the data <strong>available</strong>.</p>
<p>In summary, React is reading the state values before they are <strong>available</strong>, getting a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">falsy value</a>, that's the problem.</p>
<p>How can you solve this? There are many solutions, some people handle that on the frontend and implement a <em>hacky</em> way using <code>windows.location</code> or on the backend with Next.js using the <code>getServerSideProps</code> method on your pages which has a <code>redirect</code> property to force a redirect to another page, and as you know is executed before rendering the page, which is perfect, right? Well... yes but actually no.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632415824864/TVwJxmb72.gif" alt="confused" /></p>
<p>If your backend server gets frozen the only thing your user is gonna see is a blank page, because of that you can't give any feedback to your user, not even a spinner,  the best solution is still on the frontend, so may you ask "how can I implement the hacky solution using <code>windows.location</code>?" I don't recommend that also, so what solution do I recommend?</p>
<h2 id="the-solution">The Solution.</h2>
<p>Frameworks like Laravel or Ruby on Rails have this solved, so why a cutting-edge framework like Next.js has not resolved this yet? Well, maybe we never know it (or maybe there is a reason for it but I didn't realize), but I crafted my own solution called  <a target="_blank" href="https://imjulianeral.github.io/next-shield/">NextShield</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632417104791/PczV3QiWs.png" alt="logo" /></p>
<h3 id="philosophy">Philosophy.</h3>
<ul>
<li>Never deal with authorization code again.</li>
<li>Never hardcode a redirect, let your state handle it for you.</li>
<li>Easy To Use.</li>
</ul>
<h3 id="features">Features.</h3>
<ul>
<li>No Flashy Content.</li>
<li>RBAC.</li>
<li>Completely Agnostic.</li>
</ul>
<p>Let's create a simple example to see the API.</p>
<h2 id="nextshield-example">NextShield Example.</h2>
<ul>
<li>Create a new Next.js app:</li>
</ul>
<pre><code class="lang-shell">npx create-next-app --ts shield-example
</code></pre>
<ul>
<li>Install NextShield:</li>
</ul>
<pre><code class="lang-shell">npm i next-shield
</code></pre>
<ul>
<li>Install the spinners:</li>
</ul>
<pre><code class="lang-shell">npm i react-epic-spinners
</code></pre>
<ul>
<li>Copy the following styles in your styles/globals.css file:</li>
</ul>
<pre><code class="lang-css">* {
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">html</span>,
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
    Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  <span class="hljs-attribute">background-color</span>: black;
  <span class="hljs-attribute">color</span>: white;
}

<span class="hljs-selector-tag">nav</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">min-width:</span> <span class="hljs-number">768px</span>) {
  <span class="hljs-selector-tag">nav</span> {
    <span class="hljs-attribute">flex-direction</span>: row;
    <span class="hljs-attribute">justify-content</span>: space-evenly;
  }
}

<span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">transition</span>: all ease-in-out <span class="hljs-number">0.3s</span>;
}

<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">color</span>: black;
  <span class="hljs-attribute">background-color</span>: white;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">background-color</span>: white;
  <span class="hljs-attribute">color</span>: black;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
  <span class="hljs-attribute">transition</span>: all ease-in-out <span class="hljs-number">0.3s</span>;
}

<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#00d1ff</span>;
  <span class="hljs-attribute">color</span>: white;
}

<span class="hljs-selector-class">.center</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">40vh</span>;
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-attribute">place-items</span>: center;
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">min-width:</span> <span class="hljs-number">768px</span>) {
  <span class="hljs-selector-class">.center</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">90vh</span>;
  }
}

<span class="hljs-selector-class">.loading</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">40vh</span> auto;
}
</code></pre>
<ul>
<li>Add the following types:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632418919919/C09y6I4j0.png" alt="image.png" /></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Components.ts</span>

<span class="hljs-keyword">import</span> type { ReactNode } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">export</span> interface Children {
  <span class="hljs-attr">children</span>: ReactNode
}

<span class="hljs-keyword">export</span> type LayoutProps = {
  <span class="hljs-attr">title</span>: string
} &amp; Children
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// User.ts</span>

<span class="hljs-keyword">export</span> interface Profile {
  <span class="hljs-attr">id</span>: string
  <span class="hljs-attr">name</span>: string
  <span class="hljs-attr">role</span>: string
}
</code></pre>
<ul>
<li>Create the <code>Nav</code> Component:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632418727457/eGrL9Aut0.png" alt="image.png" /></p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">'next/link'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Nav</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/login"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/pricing"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Pricing<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/profile"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Profile<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/users"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Users<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/users/1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>Single User<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  )
}
</code></pre>
<ul>
<li>Add the following components:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632418141135/QnsMHR0sE.png" alt="image.png" /></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Layout.tsx</span>

<span class="hljs-keyword">import</span> Head <span class="hljs-keyword">from</span> <span class="hljs-string">'next/head'</span>
<span class="hljs-keyword">import</span> { LayoutProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/Components'</span>
<span class="hljs-keyword">import</span> { Nav } <span class="hljs-keyword">from</span> <span class="hljs-string">'../ui/Nav'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children, title }: LayoutProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>NextShield | {title}<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"NextShield Example with Firebase"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.ico"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Head</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Nav</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// Loading.tsx</span>

<span class="hljs-keyword">import</span> { BreedingRhombusSpinner } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-epic-spinners'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Loading</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BreedingRhombusSpinner</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#00d1ff"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"loading"</span> /&gt;</span></span>
}
</code></pre>
<p>And finally the <code>NextShield</code> setup.</p>
<h3 id="nextshield-setup">NextShield Setup.</h3>
<p>In <code>Shield.tsx</code> file import the <code>useRouter</code>, the <code>Loading.tsx</code> and <code>next-shield</code>:</p>
<pre><code class="lang-jsx"><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> { NextShield, NextShieldProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-shield'</span>

<span class="hljs-keyword">import</span> { Children } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/Components'</span>
<span class="hljs-keyword">import</span> { Loading } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Loading'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Shield</span>(<span class="hljs-params">{ children }: Children</span>) </span>{
  <span class="hljs-keyword">const</span> router = useRouter()



  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>{children}<span class="hljs-tag">&lt;/&gt;</span></span>
}
</code></pre>
<p>Just after the router create an object implementing the <code>NextShieldProps</code> called <code>shieldProps</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> shieldProps: NextShieldProps = {}
</code></pre>
<p>You need to pass some generics to the type, the first one is an array of the private routes of your application, and the second one is an array with the public routes:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>]
  &gt; = {}
</code></pre>
<p>Then you need to pass the following <strong>props</strong>:</p>
<h4 id="router">Router.</h4>
<p>⇆ Instance of your router.</p>
<h4 id="isauth">isAuth.</h4>
<p>🔑 This value must be provided by the state of your app. Indicates if the user is authenticated or not.</p>
<h4 id="isloading">isLoading.</h4>
<p>⏳ This value must be provided by the state of your app. Indicates if the user's data is already available or not.</p>
<h4 id="privateroutes">privateRoutes.</h4>
<p>🚧 🚧 🚧 Array of private routes. These are only accessible when the user is authenticated.</p>
<h4 id="publicroutes">publicRoutes.</h4>
<p>👀 👀 👀 Array of public routes. These are only accessible when the user is <strong>NOT</strong> authenticated.</p>
<h4 id="hybridroutes">hybridRoutes.</h4>
<p>🚦🚦🚦 Array of hybrid routes. These are always accessible; doesn't matter the auth state.</p>
<h4 id="loginroute">loginRoute.</h4>
<p>📋 Login page. Must be a public route.</p>
<h4 id="accessroute">accessRoute.</h4>
<p>🚧 Route where your user is going to access after login. Must be a private route.</p>
<h4 id="loadingcomponent">LoadingComponent.</h4>
<p>🌀 React Component which is going to appear when isLoading equals to true.</p>
<p>These are going to be the values, for this example:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>]
  &gt; = {
    router,
    <span class="hljs-attr">isAuth</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">isLoading</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">privateRoutes</span>: [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    <span class="hljs-attr">publicRoutes</span>: [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>],
    <span class="hljs-attr">hybridRoutes</span>: [<span class="hljs-string">'/pricing'</span>],
    <span class="hljs-attr">loginRoute</span>: <span class="hljs-string">'/login'</span>,
    <span class="hljs-attr">accessRoute</span>: <span class="hljs-string">'/profile'</span>
    <span class="hljs-attr">LoadingComponent</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loading</span> /&gt;</span></span>,
  }
</code></pre>
<h3 id="protect-the-app-with-nextshield">Protect the app with NextShield.</h3>
<p>Go to the <code>_app.tsx</code> file and wrap it with the <code>Shield</code> component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> type { AppProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/app'</span>

<span class="hljs-keyword">import</span> { Shield } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Shield'</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'@/styles/globals.css'</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">MyApp</span>(<span class="hljs-params">{ Component, pageProps }: AppProps</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Shield</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Shield</span>&gt;</span></span>
  )
}
</code></pre>
<h3 id="create-the-routes">Create the routes.</h3>
<p>Create the following routes:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632874410380/L8R_UQ5llR.png" alt="routes" /></p>
<p>And write in each of them the following content:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/routes/Layout'</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">PageName</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"PageName"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>PageName<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  )
}
</code></pre>
<p>By now you must have something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632875019492/uV_jqaamy.gif" alt="Peek 2021-09-28 19-23.gif" /></p>
<p>As you can see, you can only access the public &amp; hybrid routes, your private routes are completely protected, Even if you see your history the private routes won't appear:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632875342519/YORABi2bl.png" alt="image.png" /></p>
<p>Now you can play and set <code>isAuth: true</code>, you immediately won't be able to access the public routes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632876260602/W3sUaouse.gif" alt="mind blown" /></p>
<h3 id="add-rbac">Add RBAC.</h3>
<p>Fine, you just got a simple example with auth users, now let's add something more interesting.</p>
<p>To add RBAC you need to pass the prop called <code>RBAC</code> as an object with the following configuration:</p>
<pre><code class="lang-js">RBAC: {
      <span class="hljs-attr">ADMIN</span>: {
        <span class="hljs-attr">grantedRoutes</span>: [<span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
        <span class="hljs-attr">accessRoute</span>: <span class="hljs-string">'/dashboard'</span>,
      },
      <span class="hljs-attr">EMPLOYEE</span>: {
        <span class="hljs-attr">grantedRoutes</span>: [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>],
        <span class="hljs-attr">accessRoute</span>: <span class="hljs-string">'/profile'</span>,
      },
},
</code></pre>
<p>As you can see you define the roles of your app in the object keys, and inside you must define the <code>grantedRoutes</code> which is an array with the routes that are going to be accessible for that role, also you must define the <code>accessRoute</code> inside of this object <strong>and remove it from outside</strong>.</p>
<p>After that, you must pass the prop called <code>userRole</code>, which is the role of the current auth user, this must match with the object keys in <code>RBAC</code>, also this prop needs to be undefined when <code>isAuth</code> is false and defined when is true.</p>
<pre><code class="lang-js">userRole: <span class="hljs-string">'ADMIN'</span>, <span class="hljs-comment">// Must be undefined when isAuth is false &amp; defined when is true</span>
</code></pre>
<p>The end result should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> shieldProps: NextShieldProps&lt;
    [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>]
  &gt; = {
    router,
    isAuth: <span class="hljs-literal">true</span>,
    isLoading: <span class="hljs-literal">false</span>,
    privateRoutes: [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
    publicRoutes: [<span class="hljs-string">'/'</span>, <span class="hljs-string">'/login'</span>],
    hybridRoutes: [<span class="hljs-string">'/pricing'</span>],
    loginRoute: <span class="hljs-string">'/login'</span>,
    LoadingComponent: &lt;Loading /&gt;,
    RBAC: {
      ADMIN: {
        grantedRoutes: [<span class="hljs-string">'/dashboard'</span>, <span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/users'</span>, <span class="hljs-string">'/users/[id]'</span>],
        accessRoute: <span class="hljs-string">'/dashboard'</span>,
      },
      EMPLOYEE: {
        grantedRoutes: [<span class="hljs-string">'/profile'</span>, <span class="hljs-string">'/dashboard'</span>],
        accessRoute: <span class="hljs-string">'/profile'</span>,
      },
    },
    userRole: <span class="hljs-string">'ADMIN'</span>, <span class="hljs-comment">// Must be undefined when isAuth is false &amp; defined when is true</span>
  }
</code></pre>
<p>And that's it! You will get the same behavior as before:</p>
<ul>
<li>Unable to access ungranted routes.</li>
<li>No trace on your history.</li>
<li>0 flashy content, the <code>LoadingComponent</code> will always intercept the request before showing the page or redirecting the user.</li>
<li>Configure everything in one place.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632882692044/7LUO8Gupe.gif" alt="magikarp guy" /></p>
<h2 id="next-steps">Next Steps.</h2>
<ol>
<li><a target="_blank" href="https://imjulianeral.github.io/next-shield/">Read the docs</a>.</li>
<li><a target="_blank" href="https://github.com/imjulianeral/nextshield-examples">See the complete example on the repo (main branch)</a></li>
<li>Use it with your preferred auth provider.</li>
<li>Use  <a target="_blank" href="https://imjulianeral.github.io/next-shield/docs/protect-components/ComponentShield">ComponentShield</a> to get more control of what is displayed on the screen.</li>
<li>Wait for the following Examples!</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Create Email Templates with Pure React]]></title><description><![CDATA[Hi everyone, in this article, we are going to create email templates with React components, this can be done in any react framework, the only requirement to do this is to have react-dom installed. The technologies used in this article are:

Next.js 
...]]></description><link>https://blog.imjulian.com/create-email-templates-with-react</link><guid isPermaLink="true">https://blog.imjulian.com/create-email-templates-with-react</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[email]]></category><category><![CDATA[smtp]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Jorge Acero]]></dc:creator><pubDate>Tue, 21 Sep 2021 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1632169311747/oI9sexLR0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi everyone, in this article, we are going to create email templates with React components, this can be done in any react framework, the only requirement to do this is to have <code>react-dom</code> installed. The technologies used in this article are:</p>
<ul>
<li><a target="_blank" href="https://nextjs.org/">Next.js</a> </li>
<li><a target="_blank" href="https://smtpjs.com/">Smtp.js</a> </li>
<li><a target="_blank" href="https://chakra-ui.com/">Chakra UI</a> </li>
<li><a target="_blank" href="https://react-hook-form.com/">React Hook Form</a> </li>
</ul>
<p>Also you can see the finished project here: 
 <a target="_blank" href="https://github.com/imjulianeral/react-email-template">GitHub Repo</a> </p>
<h2 id="create-the-project">Create the project.</h2>
<p>As I already said you can use any react framework or bundler (vite, webpack, etc.) to do this, in my case I'm going to create a Next.js project.</p>
<pre><code class="lang-bash"> npx create-next-app --ts mail-template
</code></pre>
<h3 id="adding-dependencies">Adding dependencies.</h3>
<p>These dependencies are entirely optional, you can use your preferred packages.</p>
<ul>
<li>Install Chakra UI:</li>
</ul>
<pre><code class="lang-bash">npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
</code></pre>
<p>Change your <code>_app.tsx</code> page to this:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { ChakraProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"@chakra-ui/react"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ChakraProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ChakraProvider</span>&gt;</span></span>
  )
}
</code></pre>
<ul>
<li>Install React Hook Form:<pre><code class="lang-bash">npm i react-hook-form
</code></pre>
</li>
</ul>
<h2 id="creating-the-template">Creating the template.</h2>
<p>The template is nothing else but a React component, that's it, there is no magic, so let's create a directory called <code>email</code> at the root of our project, inside there create a file called <code>EmailTemplate.tsx</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632174094955/qd1hmNy5y.png" alt="directory" /></p>
<h3 id="define-the-props">Define the props</h3>
<p>On the root of our project create a directory called <code>types</code> and create a file called <code>Email.ts</code> inside of it:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632174450080/j4siVpnZq.png" alt="types" /></p>
<p>In my case, I'm only going to support the following props, but you can define what you want based on your needs:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632174595275/dezGQURc2.png" alt="Email.ts" /></p>
<h3 id="add-the-markup">Add the markup</h3>
<p>This step is complete ought to you, create a different markup if you needed but for this example, I'm going to use this:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { EmailData } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/Email'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">EmailTemplate</span>(<span class="hljs-params">{ subject, name, email, message }: EmailData</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span>
        <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">backgroundColor:</span> '#<span class="hljs-attr">1a202c</span>',
          <span class="hljs-attr">color:</span> '#<span class="hljs-attr">ffffff</span>',
          <span class="hljs-attr">padding:</span> '<span class="hljs-attr">2rem</span>',
        }}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">textAlign:</span> '<span class="hljs-attr">center</span>', <span class="hljs-attr">textTransform:</span> '<span class="hljs-attr">capitalize</span>', <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">0</span> }}&gt;</span>
          {subject}
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>
          Name: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> '#<span class="hljs-attr">81e6d9</span>' }}&gt;</span>{name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>
          Email:{' '}
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">mailto:</span>${<span class="hljs-attr">email</span>}`} <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> '#<span class="hljs-attr">81e6d9</span>' }}&gt;</span>
            {email}
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">article</span>
          <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
            <span class="hljs-attr">backgroundColor:</span> '#<span class="hljs-attr">308c7a4c</span>',
            <span class="hljs-attr">color:</span> '#<span class="hljs-attr">81e6d9</span>',
            <span class="hljs-attr">borderRadius:</span> '<span class="hljs-attr">0.5rem</span>',
            <span class="hljs-attr">padding:</span> '<span class="hljs-attr">1rem</span>',
          }}
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">margin:</span> <span class="hljs-attr">0</span> }}&gt;</span>Message:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">margin:</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">fontSize:</span> '<span class="hljs-attr">1rem</span>' }}&gt;</span>{message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span></span>
  )
}
</code></pre>
<p>As you can see I'm using inline styles, Why? This is because you need your styles already embedded on your markup, if you try to use a CSS file instead your styles won't be applied.</p>
<h2 id="configuring-smtpjs">Configuring SMTP.js</h2>
<p>Before we continue with the form component, we need to configure SMTP.js.</p>
<p>SMTP.js is a completely free solution to send emails from the frontend, but there is a caveat to consider, <strong>the maintainers have absolutely 0 spam tolerance</strong>, so you need to apply some measures before launching an app to production.</p>
<p>You need an SMTP server, if you don't have one, you can use your Gmail account,  <a target="_blank" href="https://myaccount.google.com/lesssecureapps">but you need to enable your account for "less secure apps"</a>.</p>
<p>Now you need to go to the  <a target="_blank" href="https://smtpjs.com/">SMTP.js website</a> and click on:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632177873051/oaqXnFgjx.png" alt="encrypt" /></p>
<p>After that, you need to write your SMTP credentials, (if you are using Gmail, you can use the same exact values below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632177765766/XrH3rJoyI.png" alt="smtp credentials" /></p>
<p>This will generate a token that we need to save on a <code>.env.local</code> file at the root of our project, create a variable, and paste the token there, also create another variable to store your email.</p>
<pre><code class="lang-env">NEXT_PUBLIC_EMAIL_TOKEN="secret token"
NEXT_PUBLIC_EMAIL="my@email.com"
</code></pre>
<h3 id="install-smtpjs-sdk">Install SMTP.js SDK</h3>
<p>Now click download in SMTP.js website:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632178391434/kZ7_4ITFm.png" alt="download smtp.js" /></p>
<p>And save the file inside the email directory that we created with a <code>.ts</code> extension:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632178514232/Aeco8xxIH.png" alt="smtp.js" /></p>
<p>Open the file and add on line 2 the comment <code>// @ts-ignore</code>, and replace <code>var</code> with <code>export let</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632178797557/Ij9Z_KYli.png" alt="convert SMTP.js to TS" /></p>
<h2 id="build-the-form">Build the form.</h2>
<p>As you may be already inferred, this is a simple form, 4 text fields, nothing special, so the following is the implementation of this simple form but with <code>react-hook-form</code> &amp; <code>Chakra UI</code>. </p>
<p>So create the file under the directory <code>components/forms/SendMail.tsx</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632180094966/2mOVbfjv8.png" alt="image.png" /></p>
<p>And add the code:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { renderToStaticMarkup } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/server'</span>
<span class="hljs-keyword">import</span> { Box, Center, Heading, VStack } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/layout'</span>
<span class="hljs-keyword">import</span> { FormControl, FormErrorMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/form-control'</span>
<span class="hljs-keyword">import</span> { Input } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/input'</span>
<span class="hljs-keyword">import</span> { Textarea } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/textarea'</span>
<span class="hljs-keyword">import</span> { Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/button'</span>
<span class="hljs-keyword">import</span> { useToast } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/toast'</span>
<span class="hljs-keyword">import</span> { EmailIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">'@chakra-ui/icons'</span>
<span class="hljs-keyword">import</span> { SubmitHandler, useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-hook-form'</span>

<span class="hljs-keyword">import</span> type { EmailData } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/types/Email'</span>
<span class="hljs-keyword">import</span> { EmailTemplate } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/email/EmailTemplate'</span>
<span class="hljs-keyword">import</span> { Email } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/email/smtp'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SendMailForm</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> toast = useToast()
  <span class="hljs-keyword">const</span> {
    handleSubmit,
    register,
    reset,
    <span class="hljs-attr">formState</span>: { errors, isSubmitting },
  } = useForm&lt;EmailData&gt;({ <span class="hljs-attr">mode</span>: <span class="hljs-string">'onBlur'</span> })

  <span class="hljs-keyword">const</span> sendMail: SubmitHandler&lt;EmailData&gt; = useCallback(
    <span class="hljs-keyword">async</span> data =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> Email.send({
          <span class="hljs-attr">SecureToken</span>: process.env.NEXT_PUBLIC_EMAIL_TOKEN,
          <span class="hljs-attr">To</span>: data.email,
          <span class="hljs-attr">From</span>: process.env.NEXT_PUBLIC_EMAIL,
          <span class="hljs-attr">Subject</span>: data.subject,
          <span class="hljs-attr">Body</span>: renderToStaticMarkup(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">EmailTemplate</span> {<span class="hljs-attr">...data</span>} /&gt;</span></span>),
        })

        toast({
          <span class="hljs-attr">title</span>: <span class="hljs-string">'Mail successfully sent'</span>,
          <span class="hljs-attr">description</span>: <span class="hljs-string">`The mail was sent to: <span class="hljs-subst">${data.email}</span>`</span>,
          <span class="hljs-attr">status</span>: <span class="hljs-string">'success'</span>,
          <span class="hljs-attr">position</span>: <span class="hljs-string">'top'</span>,
          <span class="hljs-attr">duration</span>: <span class="hljs-number">5000</span>,
          <span class="hljs-attr">isClosable</span>: <span class="hljs-literal">true</span>,
        })
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error)
      } <span class="hljs-keyword">finally</span> {
        reset()
      }
    },
    [reset, toast]
  )

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Center</span> <span class="hljs-attr">h</span>=<span class="hljs-string">"100vh"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">VStack</span> <span class="hljs-attr">spacing</span>=<span class="hljs-string">{7}</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"stretch"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span> <span class="hljs-attr">as</span>=<span class="hljs-string">"h1"</span> <span class="hljs-attr">alignSelf</span>=<span class="hljs-string">"center"</span>&gt;</span>
          Send mail with SMTP.js
        <span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Box</span> <span class="hljs-attr">maxW</span>=<span class="hljs-string">"lg"</span> <span class="hljs-attr">p</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">borderWidth</span>=<span class="hljs-string">"1px"</span> <span class="hljs-attr">borderRadius</span>=<span class="hljs-string">"lg"</span> <span class="hljs-attr">overflow</span>=<span class="hljs-string">"hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit(sendMail)}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">VStack</span> <span class="hljs-attr">spacing</span>=<span class="hljs-string">{7}</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"stretch"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">FormControl</span> <span class="hljs-attr">isInvalid</span>=<span class="hljs-string">{errors.subject</span> <span class="hljs-attr">as</span> <span class="hljs-attr">boolean</span> | <span class="hljs-attr">undefined</span>}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Input</span>
                  <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"subject"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Subject"</span>
                  {<span class="hljs-attr">...register</span>('<span class="hljs-attr">subject</span>', {
                    <span class="hljs-attr">required:</span> '<span class="hljs-attr">The</span> <span class="hljs-attr">subject</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>',
                    <span class="hljs-attr">minLength:</span> { <span class="hljs-attr">value:</span> <span class="hljs-attr">4</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">Minimum</span> <span class="hljs-attr">length</span> <span class="hljs-attr">should</span> <span class="hljs-attr">be</span> <span class="hljs-attr">4</span>' },
                  })}
                /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">FormErrorMessage</span>&gt;</span>{errors.subject?.message}<span class="hljs-tag">&lt;/<span class="hljs-name">FormErrorMessage</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">FormControl</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">FormControl</span> <span class="hljs-attr">isInvalid</span>=<span class="hljs-string">{errors.name</span> <span class="hljs-attr">as</span> <span class="hljs-attr">boolean</span> | <span class="hljs-attr">undefined</span>}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Input</span>
                  <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Name"</span>
                  {<span class="hljs-attr">...register</span>('<span class="hljs-attr">name</span>', {
                    <span class="hljs-attr">required:</span> '<span class="hljs-attr">The</span> <span class="hljs-attr">name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>',
                    <span class="hljs-attr">minLength:</span> { <span class="hljs-attr">value:</span> <span class="hljs-attr">4</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">Minimum</span> <span class="hljs-attr">length</span> <span class="hljs-attr">should</span> <span class="hljs-attr">be</span> <span class="hljs-attr">4</span>' },
                  })}
                /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">FormErrorMessage</span>&gt;</span>{errors.name?.message}<span class="hljs-tag">&lt;/<span class="hljs-name">FormErrorMessage</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">FormControl</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">FormControl</span> <span class="hljs-attr">isInvalid</span>=<span class="hljs-string">{errors.email</span> <span class="hljs-attr">as</span> <span class="hljs-attr">boolean</span> | <span class="hljs-attr">undefined</span>}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Input</span>
                  <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email"</span>
                  {<span class="hljs-attr">...register</span>('<span class="hljs-attr">email</span>', {
                    <span class="hljs-attr">required:</span> '<span class="hljs-attr">The</span> <span class="hljs-attr">email</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>',
                    <span class="hljs-attr">pattern:</span> { <span class="hljs-attr">value:</span> /^\<span class="hljs-attr">S</span>+@\<span class="hljs-attr">S</span>+$/<span class="hljs-attr">i</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">Write</span> <span class="hljs-attr">a</span> <span class="hljs-attr">valid</span> <span class="hljs-attr">email</span>' },
                  })}
                /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">FormErrorMessage</span>&gt;</span>{errors.email?.message}<span class="hljs-tag">&lt;/<span class="hljs-name">FormErrorMessage</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">FormControl</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">FormControl</span> <span class="hljs-attr">isInvalid</span>=<span class="hljs-string">{errors.message</span> <span class="hljs-attr">as</span> <span class="hljs-attr">boolean</span> | <span class="hljs-attr">undefined</span>}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Textarea</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Write your message"</span>
                  {<span class="hljs-attr">...register</span>('<span class="hljs-attr">message</span>', {
                    <span class="hljs-attr">required:</span> '<span class="hljs-attr">The</span> <span class="hljs-attr">message</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>',
                    <span class="hljs-attr">minLength:</span> { <span class="hljs-attr">value:</span> <span class="hljs-attr">10</span>, <span class="hljs-attr">message:</span> '<span class="hljs-attr">Minimum</span> <span class="hljs-attr">length</span> <span class="hljs-attr">should</span> <span class="hljs-attr">be</span> <span class="hljs-attr">10</span>' },
                  })}
                /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">FormErrorMessage</span>&gt;</span>{errors.message?.message}<span class="hljs-tag">&lt;/<span class="hljs-name">FormErrorMessage</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">FormControl</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">Button</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
                <span class="hljs-attr">colorScheme</span>=<span class="hljs-string">"teal"</span>
                <span class="hljs-attr">size</span>=<span class="hljs-string">"md"</span>
                <span class="hljs-attr">loadingText</span>=<span class="hljs-string">"Submitting"</span>
                <span class="hljs-attr">isFullWidth</span>=<span class="hljs-string">{true}</span>
                <span class="hljs-attr">isLoading</span>=<span class="hljs-string">{isSubmitting}</span>
                <span class="hljs-attr">leftIcon</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">EmailIcon</span> /&gt;</span>}
              &gt;
                Send
              <span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">VStack</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Box</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">VStack</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Center</span>&gt;</span></span>
  )
}
</code></pre>
<h2 id="explanation">Explanation.</h2>
<p>Basically we are using SMTP.js SDK inside the <code>sendMail</code> function:</p>
<pre><code class="lang-jsx">Email.send({
          <span class="hljs-attr">SecureToken</span>: process.env.NEXT_PUBLIC_EMAIL_TOKEN,
          <span class="hljs-attr">To</span>: data.email,
          <span class="hljs-attr">From</span>: process.env.NEXT_PUBLIC_EMAIL,
          <span class="hljs-attr">Subject</span>: data.subject,
          <span class="hljs-attr">Body</span>: renderToStaticMarkup(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">EmailTemplate</span> {<span class="hljs-attr">...data</span>} /&gt;</span></span>),
 })
</code></pre>
<p>We are passing our secret token, email, and the subject, but the really important thing is in the <code>Body</code>:</p>
<pre><code class="lang-jsx">renderToStaticMarkup(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">EmailTemplate</span> {<span class="hljs-attr">...data</span>} /&gt;</span></span>)
</code></pre>
<p><code>renderToStaticMarkup</code> is being imported from <code>react-dom/server</code>, this function receives the component which is the Email template that we did and returns a simple string with that markup as the content, that's when the email template is being created. Basically, what we did instead of writing a template string by ourselves, we created a simple React component and transpile it to a string with the tools that React already gives us by default.</p>
<p>To end this tutorial, we only need to import our form to the index page:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Head <span class="hljs-keyword">from</span> <span class="hljs-string">'next/head'</span>
<span class="hljs-keyword">import</span> { SendMailForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/forms/SendMail'</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">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Mail Sender<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Generated by create next app"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.ico"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Head</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">SendMailForm</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p>Fill the form:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632181758015/1dI9QzBaw.png" alt="form" /></p>
<p>And check your inbox :D</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1632181956662/H65g4_uT_.png" alt="image.png" /></p>
]]></content:encoded></item><item><title><![CDATA[How to Publish your Flutter App on Windows - The Easy Way]]></title><description><![CDATA[As you maybe already know, the flutter's 2.0 stable channel already integrates the feature of building apps for desktop, but it stills in beta, you have to consider that.
In this post, I'm going to show you how to generate the build for Windows and c...]]></description><link>https://blog.imjulian.com/how-to-publish-your-flutter-app-on-windows-the-easy-way</link><guid isPermaLink="true">https://blog.imjulian.com/how-to-publish-your-flutter-app-on-windows-the-easy-way</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Flutter Examples]]></category><category><![CDATA[Windows]]></category><category><![CDATA[desktop]]></category><dc:creator><![CDATA[Jorge Acero]]></dc:creator><pubDate>Fri, 23 Apr 2021 17:04:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619197270507/08wyVFLlh.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As you maybe already know, <a target="_blank" href="https://flutter.dev/desktop#beta-snapshot-in-stable-channel">the flutter's 2.0 stable channel already integrates the feature of building apps for desktop</a>, but <strong>it stills in beta</strong>, you have to consider that.</p>
<p>In this post, I'm going to show you how to generate the build for Windows and create an installer for your end-user to make his installation process easy as possible.</p>
<h2 id="what-do-you-need-before-starting">What do you need before starting?</h2>
<p>You only need two things installed in your system:</p>
<ul>
<li>Flutter</li>
<li>Visual Studio 2019 with the “Desktop development with C++” workload installed, including all of its default components</li>
<li>InnoSetup</li>
</ul>
<p>The installation of Flutter and Visual Studio won't be explained because it's beyond the scope of this post, but I'll show you the installation of <strong>InnoSetup</strong> because we are going to create the installer with it.</p>
<h2 id="innosetup-installation">InnoSetup Installation</h2>
<ol>
<li>First access the <a target="_blank" href="https://jrsoftware.org/isdl.php#qsp">InnoSetup's website</a>.</li>
<li><p>Then find this section and select a link in the <em>Download Sites</em> column:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619131513947/gg-yLIk0p.png" alt="Screenshot 2021-04-22 174452.png" /></p>
</li>
<li><p>Execute the <code>innosetup-x.y.z.exe</code> file, as you can see is the classic installation process:</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619131791940/AKkpEwtkI.png" alt="is1.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619131801757/N2OA76OFx.png" alt="is2.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619131821078/LTYxQpELo.png" alt="is3.png" /></p>
<p>Congrats! You have InnoSetup installed! 🥳</p>
<h2 id="creating-the-app">Creating the App</h2>
<p>First, you need to enable the desktop environment in flutter by writing the following in PowerShell:</p>
<pre><code class="lang-bash">PS &gt; flutter config --enable-windows-desktop
</code></pre>
<p>After that you can check the connected devices by executing <code>flutter devices</code>, you should see Windows (desktop) in the list:</p>
<pre><code class="lang-bash">PS &gt; flutter devices
1 connected device:

Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.18362.1082]
...
</code></pre>
<p>If you have problems you can execute <code>flutter doctor</code> to see what's wrong, but you should see something like this:</p>
<pre><code class="lang-bash">PS &gt; flutter doctor                                                                     
Doctor summary (to see all details, run flutter doctor -v):                                           
[√] Flutter (Channel beta, 1.27.0-1.0.pre, on Microsoft Windows [Version 10.0.19042.782], locale en-AU)
[√] Android toolchain - develop <span class="hljs-keyword">for</span> Android devices (Android SDK version 30.0.3)                      
[√] Chrome - develop <span class="hljs-keyword">for</span> the web                                                                      
[√] Visual Studio - develop <span class="hljs-keyword">for</span> Windows (Visual Studio Community 2019 16.7.7)                         
[√] Android Studio (version 4.1.0)                                                                    
[√] VS Code (version 1.51.1)                                                                          
[√] Connected device (3 available)
</code></pre>
<p>If everything is ok, you can create a new project:</p>
<pre><code class="lang-bash">PS &gt; flutter create windowsapp
</code></pre>
<p>Then go to the directory:</p>
<pre><code class="lang-bash">PS &gt; <span class="hljs-built_in">cd</span> windowsapp
</code></pre>
<p>And execute the app 🤩:</p>
<pre><code class="lang-bash">PS &gt; flutter run -d windows
</code></pre>
<p>Great you are running a flutter app on Windows 🥳🎊🎉!!!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619133547691/w4xjdUMJn.png" alt="f.png" /></p>
<h2 id="build-a-release-app">Build a Release App</h2>
<p>As easy as running a single command 🥰:</p>
<pre><code class="lang-bash">PS &gt; flutter build windows
</code></pre>
<p>The build is located in <code>build\windows\runner\Release\</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619135437238/wMDOk4LGY.png" alt="release.png" /></p>
<h2 id="distributing-the-app">Distributing the App</h2>
<p>Now comes the interesting part and the hardest one, this is because you need to put some <code>.dll</code> files <strong>manually</strong> (🤮) and create your own installer.</p>
<p>So, you need to place 3 <code>.dll</code> files in the build folder, which are:</p>
<ul>
<li>msvcp140.dll</li>
<li>vcruntime140.dll</li>
<li>vcruntime140_1.dll</li>
</ul>
<p>You must find them in the directory <code>C:\Windows\System32</code>. After you place these 3 files your build directory must look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619137031575/CtuDSckve.png" alt="dlls.png" /></p>
<h3 id="creating-the-installer">Creating the Installer</h3>
<ul>
<li>Create a folder called <code>installer</code> inside the build folder.</li>
<li>Open InnoSetup and click the first button:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619137884473/992tHh4nN.png" alt="isc1.PNG" /></p>
<ul>
<li>Then click on next:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619138039354/AD6MjD1Ei.png" alt="isc2.png" /></p>
<ul>
<li>Then write your data and click next:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619138130326/KE_h37phd.png" alt="Screenshot 2021-04-22 193447.png" /></p>
<ul>
<li>You can change the folder settings as you wish, then click on next:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619138264253/JXQOS0ZXV.png" alt="isc3.png" /></p>
<ul>
<li>This is the most important step, you have to click the <code>Browse...</code> button and select the <code>.exe</code> file, then click the <code>Add Folder...</code> button and select the <code>Release</code> folder (<code>build\windows\runner\Release\</code>) which is the build folder, finally it will appear a message saying "Should files in subfolders also be included?" you must click <code>Yes</code> and then click on next:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619138590789/alEfoYW1-.png" alt="isc5.png" /></p>
<ul>
<li>Depending on your needs you should disable this section:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139036276/a519uwsjU.png" alt="isc6.png" /></p>
<ul>
<li>You can customize this section also:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139116520/YiQw9UZuS.png" alt="isc7.png" /></p>
<ul>
<li>Here you can attach your license and some other information:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139229624/vLIuFCUcv.png" alt="isc8.png" /></p>
<ul>
<li>Then you can choose the installation method:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139320664/rfUqBFw81.png" alt="isc9.png" /></p>
<ul>
<li>Select the languages that you need for your installer:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139377370/LWM-i2lFO.png" alt="isc10.png" /></p>
<ul>
<li>Select the <code>installer</code> folder that we created. and the name of the file, you can add a custom icon and password:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139531488/Ko24XUL8_.png" alt="isc11.png" /></p>
<ul>
<li>Click next:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139566246/6BRXwyP-w.png" alt="isc12.png" /></p>
<ul>
<li>Finally, click <code>Finish</code> and <code>Yes</code></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139645540/Qe6F9n2o6.png" alt="isc13.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619139650173/_X3C64kTg.png" alt="isc14.png" /></p>
<p>It will also ask you if you want to save the script, you can choose no if you want.</p>
<h2 id="installing-our-app">Installing Our App 🙂</h2>
<p>Finally, after a few seconds, you will see the installer in the <code>installer</code> folder:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619140408634/y9X3EG0iK.png" alt="ins.png" /></p>
<p>Open the installer and you'll see a classic installation process, you just have to click next until is installed.</p>
<p>And BOOM 🤯, you got a successful installation of a Flutter App on Windows 🥰</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619140855598/iSB5CUivk.png" alt="fin.png" /></p>
<p>Hope you enjoyed this tutorial, see you next time 🤗</p>
]]></content:encoded></item><item><title><![CDATA[Install Valet Linux on Ubuntu from scratch]]></title><description><![CDATA[In this post, we will set our development environment with PHP, Laravel, MariaDB/MySQL, and Valet Linux; yeah, the exclusive macOS development tool working on Linux ;D.
Install MariaDB

To install MariaDB, open the terminal, and type the following co...]]></description><link>https://blog.imjulian.com/install-valet-linux-on-ubuntu-from-scratch</link><guid isPermaLink="true">https://blog.imjulian.com/install-valet-linux-on-ubuntu-from-scratch</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><category><![CDATA[PHP7]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Developer Tools]]></category><dc:creator><![CDATA[Jorge Acero]]></dc:creator><pubDate>Sat, 02 Jan 2021 19:23:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1609628739865/UDSNAd1I8.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609624764836/-YNhFCh5e.png" alt="valet.png" /></p>
<p>In this post, we will set our development environment with PHP, Laravel, MariaDB/MySQL, and Valet Linux; yeah, the exclusive macOS development tool working on Linux ;D.</p>
<h3 id="install-mariadb">Install MariaDB</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609625038966/skWB793sK.png" alt="mariadb.png" /></p>
<p>To install MariaDB, open the terminal, and type the following command:</p>
<pre><code class="lang-bash">sudo apt install mariadb-server mariadb-client
</code></pre>
<p>After the installation concludes, test the following commands in that specific order to check if the installation was successful:</p>
<pre><code class="lang-bash">sudo systemctl stop mariadb.service
sudo systemctl start mariadb.service
sudo systemctl <span class="hljs-built_in">enable</span> mariadb.service
</code></pre>
<p>After verifying the installation was successful, we need to run the <code>mysql_secure_installation</code>, to set MariaDB correctly in our development environment:</p>
<pre><code class="lang-bash">sudo mysql_secure_installation
</code></pre>
<p>After you execute the command, please answer the following questions:</p>
<ul>
<li>Enter current password for root (enter for none): <strong>Just press enter</strong></li>
<li>Set root password? [Y/n]: <strong>Y</strong></li>
<li>New password: <strong>Write a secure password</strong></li>
<li>Re-enter new password: <strong>Repeat the password</strong></li>
<li>Remove anonymous users? [Y/n]: <strong>Y</strong></li>
<li>Disallow root login remotely? [Y/n]: <strong>Y</strong></li>
<li>Remove test database and access to it? [Y/n]: <strong>Y</strong></li>
<li>Reload privilege tables now? [Y/n]: <strong>Y</strong></li>
</ul>
<p>Restart MariaDB's service:</p>
<pre><code class="lang-bash">sudo systemctl restart mariadb.service
</code></pre>
<p>Then, access to MariaDB through the terminal:</p>
<pre><code class="lang-bash">sudo mariadb -u root -p
</code></pre>
<p>Or</p>
<pre><code class="lang-bash">sudo mysql -u root -p
</code></pre>
<p><strong>Remember that MariaDB is fully compatible with MySQL ;).</strong></p>
<p>We need to write the password that we defined in the <code>mysql_secure_installation</code> process in the following SQL statement. This also helps to access MariaDB without <strong>sudo</strong> on the terminal:</p>
<pre><code class="lang-SQL"><span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">ALL</span> <span class="hljs-keyword">PRIVILEGES</span> <span class="hljs-keyword">ON</span> *.* <span class="hljs-keyword">TO</span> <span class="hljs-string">'root'</span>@<span class="hljs-string">'localhost'</span> <span class="hljs-keyword">IDENTIFIED</span> <span class="hljs-keyword">BY</span> <span class="hljs-string">"your_password"</span>;
</code></pre>
<p>Now exit from MariaDB by pressing: <strong>Ctrl+C</strong> on the terminal.</p>
<h3 id="install-php-on-linux">Install PHP on Linux</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609625575852/t9O1pZb5B.png" alt="php.png" /></p>
<p>This is very straight; forward we only need to run the following command:</p>
<pre><code class="lang-bash">sudo apt install php libapache2-mod-php php-common php-mbstring php-xmlrpc php-soap php-gd php-xml php-mysql php-cli php-mcrypt php-zip php-curl php-sqlite3 php-pgsql
</code></pre>
<p>And then open <strong>php.ini</strong> file to make some adjustments:</p>
<pre><code class="lang-bash">sudo nano /etc/php/7.X/apache2/php.ini
</code></pre>
<p>Modify the following variables:</p>
<pre><code class="lang-bash">memory_limit = 256M
upload_max_filesize = 64M
cgi.fix_pathinfo=0
</code></pre>
<p>We save the changes by pressing <code>Ctrl+O</code> and exit with <code>Ctrl+X</code>.</p>
<h3 id="install-laravel-on-linux">Install Laravel on Linux</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609625439070/v0Vw6xZ_O.png" alt="laravel.png" /></p>
<p>We need <code>composer</code> installed in our system to do that, we can install it with:</p>
<pre><code class="lang-bash">sudo apt install composer
</code></pre>
<p>And now install Laravel:</p>
<pre><code class="lang-bash">composer global require laravel/installer
</code></pre>
<p>Now we need to add Laravel in our $PATH variable, to make "laravel" a recognizable command:</p>
<pre><code class="lang-bash">sudo nano .bashrc
</code></pre>
<p>If you are using <strong>zsh</strong>:</p>
<pre><code class="lang-bash">sudo nano .zshrc
</code></pre>
<p>Scroll down to the last line of the file and add the following:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.config/composer/vendor/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre>
<p>We save the changes by pressing <code>Ctrl+O</code> and exit with <code>Ctrl+X</code>.</p>
<p>And to force our shell to read the changes, execute:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> .bashrc
</code></pre>
<p>If you are using <strong>zsh</strong>:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> .zshrc
</code></pre>
<p>Now we can create a Laravel app:</p>
<pre><code class="lang-bash">laravel new laravel-app
</code></pre>
<p>If you want to use Laravel 7, try with the following commands and ensure that the PHP version you installed is PHP 7 and not PHP 8:</p>
<pre><code class="lang-bash">composer create-project laravel/laravel=^7 laravel-app --prefer-dist
</code></pre>
<p>If it does not work try replacing the <strong>^</strong> with <strong>&gt;</strong>:</p>
<pre><code class="lang-bash">composer create-project laravel/laravel=&gt;7 laravel-app --prefer-dist
</code></pre>
<p>It should create a Laravel app without problems.</p>
<h3 id="install-valet-linux">Install Valet Linux</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609624764836/-YNhFCh5e.png" alt="valet.png" /></p>
<p>Finally, execute:</p>
<pre><code class="lang-bash">composer global require cpriego/valet-linux
</code></pre>
<p>If it throws an error, please execute:</p>
<pre><code class="lang-bash">sudo apt install php-curl
</code></pre>
<p>Try again and now it should work.</p>
<p>Now execute the installation process:</p>
<pre><code class="lang-bash">valet install
</code></pre>
<p>This also could throw an error, if that's the case, please force the shell to read the changes as we did before:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> .bashrc
</code></pre>
<p>If you are using <strong>zsh</strong>:</p>
<pre><code class="lang-bash">sudo nano .zshrc
</code></pre>
<p>We need to ensure that we update our $PATH environment variable correctly on the previous steps.</p>
<p>Now <code>valet</code> should throw an error about missing dependencies, to solve that we simply run:</p>
<pre><code class="lang-bash">sudo apt-get install network-manager libnss3-tools jq xsel
</code></pre>
<p>Now it should install valet correctly:</p>
<pre><code class="lang-bash">valet install
</code></pre>
<p>But if you have another error related with <code>nginx</code>, you need to stop the service that is running in the same port as <code>nginx</code>, if it's <code>apache</code> who is provoking this problem you can simply stop the apache's service:</p>
<pre><code class="lang-bash">sudo systemctl stop apache2.service
</code></pre>
<p>And try again:</p>
<pre><code class="lang-bash">valet install
</code></pre>
<p>To verify everything is ok, run:</p>
<pre><code class="lang-bash">ping valet.test
</code></pre>
<p>And after a few seconds it should say something like this:</p>
<pre><code class="lang-bash">X packets transmited, X received...
</code></pre>
<p>If that's the case, congrats! you successfully installed Valet Linux!</p>
<p>Now we need to set the directory where we are going to create Laravel or PHP projects:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> directory/<span class="hljs-built_in">where</span>/you/are/going/to/work
</code></pre>
<p>And execute:</p>
<pre><code class="lang-bash">valet park
</code></pre>
<p>Now create a folder called: <code>phpinfo</code>, inside the create a file called <code>index.php</code> and in the file, we can write something to check its working:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> phpinfo();
</code></pre>
<p>And finally, we write the following URL in our browser:</p>
<p>http://phpinfo.test/index.php</p>
<p>It should display info about PHP without errors.</p>
<p>You can custom the domain if you don't like <code>.test</code> with:</p>
<pre><code class="lang-bash">valet domain dev
</code></pre>
<p>And now to get access you need to change your current URL with the following:
http://phpinfo.dev/index.php</p>
<p>And that's it, hope you enjoy it, if you want to see how to do it in fedora and manjaro, please comment it :D.</p>
]]></content:encoded></item></channel></rss>